From a3dfcf51c6543ac1af853f1799d70eae83073f1a Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Fri, 1 May 2015 19:50:27 -0700 Subject: 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 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src/cmd') 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 } -- cgit v1.3 From fd392ee52b984e655390ad9147c9fe95e82bc459 Mon Sep 17 00:00:00 2001 From: Shenghou Ma Date: Thu, 7 May 2015 00:48:09 -0400 Subject: 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(-) (limited to 'src/cmd') 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", -- cgit v1.3 From e8fc93ea45ca0147f24f61f5ed48e68b57d473df Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Thu, 7 May 2015 12:58:43 -0700 Subject: 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(+) (limited to 'src/cmd') 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 -- cgit v1.3 From 6d8a147bef8ee28eb647db21ea91ecb823fa2480 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 28 Apr 2015 00:28:47 -0400 Subject: 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 | 154 ++++++++++++++++++----------------------- 10 files changed, 205 insertions(+), 302 deletions(-) (limited to 'src/cmd') 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) - } - // 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) + 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 } - 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 } -- cgit v1.3 From 54af9a3ba5f3e656077eab2e9305cbbb41d7b154 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Mon, 4 May 2015 22:53:54 -0400 Subject: 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(-) (limited to 'src/cmd') 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 } } -- cgit v1.3 From 351897d9d4f61dba5bd19270463c393a58d1f2ce Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 7 May 2015 10:34:12 -0400 Subject: 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(+) (limited to 'src/cmd') 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 } -- cgit v1.3 From c70b4b5f7e419214c8c61ba5306b569fbab7dc30 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 6 May 2015 12:22:05 -0400 Subject: 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(-) (limited to 'src/cmd') 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)) } } } -- cgit v1.3 From dcf6e20606bb7e3920fef186d87e69742f7986fb Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 6 May 2015 12:25:56 -0400 Subject: 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(-) (limited to 'src/cmd') 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 } -- cgit v1.3 From fc595b78d216e9470afce5d99c002b4bf5760c2c Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 6 May 2015 12:30:59 -0400 Subject: 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(-) (limited to 'src/cmd') 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) } -- cgit v1.3 From 3475ec7f36b68126310878e611c8594514b98438 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Mon, 11 May 2015 11:24:10 +1200 Subject: 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(-) (limited to 'src/cmd') 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) { -- cgit v1.3 From 7c0db1b7e23e14f7a9cc0c424f57a57a613846d3 Mon Sep 17 00:00:00 2001 From: Didier Spezia Date: Sat, 9 May 2015 17:21:42 +0000 Subject: 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 (limited to 'src/cmd') 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 _" +} -- cgit v1.3 From 754e98cb821f6a48d21bfb8473ad3063c2021061 Mon Sep 17 00:00:00 2001 From: Rahul Chaudhry Date: Fri, 8 May 2015 12:33:30 -0700 Subject: 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(-) (limited to 'src/cmd') 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", -- cgit v1.3 From 77fc03f4cd7f6ea0b142bd17ea172205d5f45cff Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Sat, 11 Apr 2015 12:05:21 +0800 Subject: 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(-) (limited to 'src/cmd') 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 -- cgit v1.3 From 29dc4b40f85fdb0985eea3e718385dc3c6cd22b7 Mon Sep 17 00:00:00 2001 From: Jens Frederich Date: Thu, 19 Feb 2015 21:37:38 +0100 Subject: 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(-) (limited to 'src/cmd') 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}", -- cgit v1.3 From 3f209abb2954bfb89e3dbd28ed0a622a6fe33242 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Mon, 11 May 2015 16:12:01 -0400 Subject: 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(-) (limited to 'src/cmd') 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" } -- cgit v1.3 From 18d98bc9cb34df680ae3dac89712366a9883789f Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 30 Apr 2015 20:35:47 -0400 Subject: 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(-) (limited to 'src/cmd') 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 -- cgit v1.3 From f8d14fc3a00052727c717f62908a9db661483093 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 6 May 2015 12:28:19 -0400 Subject: 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(-) (limited to 'src/cmd') 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) -- cgit v1.3 From 8552047a32cccccc1c376e49048c5a22494b7611 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 6 May 2015 12:34:30 -0400 Subject: 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 (limited to 'src/cmd') 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" +} -- cgit v1.3 From 5ed4bb6db111d31a2c8625fe122de0ed7f1a9cbd Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 12 May 2015 15:51:22 -0400 Subject: 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(-) (limited to 'src/cmd') 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) -- cgit v1.3 From 99475dfb5923b775a9ab587ad99cb64de5d1f51c Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 21 Apr 2015 10:39:21 -0700 Subject: 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 (limited to 'src/cmd') 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) +} -- cgit v1.3 From d4472799277102e461968fa059f49bc8b9b6e433 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 6 May 2015 12:35:53 -0400 Subject: 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 | 286 +++----------------- test/sliceopt.go | 41 ++- 9 files changed, 686 insertions(+), 378 deletions(-) (limited to 'src/cmd') 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 - 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) + 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) - 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 + + 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 + } 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) +} -- cgit v1.3 From 1e26df40fa2ee41da971338ab25299e27a221704 Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Mon, 11 May 2015 13:31:05 -0700 Subject: 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(+) (limited to 'src/cmd') 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 { -- cgit v1.3 From 5c7f94421ef5eca55b37f778b427abd5ea174c26 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 13 May 2015 15:28:11 -0700 Subject: 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(-) (limited to 'src/cmd') 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) } -- cgit v1.3 From b3fb0fdd3f7c35e020788acf6d12278590b416cb Mon Sep 17 00:00:00 2001 From: Shenghou Ma Date: Wed, 13 May 2015 19:05:50 -0400 Subject: 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(-) (limited to 'src/cmd') 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 -- cgit v1.3 From 85a15778005c8412005ef7366cf40b0a50ace5ba Mon Sep 17 00:00:00 2001 From: Shenghou Ma Date: Wed, 13 May 2015 20:28:05 -0400 Subject: 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(-) (limited to 'src/cmd') 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) } } } -- cgit v1.3 From a901d7fb8f3126f5fbaf3be097449769b490503a Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Thu, 14 May 2015 13:03:02 -0700 Subject: 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(-) (limited to 'src/cmd') 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{ -- cgit v1.3 From 83c7b60f2745e7b0e35e762e3fc2702f59169a7b Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Thu, 14 May 2015 15:45:10 -0700 Subject: 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(-) (limited to 'src/cmd') 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, } } -- cgit v1.3 From 37eb1d1964049df2feb847ccef501f2807fdf618 Mon Sep 17 00:00:00 2001 From: Mikio Hara Date: Fri, 15 May 2015 09:39:16 +0900 Subject: 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(-) (limited to 'src/cmd') 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: -- cgit v1.3 From 3b214175bcf1e1441f0411f4691ca9a0963c4564 Mon Sep 17 00:00:00 2001 From: David Symonds Date: Fri, 15 May 2015 15:37:40 +1000 Subject: 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(-) (limited to 'src/cmd') 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. -- cgit v1.3 From 0112f6f6b605b17194175aa7cfe15b4054f862fc Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Wed, 22 Apr 2015 20:08:03 -0700 Subject: 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(-) (limited to 'src/cmd') 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 + + var i big.Int + i.SetInt64(1) + i.Lsh(&i, 63) - gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 1) - gc.Mpshiftfix(bigi.Val.U.Xval, 63) + gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0) + bigi.SetBigInt(&i) - 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.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 + + gc.Nodconst(&zerof, gc.Types[gc.TINT64], 0) + gc.Convconst(&zerof, gc.Types[gc.TFLOAT64], &zerof.Val) - 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.) + var i big.Int + i.SetInt64(1) + i.Lsh(&i, 63) + var bigi gc.Node - two63f = two64f - two63f.Val.U.Fval = new(gc.Mpflt) - gc.Mpmovecflt(two63f.Val.U.Fval, 9223372036854775808.) + gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0) + bigi.SetBigInt(&i) + gc.Convconst(&two63f, gc.Types[gc.TFLOAT64], &bigi.Val) - zerof = two64f - zerof.Val.U.Fval = new(gc.Mpflt) - gc.Mpmovecflt(zerof.Val.U.Fval, 0) + 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 + + var i big.Int + i.SetInt64(1) + i.Lsh(&i, 63) - gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 1) - gc.Mpshiftfix(bigi.Val.U.Xval, 63) + gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0) + bigi.SetBigInt(&i) - 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.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. -- cgit v1.3 From ba57781181b18187958756b7ad7ccb126728de2d Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Thu, 7 May 2015 18:43:03 -0700 Subject: 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(-) (limited to 'src/cmd') 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 } -- cgit v1.3 From 3c06cff7d1d7382b74ab39bd3bf0e46264ac845d Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Thu, 14 May 2015 17:32:07 -0700 Subject: 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(+) (limited to 'src/cmd') 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, -- cgit v1.3 From 13485be939d73d6d2a5a8407c74c5fd502e35a6f Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Thu, 14 May 2015 17:57:42 -0700 Subject: 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(-) (limited to 'src/cmd') 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: -- cgit v1.3 From 76ec0ee53a5619b4ead4772c075105bf66c0ac67 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Fri, 3 Apr 2015 17:43:38 -0700 Subject: 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(-) (limited to 'src/cmd') 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 -- cgit v1.3 From 5726af54eb3a52b9446a834991110b945e780e99 Mon Sep 17 00:00:00 2001 From: Daniel Morsing Date: Wed, 29 Apr 2015 10:51:42 +0100 Subject: 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(-) (limited to 'src/cmd') 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: +} -- cgit v1.3 From 97494a45e21099c3a357785f31894a5e69e086c8 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Fri, 15 May 2015 16:35:43 +0000 Subject: 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(-) (limited to 'src/cmd') 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: -} -- cgit v1.3 From 82e1651a246db496fa9598f46a6c6af999b699ba Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 7 Apr 2015 11:57:52 -0700 Subject: 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 (limited to 'src/cmd') 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. -- cgit v1.3 From 1467776b17c7dc232f5586944785f85f48862b49 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Mon, 20 Apr 2015 13:32:40 -0700 Subject: 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(-) (limited to 'src/cmd') 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) +} + +func linehistpush(file string) { + if Debug['i'] != 0 { + fmt.Printf("import %s at line %v\n", file, Ctxt.Line(int(lexlineno))) } + Ctxt.LineHist.Push(int(lexlineno), file) +} - if off < 0 && file[0] != '/' && relative == 0 { - file = fmt.Sprintf("%s/%s", Ctxt.Pathname, 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))) } - obj.Linklinehist(Ctxt, int(lexlineno), file, int(off)) + 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 { -- cgit v1.3 From 8fa14ea8b4744576bd28073901154c15813e29de Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Fri, 15 May 2015 10:02:19 -0700 Subject: 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(-) (limited to 'src/cmd') 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 -- cgit v1.3 From 004706099d95160960785a45a56b7cb54fc6f7af Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Tue, 12 May 2015 15:40:13 +1200 Subject: 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(-) (limited to 'src/cmd') 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() -- cgit v1.3 From 4cfff271c2aacd4ef23f7eacd9adf61605c45e74 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Tue, 12 May 2015 15:59:15 +1200 Subject: 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(-) (limited to 'src/cmd') 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 -- cgit v1.3 From ddc4c146a46cd8ae3a4f1f9b7f0cd14f4bb2aca4 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Tue, 12 May 2015 16:07:05 +1200 Subject: 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(-) (limited to 'src/cmd') 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 -- cgit v1.3 From 512f75e8dfc7653c873971480c23ba308307b85a Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 8 May 2015 01:43:18 -0400 Subject: 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 | 7 +- src/cmd/internal/gc/plive.go | 2 +- src/cmd/internal/gc/reflect.go | 368 ++++++++---------- src/cmd/internal/gcprog/gcprog.go | 298 +++++++++++++++ src/cmd/internal/ld/data.go | 177 +++------ 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 | 758 +++++++++++++++++++++++++++----------- src/runtime/mgc.go | 4 +- src/runtime/symtab.go | 2 + src/runtime/traceback.go | 2 +- src/runtime/type.go | 15 +- 19 files changed, 1398 insertions(+), 796 deletions(-) create mode 100644 src/cmd/internal/gcprog/gcprog.go (limited to 'src/cmd') 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 }{ + {"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 - {"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/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) + useGCProg = true + sym, ptrdata = dgcprog(t) + return +} - // 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) +// 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 } -// Generates GC bitmask (1 bit per word). -func gengcmask(t *Type, gcmask []byte) { - for i := int64(0); i < 16; i++ { - gcmask[i] = 0 +// 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 -// Emits insData block from g->data. -func proggendataflush(g *ProgGen) { - if g.datasize == 0 { - return +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) } - 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) writeByte(x byte) { + p.symoff = duint8(p.sym, p.symoff, x) } -// 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) - } +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) } } -// 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 (p *GCProg) emit(t *Type, offset int64) { + dowidth(t) + if !haspointers(t) { + return } -} - -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 + if t.Width == int64(Widthptr) { + p.w.Ptr(offset / int64(Widthptr)) + return } - - // 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 -} - -func proggeninit(g *ProgGen, s *LSym) { - g.s = s - g.datasize = 0 - g.pos = 0 - g.data = [256 / 8]uint8{} -} +const debugGCProg = false -func proggenemit(g *ProgGen, v uint8) { - Adduint8(Ctxt, g.s, v) +type GCProg struct { + sym *LSym + w gcprog.Writer } -// Writes insData block from g->data. -func proggendataflush(g *ProgGen) { - if g.datasize == 0 { - return +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) } - 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) writeByte(x byte) { + Adduint8(Ctxt, p.sym, x) } -// 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) 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 { +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 } - // Skip alignment hole from the previous symbol. - proggenskip(g, g.pos, s.Value-g.pos) - g.pos = s.Value + ptrsize := int64(Thearch.Ptrsize) + nptr := decodetype_ptrdata(typ) / ptrsize - 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 + if debugGCProg { + fmt.Fprintf(os.Stderr, "gcprog sym: %s at %d (ptr=%d+%d)\n", s.Name, s.Value, s.Value/ptrsize, nptr) } - 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) - } + 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) } - } 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 + 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) - } - // keys - for i := 0; i < bucketSize; i++ { - gc.appendProg(ktyp) - } - // values - for i := 0; i < bucketSize; i++ { - gc.appendProg(etyp) + // 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 } - // 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 + + // 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++ + + runGCProg(prog, &trailer[0], h.bitp, 2) + + // 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)) +} -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 - - // 64 bytes cover objects of size 1024/512 on 64/32 bits, respectively. - maxGCMask = 65536 // TODO(rsc): change back to 64 -) +// 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]} +} -// 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)) +// 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") - - 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 + // 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 + } + } + + // 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 + } + } + if n %= 8; n > 0 { + bits |= uintptr(*p) << nbits + p = add1(p) + nbits += n + } + continue Run + } + + // 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 + } + } + } + + // 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 } } - prog = addb(prog, (uintptr(siz)+7)/8) - 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))) + // 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 } - prog = (*byte)(add(unsafe.Pointer(prog), ptrSize)) - var prog1 *byte - for i := uintptr(0); i < siz; i++ { - prog1 = unrollgcprog1(&mask[0], prog, &pos, inplace) + + // 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 + } + } } - prog = (*byte)(add(unsafe.Pointer(prog1), 1)) - case insArrayEnd, insEnd: - *ppos = pos - return prog + // 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 -- cgit v1.3 From 6e8bcbbe8987d9b06ef2c349913cde11e6dd8339 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 15 May 2015 16:11:25 -0400 Subject: 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(-) (limited to 'src/cmd') 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 -- cgit v1.3 From ab4e7988bb30eaf43ef9c5b4dcedc30502a0b0e6 Mon Sep 17 00:00:00 2001 From: Shenghou Ma Date: Fri, 1 May 2015 22:23:04 -0400 Subject: 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(-) (limited to 'src/cmd') 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") -- cgit v1.3 From a21cf5b6a281df2c3506105cecfbeeda70afca1c Mon Sep 17 00:00:00 2001 From: David Chase Date: Fri, 15 May 2015 12:19:07 -0400 Subject: 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(-) (limited to 'src/cmd') 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}, + }...) +} -- cgit v1.3 From 362a40e37da849696e1af6874d0d5f1275a1a386 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Thu, 7 May 2015 21:29:47 +1200 Subject: 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 (limited to 'src/cmd') 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") -- cgit v1.3 From 6f7b4e893871775b74e57d5d048ff0565d32ef79 Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Fri, 15 May 2015 13:30:42 -0700 Subject: 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(-) (limited to 'src/cmd') 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) -- cgit v1.3 From 19354b9dc87d62dbb5280354be7fd90bdf196a24 Mon Sep 17 00:00:00 2001 From: Daniel Morsing Date: Sat, 2 May 2015 13:08:12 +0100 Subject: 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(-) (limited to 'src/cmd') 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 -- cgit v1.3 From 2b063bdff1a61961936db7ef1e963aecf1ae3db7 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Thu, 14 May 2015 19:33:31 -0700 Subject: 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(-) (limited to 'src/cmd') 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 -- cgit v1.3 From ddc93398b955a4d71683c8019d87d2ff9c739070 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Thu, 14 May 2015 19:50:41 -0700 Subject: 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(-) (limited to 'src/cmd') 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 -- cgit v1.3 From f4ab8203bab58b3c4ae53a99535719a747d05332 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Mon, 18 May 2015 10:27:59 -0700 Subject: 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(-) (limited to 'src/cmd') 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 } } -- cgit v1.3 From 82833b313e5e23f67d5ed1141d9a2464bf78f277 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Mon, 18 May 2015 15:49:02 -0700 Subject: 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 | 90 +++++++++++++++++++++++-------------------- 1 file changed, 49 insertions(+), 41 deletions(-) (limited to 'src/cmd') 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,32 +23,6 @@ type Node struct { List *NodeList Rlist *NodeList - Op uint8 - Nointerface bool - Ullman uint8 // sethi/ullman number - Addable bool // addressable - 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 - Colas bool // OAS resulting from := - Diag uint8 // already printed error about this - Noescape bool // func arguments do not escape; TODO(rsc): move Noescape to Func struct (see CL 7360) - Walkdef uint8 - Typecheck uint8 - Local bool - Dodata uint8 - Initorder uint8 - 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 @@ -57,12 +31,6 @@ type Node struct { // func Func *Func - // OLITERAL - Val Val - - // OREGISTER, OINDREG - Reg int16 - // ONAME Name *Name Defn *Node // ONAME: initializing assignment; OLABEL: labeled statement @@ -79,19 +47,59 @@ type Node struct { 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 + 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 - 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 + + // 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 - Opt interface{} // for optimization passes + Esc uint16 // EscXXX + + Op uint8 + Nointerface bool + Ullman uint8 // sethi/ullman number + Addable bool // addressable + 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 + Colas bool // OAS resulting from := + Diag uint8 // already printed error about this + Noescape bool // func arguments do not escape; TODO(rsc): move Noescape to Func struct (see CL 7360) + Walkdef uint8 + Typecheck uint8 + Local bool + Dodata uint8 + Initorder uint8 + 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 } // Name holds Node fields used only by ONAME nodes. -- cgit v1.3 From 366ba526e88f5b523298d3ad5014e04a495add82 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Mon, 18 May 2015 16:54:59 -0400 Subject: 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(-) (limited to 'src/cmd') 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" +} -- cgit v1.3 From c735064cdeb6bf4ec84a0a4b2b48a5cafc4b83dd Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 19 May 2015 14:00:27 -0400 Subject: 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 (limited to 'src/cmd') 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" +} -- cgit v1.3 From 9c9e36b34040d33b9f9a0b6fd918ef470338aec4 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 19 May 2015 15:15:52 -0400 Subject: 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(+) (limited to 'src/cmd') 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") } -- cgit v1.3 From 8b83306cf20abed54d7cb23a3f3091b7e6202056 Mon Sep 17 00:00:00 2001 From: Ryan Brown Date: Wed, 8 Apr 2015 12:55:34 -0700 Subject: 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 (limited to 'src/cmd') 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) +} -- cgit v1.3 From b3241912ff62ab64ec3c61bc8c198b6e12890a77 Mon Sep 17 00:00:00 2001 From: Alexander Zolotov Date: Sat, 9 May 2015 16:41:32 +0300 Subject: 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(-) (limited to 'src/cmd') 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" +} -- cgit v1.3 From f763da3d3404673484bcc9d0d911c75a40e326c2 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 19 May 2015 16:58:14 -0400 Subject: 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(-) (limited to 'src/cmd') 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)) -- cgit v1.3 From d6bbcea22a3d4cbcf8350b4b861f0d73ab142ac2 Mon Sep 17 00:00:00 2001 From: Shenghou Ma Date: Tue, 19 May 2015 18:25:22 -0400 Subject: 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(-) (limited to 'src/cmd') 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() { -- cgit v1.3 From 791bb4f5aefdf3d7ae8aea7d360dec6cb238698a Mon Sep 17 00:00:00 2001 From: Shenghou Ma Date: Tue, 19 May 2015 03:39:30 -0400 Subject: 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(+) (limited to 'src/cmd') 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) } -- cgit v1.3 From 7bdb4a28a87f24a6c00ae3194598934e0fa142f0 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Tue, 19 May 2015 23:49:24 +0000 Subject: 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 (limited to 'src/cmd') 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) -} -- cgit v1.3 From ae3e3610d5ea9814fcc8bff5c4cea51795465565 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Mon, 18 May 2015 13:03:22 +1200 Subject: 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(-) (limited to 'src/cmd') 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) } -- cgit v1.3 From 8401b19e7e6bf60d66b2d71cd3fa2215c4649d31 Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Wed, 20 May 2015 13:26:02 -0700 Subject: 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(-) (limited to 'src/cmd') 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? -- cgit v1.3 From be59731d9fffb53a499d1ae64863e2a380f2dede Mon Sep 17 00:00:00 2001 From: Ryan Brown Date: Wed, 8 Apr 2015 12:55:34 -0700 Subject: 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 (limited to 'src/cmd') 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) +} -- cgit v1.3 From 2a141dedc4bb7b33f5caee6e7b185dfbd20c92bc Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 21 May 2015 13:28:06 -0400 Subject: 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/auto.go | 117 -- src/cmd/link/auto_test.go | 72 - src/cmd/link/dead.go | 74 - src/cmd/link/dead_test.go | 97 -- src/cmd/link/debug.go | 11 - src/cmd/link/hex_test.go | 74 - src/cmd/link/layout.go | 180 --- src/cmd/link/layout_test.go | 45 - src/cmd/link/link_test.go | 35 - src/cmd/link/load.go | 108 -- src/cmd/link/macho.go | 380 ----- src/cmd/link/macho_test.go | 407 ----- src/cmd/link/main.go | 9 - src/cmd/link/pclntab.go | 480 ------ src/cmd/link/pclntab_test.go | 340 ----- src/cmd/link/prog.go | 220 --- src/cmd/link/prog_test.go | 163 -- src/cmd/link/runtime.go | 28 - src/cmd/link/scan.go | 187 --- src/cmd/link/testdata/Makefile | 15 - src/cmd/link/testdata/autosection.6 | Bin 937 -> 0 bytes src/cmd/link/testdata/autosection.s | 60 - src/cmd/link/testdata/autoweak.6 | Bin 386 -> 0 bytes src/cmd/link/testdata/autoweak.s | 30 - src/cmd/link/testdata/dead.6 | Bin 849 -> 0 bytes src/cmd/link/testdata/dead.s | 48 - src/cmd/link/testdata/genpcln.go | 112 -- src/cmd/link/testdata/hello.6 | Bin 229 -> 0 bytes src/cmd/link/testdata/hello.s | 15 - src/cmd/link/testdata/layout.6 | Bin 396 -> 0 bytes src/cmd/link/testdata/layout.s | 29 - src/cmd/link/testdata/link.hello.darwin.amd64 | 55 - src/cmd/link/testdata/macho.amd64.exit9 | 24 - src/cmd/link/testdata/macho.amd64.hello | 39 - src/cmd/link/testdata/macho.amd64.helloro | 34 - src/cmd/link/testdata/pclntab.6 | Bin 3804 -> 0 bytes src/cmd/link/testdata/pclntab.s | 1751 ---------------------- src/cmd/link/util.go | 11 - src/cmd/link/write.go | 14 - src/cmd/newlink/auto.go | 117 ++ src/cmd/newlink/auto_test.go | 72 + src/cmd/newlink/dead.go | 74 + src/cmd/newlink/dead_test.go | 97 ++ src/cmd/newlink/debug.go | 11 + src/cmd/newlink/hex_test.go | 74 + src/cmd/newlink/layout.go | 180 +++ src/cmd/newlink/layout_test.go | 45 + src/cmd/newlink/link_test.go | 35 + src/cmd/newlink/load.go | 108 ++ src/cmd/newlink/macho.go | 380 +++++ src/cmd/newlink/macho_test.go | 407 +++++ src/cmd/newlink/main.go | 9 + src/cmd/newlink/pclntab.go | 480 ++++++ src/cmd/newlink/pclntab_test.go | 340 +++++ src/cmd/newlink/prog.go | 220 +++ src/cmd/newlink/prog_test.go | 163 ++ src/cmd/newlink/runtime.go | 28 + src/cmd/newlink/scan.go | 187 +++ src/cmd/newlink/testdata/Makefile | 15 + src/cmd/newlink/testdata/autosection.6 | Bin 0 -> 937 bytes src/cmd/newlink/testdata/autosection.s | 60 + src/cmd/newlink/testdata/autoweak.6 | Bin 0 -> 386 bytes src/cmd/newlink/testdata/autoweak.s | 30 + src/cmd/newlink/testdata/dead.6 | Bin 0 -> 849 bytes src/cmd/newlink/testdata/dead.s | 48 + src/cmd/newlink/testdata/genpcln.go | 112 ++ src/cmd/newlink/testdata/hello.6 | Bin 0 -> 229 bytes src/cmd/newlink/testdata/hello.s | 15 + src/cmd/newlink/testdata/layout.6 | Bin 0 -> 396 bytes src/cmd/newlink/testdata/layout.s | 29 + src/cmd/newlink/testdata/link.hello.darwin.amd64 | 55 + src/cmd/newlink/testdata/macho.amd64.exit9 | 24 + src/cmd/newlink/testdata/macho.amd64.hello | 39 + src/cmd/newlink/testdata/macho.amd64.helloro | 34 + src/cmd/newlink/testdata/pclntab.6 | Bin 0 -> 3804 bytes src/cmd/newlink/testdata/pclntab.s | 1751 ++++++++++++++++++++++ src/cmd/newlink/util.go | 11 + src/cmd/newlink/write.go | 14 + 81 files changed, 5267 insertions(+), 5267 deletions(-) delete mode 100644 src/cmd/link/auto.go delete mode 100644 src/cmd/link/auto_test.go delete mode 100644 src/cmd/link/dead.go delete mode 100644 src/cmd/link/dead_test.go delete mode 100644 src/cmd/link/debug.go delete mode 100644 src/cmd/link/hex_test.go delete mode 100644 src/cmd/link/layout.go delete mode 100644 src/cmd/link/layout_test.go delete mode 100644 src/cmd/link/link_test.go delete mode 100644 src/cmd/link/load.go delete mode 100644 src/cmd/link/macho.go delete mode 100644 src/cmd/link/macho_test.go delete mode 100644 src/cmd/link/main.go delete mode 100644 src/cmd/link/pclntab.go delete mode 100644 src/cmd/link/pclntab_test.go delete mode 100644 src/cmd/link/prog.go delete mode 100644 src/cmd/link/prog_test.go delete mode 100644 src/cmd/link/runtime.go delete mode 100644 src/cmd/link/scan.go delete mode 100644 src/cmd/link/testdata/Makefile delete mode 100644 src/cmd/link/testdata/autosection.6 delete mode 100644 src/cmd/link/testdata/autosection.s delete mode 100644 src/cmd/link/testdata/autoweak.6 delete mode 100644 src/cmd/link/testdata/autoweak.s delete mode 100644 src/cmd/link/testdata/dead.6 delete mode 100644 src/cmd/link/testdata/dead.s delete mode 100644 src/cmd/link/testdata/genpcln.go delete mode 100644 src/cmd/link/testdata/hello.6 delete mode 100644 src/cmd/link/testdata/hello.s delete mode 100644 src/cmd/link/testdata/layout.6 delete mode 100644 src/cmd/link/testdata/layout.s delete mode 100644 src/cmd/link/testdata/link.hello.darwin.amd64 delete mode 100644 src/cmd/link/testdata/macho.amd64.exit9 delete mode 100644 src/cmd/link/testdata/macho.amd64.hello delete mode 100644 src/cmd/link/testdata/macho.amd64.helloro delete mode 100644 src/cmd/link/testdata/pclntab.6 delete mode 100644 src/cmd/link/testdata/pclntab.s delete mode 100644 src/cmd/link/util.go delete mode 100644 src/cmd/link/write.go create mode 100644 src/cmd/newlink/auto.go create mode 100644 src/cmd/newlink/auto_test.go create mode 100644 src/cmd/newlink/dead.go create mode 100644 src/cmd/newlink/dead_test.go create mode 100644 src/cmd/newlink/debug.go create mode 100644 src/cmd/newlink/hex_test.go create mode 100644 src/cmd/newlink/layout.go create mode 100644 src/cmd/newlink/layout_test.go create mode 100644 src/cmd/newlink/link_test.go create mode 100644 src/cmd/newlink/load.go create mode 100644 src/cmd/newlink/macho.go create mode 100644 src/cmd/newlink/macho_test.go create mode 100644 src/cmd/newlink/main.go create mode 100644 src/cmd/newlink/pclntab.go create mode 100644 src/cmd/newlink/pclntab_test.go create mode 100644 src/cmd/newlink/prog.go create mode 100644 src/cmd/newlink/prog_test.go create mode 100644 src/cmd/newlink/runtime.go create mode 100644 src/cmd/newlink/scan.go create mode 100644 src/cmd/newlink/testdata/Makefile create mode 100644 src/cmd/newlink/testdata/autosection.6 create mode 100644 src/cmd/newlink/testdata/autosection.s create mode 100644 src/cmd/newlink/testdata/autoweak.6 create mode 100644 src/cmd/newlink/testdata/autoweak.s create mode 100644 src/cmd/newlink/testdata/dead.6 create mode 100644 src/cmd/newlink/testdata/dead.s create mode 100644 src/cmd/newlink/testdata/genpcln.go create mode 100644 src/cmd/newlink/testdata/hello.6 create mode 100644 src/cmd/newlink/testdata/hello.s create mode 100644 src/cmd/newlink/testdata/layout.6 create mode 100644 src/cmd/newlink/testdata/layout.s create mode 100644 src/cmd/newlink/testdata/link.hello.darwin.amd64 create mode 100644 src/cmd/newlink/testdata/macho.amd64.exit9 create mode 100644 src/cmd/newlink/testdata/macho.amd64.hello create mode 100644 src/cmd/newlink/testdata/macho.amd64.helloro create mode 100644 src/cmd/newlink/testdata/pclntab.6 create mode 100644 src/cmd/newlink/testdata/pclntab.s create mode 100644 src/cmd/newlink/util.go create mode 100644 src/cmd/newlink/write.go (limited to 'src/cmd') 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/link/auto.go deleted file mode 100644 index 21f6d60827..0000000000 --- a/src/cmd/link/auto.go +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2014 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. - -// Automatic symbol generation. - -// TODO(rsc): Handle go.typelink, go.track symbols. -// TODO(rsc): Do not handle $f64. and $f32. symbols. Instead, generate those -// from the compiler and assemblers as dupok data, and then remove autoData below. -package main - -import ( - "cmd/internal/goobj" - "strconv" - "strings" -) - -// linkerDefined lists the symbols supplied by other parts of the linker -// (runtime.go and layout.go). -var linkerDefined = map[string]bool{ - "runtime.bss": true, - "runtime.data": true, - "runtime.ebss": true, - "runtime.edata": true, - "runtime.efunctab": true, - "runtime.end": true, - "runtime.enoptrbss": true, - "runtime.enoptrdata": true, - "runtime.erodata": true, - "runtime.etext": true, - "runtime.etypelink": true, - "runtime.functab": true, - "runtime.gcbss": true, - "runtime.gcdata": true, - "runtime.noptrbss": true, - "runtime.noptrdata": true, - "runtime.pclntab": true, - "runtime.rodata": true, - "runtime.text": true, - "runtime.typelink": true, -} - -// isAuto reports whether sym is an automatically-generated data or constant symbol. -func (p *Prog) isAuto(sym goobj.SymID) bool { - return strings.HasPrefix(sym.Name, "go.weak.") || - strings.HasPrefix(sym.Name, "$f64.") || - strings.HasPrefix(sym.Name, "$f32.") || - linkerDefined[sym.Name] -} - -// autoData defines the automatically generated data symbols needed by p. -func (p *Prog) autoData() { - for sym := range p.Missing { - switch { - // Floating-point constants that need to be loaded from memory are - // written as $f64.{16 hex digits} or $f32.{8 hex digits}; the hex digits - // give the IEEE bit pattern of the constant. As far as the layout into - // memory is concerned, we interpret these as uint64 or uint32 constants. - case strings.HasPrefix(sym.Name, "$f64."), strings.HasPrefix(sym.Name, "$f32."): - size := 64 - if sym.Name[2:4] == "32" { - size = 32 - } - delete(p.Missing, sym) - fbits, err := strconv.ParseUint(sym.Name[len("$f64."):], 16, size) - if err != nil { - p.errorf("unexpected floating point symbol %s", sym) - continue - } - data := make([]byte, size/8) - if size == 64 { - p.byteorder.PutUint64(data, fbits) - } else { - p.byteorder.PutUint32(data, uint32(fbits)) - } - p.addSym(&Sym{ - Sym: &goobj.Sym{ - SymID: sym, - Kind: goobj.SRODATA, - Size: size / 8, - }, - Bytes: data, - }) - } - } -} - -// autoConst defines the automatically generated constant symbols needed by p. -func (p *Prog) autoConst() { - for sym := range p.Missing { - switch { - case strings.HasPrefix(sym.Name, "go.weak."): - // weak symbol resolves to actual symbol if present, or else nil. - delete(p.Missing, sym) - targ := sym - targ.Name = sym.Name[len("go.weak."):] - var addr Addr - if s := p.Syms[targ]; s != nil { - addr = s.Addr - } - p.defineConst(sym.Name, addr) - } - } -} - -// defineConst defines a new symbol with the given name and constant address. -func (p *Prog) defineConst(name string, addr Addr) { - sym := goobj.SymID{Name: name} - p.addSym(&Sym{ - Sym: &goobj.Sym{ - SymID: sym, - Kind: goobj.SCONST, - }, - Package: nil, - Addr: addr, - }) -} diff --git a/src/cmd/link/auto_test.go b/src/cmd/link/auto_test.go deleted file mode 100644 index f99e097a9c..0000000000 --- a/src/cmd/link/auto_test.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2014 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 for auto-generated symbols. - -// There is no test for $f64. and $f32. symbols, because those are -// not possible to write in the assembler syntax. Instead of changing -// the assembler to allow that, we plan to change the compilers -// not to generate such symbols (plain dupok data is sufficient). - -package main - -import ( - "bytes" - "cmd/internal/goobj" - "testing" -) - -// Each test case is an object file, generated from a corresponding .s file. -// The image of the autotab symbol should be a sequence of pairs of -// identical 8-byte sequences. -var autoTests = []string{ - "testdata/autosection.6", - "testdata/autoweak.6", -} - -func TestAuto(t *testing.T) { - for _, obj := range autoTests { - p := Prog{GOOS: "darwin", GOARCH: "amd64", StartSym: "start"} - p.omitRuntime = true - p.Error = func(s string) { t.Error(s) } - var buf bytes.Buffer - p.link(&buf, obj) - if p.NumError > 0 { - continue // already reported - } - - const name = "autotab" - sym := p.Syms[goobj.SymID{Name: name}] - if sym == nil { - t.Errorf("%s is missing %s symbol", obj, name) - return - } - if sym.Size == 0 { - return - } - - seg := sym.Section.Segment - off := sym.Addr - seg.VirtAddr - data := seg.Data[off : off+Addr(sym.Size)] - if len(data)%16 != 0 { - t.Errorf("%s: %s.Size = %d, want multiple of 16", obj, name, len(data)) - return - } - Data: - for i := 0; i < len(data); i += 16 { - have := p.byteorder.Uint64(data[i : i+8]) - want := p.byteorder.Uint64(data[i+8 : i+16]) - if have != want { - // Look for relocation so we can explain what went wrong. - for _, r := range sym.Reloc { - if r.Offset == i { - t.Errorf("%s: %s+%#x: %s: have %#x want %#x", obj, name, i, r.Sym, have, want) - continue Data - } - } - t.Errorf("%s: %s+%#x: have %#x want %#x", obj, name, i, have, want) - } - } - } -} diff --git a/src/cmd/link/dead.go b/src/cmd/link/dead.go deleted file mode 100644 index ee23a61f81..0000000000 --- a/src/cmd/link/dead.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2014 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. - -// Removal of dead code and data. - -package main - -import "cmd/internal/goobj" - -// dead removes unreachable code and data from the program. -// It is basically a mark-sweep garbage collection: traverse all the -// symbols reachable from the entry (startSymID) and then delete -// the rest. -func (p *Prog) dead() { - p.Dead = make(map[goobj.SymID]bool) - reachable := make(map[goobj.SymID]bool) - p.walkDead(p.startSym, reachable) - - for sym := range p.Syms { - if !reachable[sym] { - delete(p.Syms, sym) - p.Dead[sym] = true - } - } - - for sym := range p.Missing { - if !reachable[sym] { - delete(p.Missing, sym) - p.Dead[sym] = true - } - } - - p.SymOrder = removeDead(p.SymOrder, reachable) - - for _, pkg := range p.Packages { - pkg.Syms = removeDead(pkg.Syms, reachable) - } -} - -// walkDead traverses the symbols reachable from sym, adding them to reachable. -// The caller has verified that reachable[sym] = false. -func (p *Prog) walkDead(sym goobj.SymID, reachable map[goobj.SymID]bool) { - reachable[sym] = true - s := p.Syms[sym] - if s == nil { - return - } - for i := range s.Reloc { - r := &s.Reloc[i] - if !reachable[r.Sym] { - p.walkDead(r.Sym, reachable) - } - } - if s.Func != nil { - for _, fdata := range s.Func.FuncData { - if fdata.Sym.Name != "" && !reachable[fdata.Sym] { - p.walkDead(fdata.Sym, reachable) - } - } - } -} - -// removeDead removes unreachable (dead) symbols from syms, -// returning a shortened slice using the same underlying array. -func removeDead(syms []*Sym, reachable map[goobj.SymID]bool) []*Sym { - keep := syms[:0] - for _, sym := range syms { - if reachable[sym.SymID] { - keep = append(keep, sym) - } - } - return keep -} diff --git a/src/cmd/link/dead_test.go b/src/cmd/link/dead_test.go deleted file mode 100644 index eb34d0580d..0000000000 --- a/src/cmd/link/dead_test.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2014 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/goobj" - "reflect" - "strings" - "testing" -) - -// Each test case is an object file, generated from a corresponding .s file. -// The symbols in the object file with a dead_ prefix are the ones that -// should be removed from the program. -var deadTests = []string{ - "testdata/dead.6", -} - -func TestDead(t *testing.T) { - for _, obj := range deadTests { - p := Prog{GOOS: "darwin", GOARCH: "amd64", StartSym: "start"} - p.omitRuntime = true - p.Error = func(s string) { t.Error(s) } - p.init() - p.scan(obj) - if p.NumError > 0 { - continue // already reported - } - origSyms := copyMap(p.Syms) - origMissing := copyMap(p.Missing) - origSymOrder := copySlice(p.SymOrder) - origPkgSyms := copySlice(p.Packages["main"].Syms) - p.dead() - checkDeadMap(t, obj, "p.Syms", origSyms, p.Syms) - checkDeadMap(t, obj, "p.Missing", origMissing, p.Missing) - checkDeadSlice(t, obj, "p.SymOrder", origSymOrder, p.SymOrder) - checkDeadSlice(t, obj, `p.Packages["main"].Syms`, origPkgSyms, p.Packages["main"].Syms) - } -} - -func copyMap(m interface{}) interface{} { - v := reflect.ValueOf(m) - out := reflect.MakeMap(v.Type()) - for _, key := range v.MapKeys() { - out.SetMapIndex(key, v.MapIndex(key)) - } - return out.Interface() -} - -func checkDeadMap(t *testing.T, obj, name string, old, new interface{}) { - vold := reflect.ValueOf(old) - vnew := reflect.ValueOf(new) - for _, vid := range vold.MapKeys() { - id := vid.Interface().(goobj.SymID) - if strings.HasPrefix(id.Name, "dead_") { - if vnew.MapIndex(vid).IsValid() { - t.Errorf("%s: %s contains unnecessary symbol %s", obj, name, id) - } - } else { - if !vnew.MapIndex(vid).IsValid() { - t.Errorf("%s: %s is missing symbol %s", obj, name, id) - } - } - } - for _, vid := range vnew.MapKeys() { - id := vid.Interface().(goobj.SymID) - if !vold.MapIndex(vid).IsValid() { - t.Errorf("%s: %s contains unexpected symbol %s", obj, name, id) - } - } -} - -func copySlice(x []*Sym) (out []*Sym) { - return append(out, x...) -} - -func checkDeadSlice(t *testing.T, obj, name string, old, new []*Sym) { - for i, s := range old { - if strings.HasPrefix(s.Name, "dead_") { - continue - } - if len(new) == 0 { - t.Errorf("%s: %s is missing symbol %s\nhave%v\nwant%v", obj, name, s, new, old[i:]) - return - } - if new[0].SymID != s.SymID { - t.Errorf("%s: %s is incorrect: have %s, want %s\nhave%v\nwant%v", obj, name, new[0].SymID, s.SymID, new, old[i:]) - return - } - new = new[1:] - } - if len(new) > 0 { - t.Errorf("%s: %s has unexpected symbols: %v", obj, name, new) - } -} diff --git a/src/cmd/link/debug.go b/src/cmd/link/debug.go deleted file mode 100644 index ee20644fd0..0000000000 --- a/src/cmd/link/debug.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2014 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. - -// Generation of debug data structures (in the executable but not mapped at run time). -// See also runtime.go. - -package main - -func (p *Prog) debug() { -} diff --git a/src/cmd/link/hex_test.go b/src/cmd/link/hex_test.go deleted file mode 100644 index b76b266558..0000000000 --- a/src/cmd/link/hex_test.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2014 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 ( - "encoding/hex" - "fmt" - "io/ioutil" - "regexp" - "strconv" - "strings" - "testing" -) - -// mustParseHexdumpFile returns a block of data generated by -// parsing the hex dump in the named file. -// If the file cannot be read or does not contain a valid hex dump, -// mustParseHexdumpFile calls t.Fatal. -func mustParseHexdumpFile(t *testing.T, file string) []byte { - hex, err := ioutil.ReadFile(file) - if err != nil { - t.Fatal(err) - } - data, err := parseHexdump(string(hex)) - if err != nil { - t.Fatal(err) - } - return data -} - -// parseHexdump parses the hex dump in text, which should be the -// output of "hexdump -C" or Plan 9's "xd -b", -// and returns the original data used to produce the dump. -// It is meant to enable storing golden binary files as text, so that -// changes to the golden files can be seen during code reviews. -func parseHexdump(text string) ([]byte, error) { - var out []byte - for _, line := range strings.Split(text, "\n") { - if i := strings.Index(line, "|"); i >= 0 { // remove text dump - line = line[:i] - } - f := strings.Fields(line) - if len(f) > 1+16 { - return nil, fmt.Errorf("parsing hex dump: too many fields on line %q", line) - } - if len(f) == 0 || len(f) == 1 && f[0] == "*" { // all zeros block omitted - continue - } - addr64, err := strconv.ParseUint(f[0], 16, 0) - if err != nil { - return nil, fmt.Errorf("parsing hex dump: invalid address %q", f[0]) - } - addr := int(addr64) - if len(out) < addr { - out = append(out, make([]byte, addr-len(out))...) - } - for _, x := range f[1:] { - val, err := strconv.ParseUint(x, 16, 8) - if err != nil { - return nil, fmt.Errorf("parsing hexdump: invalid hex byte %q", x) - } - out = append(out, byte(val)) - } - } - return out, nil -} - -func hexdump(data []byte) string { - text := hex.Dump(data) + fmt.Sprintf("%08x\n", len(data)) - text = regexp.MustCompile(`\n([0-9a-f]+(\s+00){16}.*\n)+`).ReplaceAllString(text, "\n*\n") - return text -} diff --git a/src/cmd/link/layout.go b/src/cmd/link/layout.go deleted file mode 100644 index d5c291e255..0000000000 --- a/src/cmd/link/layout.go +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright 2014 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. - -// Executable image layout - address assignment. - -package main - -import ( - "cmd/internal/goobj" -) - -// A layoutSection describes a single section to add to the -// final executable. Go binaries only have a fixed set of possible -// sections, and the symbol kind determines the section. -type layoutSection struct { - Segment string - Section string - Kind goobj.SymKind - Index int -} - -// layout defines the layout of the generated Go executable. -// The order of entries here is the order in the executable. -// Entries with the same Segment name must be contiguous. -var layout = []layoutSection{ - {Segment: "text", Section: "text", Kind: goobj.STEXT}, - {Segment: "rodata", Section: "rodata", Kind: goobj.SRODATA}, - {Segment: "rodata", Section: "functab", Kind: goobj.SPCLNTAB}, - {Segment: "rodata", Section: "typelink", Kind: goobj.STYPELINK}, - {Segment: "data", Section: "noptrdata", Kind: goobj.SNOPTRDATA}, - {Segment: "data", Section: "data", Kind: goobj.SDATA}, - {Segment: "data", Section: "bss", Kind: goobj.SBSS}, - {Segment: "data", Section: "noptrbss", Kind: goobj.SNOPTRBSS}, - - // Later: - // {"rodata", "type", goobj.STYPE}, - // {"rodata", "string", goobj.SSTRING}, - // {"rodata", "gostring", goobj.SGOSTRING}, - // {"rodata", "gofunc", goobj.SGOFUNC}, -} - -// layoutByKind maps from SymKind to an entry in layout. -var layoutByKind []*layoutSection - -func init() { - // Build index from symbol type to layout entry. - max := 0 - for _, sect := range layout { - if max <= int(sect.Kind) { - max = int(sect.Kind) + 1 - } - } - layoutByKind = make([]*layoutSection, max) - for i := range layout { - sect := &layout[i] - layoutByKind[sect.Kind] = sect - sect.Index = i - } -} - -// layout arranges symbols into sections and sections into segments, -// and then it assigns addresses to segments, sections, and symbols. -func (p *Prog) layout() { - sections := make([]*Section, len(layout)) - - // Assign symbols to sections using index, creating sections as needed. - // Could keep sections separated by type during input instead. - for _, sym := range p.SymOrder { - kind := sym.Kind - if kind < 0 || int(kind) >= len(layoutByKind) || layoutByKind[kind] == nil { - p.errorf("%s: unexpected symbol kind %v", sym.SymID, kind) - continue - } - lsect := layoutByKind[kind] - sect := sections[lsect.Index] - if sect == nil { - sect = &Section{ - Name: lsect.Section, - Align: 1, - } - sections[lsect.Index] = sect - } - if sym.Data.Size > 0 || len(sym.Bytes) > 0 { - sect.InFile = true - } - sym.Section = sect - sect.Syms = append(sect.Syms, sym) - - // TODO(rsc): Incorporate alignment information. - // First that information needs to be added to the object files. - // - // if sect.Align < Addr(sym.Align) { - // sect.Align = Addr(sym.Align) - // } - } - - // Assign sections to segments, creating segments as needed. - var seg *Segment - for i, sect := range sections { - if sect == nil { - continue - } - segName := layout[i].Segment - - // Special case: Mach-O does not support "rodata" segment, - // so store read-only data in text segment. - if p.GOOS == "darwin" && segName == "rodata" { - segName = "text" - } - - if seg == nil || seg.Name != segName { - seg = &Segment{ - Name: segName, - } - p.Segments = append(p.Segments, seg) - } - sect.Segment = seg - seg.Sections = append(seg.Sections, sect) - } - - // Assign addresses. - - // TODO(rsc): This choice needs to be informed by both - // the formatter and the target architecture. - // And maybe eventually a command line flag (sigh). - const segAlign = 4096 - - // TODO(rsc): Use a larger amount on most systems, which will let the - // compiler eliminate more nil checks. - if p.UnmappedSize == 0 { - p.UnmappedSize = segAlign - } - - // TODO(rsc): addr := Addr(0) when generating a shared library or PIE. - addr := p.UnmappedSize - - // Account for initial file header. - hdrVirt, hdrFile := p.formatter.headerSize(p) - addr += hdrVirt - - // Assign addresses to segments, sections, symbols. - // Assign sizes to segments, sections. - startVirt := addr - startFile := hdrFile - for _, seg := range p.Segments { - addr = round(addr, segAlign) - seg.VirtAddr = addr - seg.FileOffset = startFile + seg.VirtAddr - startVirt - for _, sect := range seg.Sections { - addr = round(addr, sect.Align) - sect.VirtAddr = addr - for _, sym := range sect.Syms { - // TODO(rsc): Respect alignment once we have that information. - sym.Addr = addr - addr += Addr(sym.Size) - } - sect.Size = addr - sect.VirtAddr - if sect.InFile { - seg.FileSize = addr - seg.VirtAddr - } - } - seg.VirtSize = addr - seg.VirtAddr - } - - // Define symbols for section names. - var progEnd Addr - for i, sect := range sections { - name := layout[i].Section - var start, end Addr - if sect != nil { - start = sect.VirtAddr - end = sect.VirtAddr + sect.Size - } - p.defineConst("runtime."+name, start) - p.defineConst("runtime.e"+name, end) - progEnd = end - } - p.defineConst("runtime.end", progEnd) -} diff --git a/src/cmd/link/layout_test.go b/src/cmd/link/layout_test.go deleted file mode 100644 index e5513af35e..0000000000 --- a/src/cmd/link/layout_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2014 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 ( - "bytes" - "strings" - "testing" -) - -func TestLayout(t *testing.T) { - p := Prog{GOOS: "darwin", GOARCH: "amd64", StartSym: "text_start"} - p.omitRuntime = true - p.Error = func(s string) { t.Error(s) } - var buf bytes.Buffer - const obj = "testdata/layout.6" - p.link(&buf, obj) - if p.NumError > 0 { - return // already reported - } - if len(p.Dead) > 0 { - t.Errorf("%s: unexpected dead symbols %v", obj, p.Dead) - return - } - - for _, sym := range p.SymOrder { - if p.isAuto(sym.SymID) { - continue - } - if sym.Section == nil { - t.Errorf("%s: symbol %s is missing section", obj, sym) - continue - } - i := strings.Index(sym.Name, "_") - if i < 0 { - t.Errorf("%s: unexpected symbol %s", obj, sym) - continue - } - if sym.Section.Name != sym.Name[:i] { - t.Errorf("%s: symbol %s in section %s, want %s", obj, sym, sym.Section.Name, sym.Name[:i]) - } - } -} diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go deleted file mode 100644 index b5ae15fc71..0000000000 --- a/src/cmd/link/link_test.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2014 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 ( - "bytes" - "cmd/internal/goobj" - "io/ioutil" - "testing" -) - -func TestLinkHello(t *testing.T) { - p := &Prog{ - GOOS: "darwin", - GOARCH: "amd64", - Error: func(s string) { t.Error(s) }, - StartSym: "_rt0_go", - } - var buf bytes.Buffer - p.link(&buf, "testdata/hello.6") - if p.NumError > 0 { - return - } - if p.Syms[goobj.SymID{"_rt0_go", 0}] == nil || p.Syms[goobj.SymID{"hello", 1}] == nil { - t.Errorf("Syms = %v, want at least [_rt0_go hello<1>]", p.Syms) - } - - // uncomment to leave file behind for execution: - if false { - ioutil.WriteFile("a.out", buf.Bytes(), 0777) - } - checkGolden(t, buf.Bytes(), "testdata/link.hello.darwin.amd64") -} diff --git a/src/cmd/link/load.go b/src/cmd/link/load.go deleted file mode 100644 index 50602b82a1..0000000000 --- a/src/cmd/link/load.go +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2014 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. - -// Loading of code and data fragments from package files into final image. - -package main - -import ( - "cmd/internal/obj" - "os" -) - -// load allocates segment images, populates them with data -// read from package files, and applies relocations to the data. -func (p *Prog) load() { - // TODO(rsc): mmap the output file and store the data directly. - // That will make writing the output file more efficient. - for _, seg := range p.Segments { - seg.Data = make([]byte, seg.FileSize) - } - for _, pkg := range p.Packages { - p.loadPackage(pkg) - } -} - -// loadPackage loads and relocates data for all the -// symbols needed in the given package. -func (p *Prog) loadPackage(pkg *Package) { - if pkg.File == "" { - // This "package" contains internally generated symbols only. - // All such symbols have a sym.Bytes field holding the actual data - // (if any), plus relocations. - for _, sym := range pkg.Syms { - if sym.Bytes == nil { - continue - } - seg := sym.Section.Segment - off := sym.Addr - seg.VirtAddr - data := seg.Data[off : off+Addr(sym.Size)] - copy(data, sym.Bytes) - p.relocateSym(sym, data) - } - return - } - - // Package stored in file. - f, err := os.Open(pkg.File) - if err != nil { - p.errorf("%v", err) - return - } - defer f.Close() - - // TODO(rsc): Mmap file into memory. - - for _, sym := range pkg.Syms { - if sym.Data.Size == 0 { - continue - } - // TODO(rsc): If not using mmap, at least coalesce nearby reads. - if sym.Section == nil { - p.errorf("internal error: missing section for %s", sym.Name) - } - seg := sym.Section.Segment - off := sym.Addr - seg.VirtAddr - if off >= Addr(len(seg.Data)) || off+Addr(sym.Data.Size) > Addr(len(seg.Data)) { - p.errorf("internal error: allocated space for %s too small: %d bytes for %d+%d (%d)", sym, len(seg.Data), off, sym.Data.Size, sym.Size) - } - data := seg.Data[off : off+Addr(sym.Data.Size)] - _, err := f.ReadAt(data, sym.Data.Offset) - if err != nil { - p.errorf("reading %v: %v", sym.SymID, err) - } - p.relocateSym(sym, data) - } -} - -// relocateSym applies relocations to sym's data. -func (p *Prog) relocateSym(sym *Sym, data []byte) { - for i := range sym.Reloc { - r := &sym.Reloc[i] - targ := p.Syms[r.Sym] - if targ == nil { - p.errorf("%v: reference to undefined symbol %v", sym, r.Sym) - continue - } - val := targ.Addr + Addr(r.Add) - switch r.Type { - default: - p.errorf("%v: unknown relocation type %d", sym, r.Type) - case obj.R_ADDR, obj.R_CALLIND: - // ok - case obj.R_PCREL, obj.R_CALL: - val -= sym.Addr + Addr(r.Offset+r.Size) - } - frag := data[r.Offset : r.Offset+r.Size] - switch r.Size { - default: - p.errorf("%v: unknown relocation size %d", sym, r.Size) - case 4: - // TODO(rsc): Check for overflow? - p.byteorder.PutUint32(frag, uint32(val)) - case 8: - p.byteorder.PutUint64(frag, uint64(val)) - } - } -} diff --git a/src/cmd/link/macho.go b/src/cmd/link/macho.go deleted file mode 100644 index 4e5524be9b..0000000000 --- a/src/cmd/link/macho.go +++ /dev/null @@ -1,380 +0,0 @@ -// Copyright 2014 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. - -// Mach-O (Darwin) object file writing. - -package main - -import ( - "debug/macho" - "encoding/binary" - "io" - "strings" -) - -// machoFormat is the implementation of formatter. -type machoFormat struct{} - -// machoHeader and friends are data structures -// corresponding to the Mach-O file header -// to be written to disk. - -const ( - macho64Bit = 1 << 24 - machoSubCPU386 = 3 -) - -// machoArch describes a Mach-O target architecture. -type machoArch struct { - CPU uint32 - SubCPU uint32 -} - -// machoHeader is the Mach-O file header. -type machoHeader struct { - machoArch - FileType uint32 - Loads []*machoLoad - Segments []*machoSegment - p *Prog // for reporting errors -} - -// machoLoad is a Mach-O load command. -type machoLoad struct { - Type uint32 - Data []uint32 -} - -// machoSegment is a Mach-O segment. -type machoSegment struct { - Name string - VirtAddr Addr - VirtSize Addr - FileOffset Addr - FileSize Addr - Prot1 uint32 - Prot2 uint32 - Flags uint32 - Sections []*machoSection -} - -// machoSection is a Mach-O section, inside a segment. -type machoSection struct { - Name string - Segment string - Addr Addr - Size Addr - Offset uint32 - Align uint32 - Reloc uint32 - Nreloc uint32 - Flags uint32 - Res1 uint32 - Res2 uint32 -} - -// layout positions the segments and sections in p -// to make room for the Mach-O file header. -// That is, it edits their VirtAddr fields to adjust for the presence -// of the Mach-O header at the beginning of the address space. -func (machoFormat) headerSize(p *Prog) (virt, file Addr) { - var h machoHeader - h.init(p) - size := Addr(h.size()) - size = round(size, 4096) - p.HeaderSize = size - return size, size -} - -// write writes p to w as a Mach-O executable. -// layout(p) must have already been called, -// and the number, sizes, and addresses of the segments -// and sections must not have been modified since the call. -func (machoFormat) write(w io.Writer, p *Prog) { - var h machoHeader - h.init(p) - off := Addr(0) - enc := h.encode() - w.Write(enc) - off += Addr(len(enc)) - for _, seg := range p.Segments { - if seg.FileOffset < off { - h.p.errorf("mach-o error: invalid file offset") - } - w.Write(make([]byte, int(seg.FileOffset-off))) - if seg.FileSize != Addr(len(seg.Data)) { - h.p.errorf("mach-o error: invalid file size") - } - w.Write(seg.Data) - off = seg.FileOffset + Addr(len(seg.Data)) - } -} - -// Conversion of Prog to macho data structures. - -// machoArches maps from GOARCH to machoArch. -var machoArches = map[string]machoArch{ - "amd64": { - CPU: uint32(macho.CpuAmd64), - SubCPU: uint32(machoSubCPU386), - }, -} - -// init initializes the header h to describe p. -func (h *machoHeader) init(p *Prog) { - h.p = p - h.Segments = nil - h.Loads = nil - var ok bool - h.machoArch, ok = machoArches[p.GOARCH] - if !ok { - p.errorf("mach-o: unknown target GOARCH %q", p.GOARCH) - return - } - h.FileType = uint32(macho.TypeExec) - - mseg := h.addSegment(p, "__PAGEZERO", nil) - mseg.VirtSize = p.UnmappedSize - - for _, seg := range p.Segments { - h.addSegment(p, "__"+strings.ToUpper(seg.Name), seg) - } - - var data []uint32 - switch h.CPU { - default: - p.errorf("mach-o: unknown cpu %#x for GOARCH %q", h.CPU, p.GOARCH) - case uint32(macho.CpuAmd64): - data = make([]uint32, 2+42) - data[0] = 4 // thread type - data[1] = 42 // word count - data[2+32] = uint32(p.Entry) // RIP register, in two parts - data[2+32+1] = uint32(p.Entry >> 32) - } - - h.Loads = append(h.Loads, &machoLoad{ - Type: uint32(macho.LoadCmdUnixThread), - Data: data, - }) -} - -// addSegment adds to h a Mach-O segment like seg with the given name. -func (h *machoHeader) addSegment(p *Prog, name string, seg *Segment) *machoSegment { - mseg := &machoSegment{ - Name: name, - } - h.Segments = append(h.Segments, mseg) - if seg == nil { - return mseg - } - - mseg.VirtAddr = seg.VirtAddr - mseg.VirtSize = seg.VirtSize - mseg.FileOffset = round(seg.FileOffset, 4096) - mseg.FileSize = seg.FileSize - - if name == "__TEXT" { - // Initially RWX, then just RX - mseg.Prot1 = 7 - mseg.Prot2 = 5 - - // Text segment maps Mach-O header, needed by dynamic linker. - mseg.VirtAddr -= p.HeaderSize - mseg.VirtSize += p.HeaderSize - mseg.FileOffset -= p.HeaderSize - mseg.FileSize += p.HeaderSize - } else { - // RW - mseg.Prot1 = 3 - mseg.Prot2 = 3 - } - - for _, sect := range seg.Sections { - h.addSection(mseg, seg, sect) - } - return mseg -} - -// addSection adds to mseg a Mach-O section like sect, inside seg, with the given name. -func (h *machoHeader) addSection(mseg *machoSegment, seg *Segment, sect *Section) { - msect := &machoSection{ - Name: "__" + sect.Name, - Segment: mseg.Name, - // Reloc: sect.RelocOffset, - // NumReloc: sect.RelocLen / 8, - Addr: sect.VirtAddr, - Size: sect.Size, - } - mseg.Sections = append(mseg.Sections, msect) - - for 1< seg.FileSize-off { - h.p.errorf("mach-o error: section crosses file boundary") - } - msect.Offset = uint32(seg.FileOffset + off) - } else { - // Zero filled. - msect.Flags |= 1 - } - - if sect.Name == "text" { - msect.Flags |= 0x400 // contains executable instructions - } -} - -// A machoWriter helps write Mach-O headers. -// It is basically a buffer with some helper routines for writing integers. -type machoWriter struct { - dst []byte - tmp [8]byte - order binary.ByteOrder - is64 bool - p *Prog -} - -// if64 returns x if w is writing a 64-bit object file; otherwise it returns y. -func (w *machoWriter) if64(x, y interface{}) interface{} { - if w.is64 { - return x - } - return y -} - -// encode encodes each of the given arguments into the writer. -// It encodes uint32, []uint32, uint64, and []uint64 by writing each value -// in turn in the correct byte order for the output file. -// It encodes an Addr as a uint64 if writing a 64-bit output file, or else as a uint32. -// It encodes []byte and string by writing the raw bytes (no length prefix). -// It skips nil values in the args list. -func (w *machoWriter) encode(args ...interface{}) { - for _, arg := range args { - switch arg := arg.(type) { - default: - w.p.errorf("mach-o error: cannot encode %T", arg) - case nil: - // skip - case []byte: - w.dst = append(w.dst, arg...) - case string: - w.dst = append(w.dst, arg...) - case uint32: - w.order.PutUint32(w.tmp[:], arg) - w.dst = append(w.dst, w.tmp[:4]...) - case []uint32: - for _, x := range arg { - w.order.PutUint32(w.tmp[:], x) - w.dst = append(w.dst, w.tmp[:4]...) - } - case uint64: - w.order.PutUint64(w.tmp[:], arg) - w.dst = append(w.dst, w.tmp[:8]...) - case Addr: - if w.is64 { - w.order.PutUint64(w.tmp[:], uint64(arg)) - w.dst = append(w.dst, w.tmp[:8]...) - } else { - if Addr(uint32(arg)) != arg { - w.p.errorf("mach-o error: truncating address %#x to uint32", arg) - } - w.order.PutUint32(w.tmp[:], uint32(arg)) - w.dst = append(w.dst, w.tmp[:4]...) - } - } - } -} - -// segmentSize returns the size of the encoding of seg in bytes. -func (w *machoWriter) segmentSize(seg *machoSegment) int { - if w.is64 { - return 18*4 + 20*4*len(seg.Sections) - } - return 14*4 + 22*4*len(seg.Sections) -} - -// zeroPad returns the string s truncated or padded with NULs to n bytes. -func zeroPad(s string, n int) string { - if len(s) >= n { - return s[:n] - } - return s + strings.Repeat("\x00", n-len(s)) -} - -// size returns the encoded size of the header. -func (h *machoHeader) size() int { - // Could write separate code, but encoding is cheap; encode and throw it away. - return len(h.encode()) -} - -// encode returns the Mach-O encoding of the header. -func (h *machoHeader) encode() []byte { - w := &machoWriter{p: h.p} - w.is64 = h.CPU&macho64Bit != 0 - w.order = w.p.byteorder - - loadSize := 0 - for _, seg := range h.Segments { - loadSize += w.segmentSize(seg) - } - for _, l := range h.Loads { - loadSize += 4 * (2 + len(l.Data)) - } - - w.encode( - w.if64(macho.Magic64, macho.Magic32), - uint32(h.CPU), - uint32(h.SubCPU), - uint32(h.FileType), - uint32(len(h.Loads)+len(h.Segments)), - uint32(loadSize), - uint32(1), - w.if64(uint32(0), nil), - ) - - for _, seg := range h.Segments { - w.encode( - w.if64(uint32(macho.LoadCmdSegment64), uint32(macho.LoadCmdSegment)), - uint32(w.segmentSize(seg)), - zeroPad(seg.Name, 16), - seg.VirtAddr, - seg.VirtSize, - seg.FileOffset, - seg.FileSize, - seg.Prot1, - seg.Prot2, - uint32(len(seg.Sections)), - seg.Flags, - ) - for _, sect := range seg.Sections { - w.encode( - zeroPad(sect.Name, 16), - zeroPad(seg.Name, 16), - sect.Addr, - sect.Size, - sect.Offset, - sect.Align, - sect.Reloc, - sect.Nreloc, - sect.Flags, - sect.Res1, - sect.Res2, - w.if64(uint32(0), nil), - ) - } - } - - for _, load := range h.Loads { - w.encode( - load.Type, - uint32(4*(2+len(load.Data))), - load.Data, - ) - } - - return w.dst -} diff --git a/src/cmd/link/macho_test.go b/src/cmd/link/macho_test.go deleted file mode 100644 index 37c4418b77..0000000000 --- a/src/cmd/link/macho_test.go +++ /dev/null @@ -1,407 +0,0 @@ -// Copyright 2014 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 ( - "bytes" - "debug/macho" - "encoding/binary" - "fmt" - "io/ioutil" - "strings" - "testing" -) - -// Test macho writing by checking that each generated prog can be written -// and then read back using debug/macho to get the same prog. -// Also check against golden testdata file. -var machoWriteTests = []struct { - name string - golden bool - prog *Prog -}{ - // amd64 exit 9 - { - name: "exit9", - golden: true, - prog: &Prog{ - GOARCH: "amd64", - GOOS: "darwin", - UnmappedSize: 0x1000, - Entry: 0x1000, - Segments: []*Segment{ - { - Name: "text", - VirtAddr: 0x1000, - VirtSize: 13, - FileOffset: 0, - FileSize: 13, - Data: []byte{ - 0xb8, 0x01, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX - 0xbf, 0x09, 0x00, 0x00, 0x00, // MOVL $9, DI - 0x0f, 0x05, // SYSCALL - 0xf4, // HLT - }, - Sections: []*Section{ - { - Name: "text", - VirtAddr: 0x1000, - Size: 13, - Align: 64, - }, - }, - }, - }, - }, - }, - - // amd64 write hello world & exit 9 - { - name: "hello", - golden: true, - prog: &Prog{ - GOARCH: "amd64", - GOOS: "darwin", - UnmappedSize: 0x1000, - Entry: 0x1000, - Segments: []*Segment{ - { - Name: "text", - VirtAddr: 0x1000, - VirtSize: 35, - FileOffset: 0, - FileSize: 35, - Data: []byte{ - 0xb8, 0x04, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX - 0xbf, 0x01, 0x00, 0x00, 0x00, // MOVL $1, DI - 0xbe, 0x00, 0x30, 0x00, 0x00, // MOVL $0x3000, SI - 0xba, 0x0c, 0x00, 0x00, 0x00, // MOVL $12, DX - 0x0f, 0x05, // SYSCALL - 0xb8, 0x01, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX - 0xbf, 0x09, 0x00, 0x00, 0x00, // MOVL $9, DI - 0x0f, 0x05, // SYSCALL - 0xf4, // HLT - }, - Sections: []*Section{ - { - Name: "text", - VirtAddr: 0x1000, - Size: 35, - Align: 64, - }, - }, - }, - { - Name: "data", - VirtAddr: 0x2000, - VirtSize: 12, - FileOffset: 0x1000, - FileSize: 12, - Data: []byte("hello world\n"), - Sections: []*Section{ - { - Name: "data", - VirtAddr: 0x2000, - Size: 12, - Align: 64, - }, - }, - }, - }, - }, - }, - - // amd64 write hello world from rodata & exit 0 - { - name: "helloro", - golden: true, - prog: &Prog{ - GOARCH: "amd64", - GOOS: "darwin", - UnmappedSize: 0x1000, - Entry: 0x1000, - Segments: []*Segment{ - { - Name: "text", - VirtAddr: 0x1000, - VirtSize: 0x100c, - FileOffset: 0, - FileSize: 0x100c, - Data: concat( - []byte{ - 0xb8, 0x04, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX - 0xbf, 0x01, 0x00, 0x00, 0x00, // MOVL $1, DI - 0xbe, 0x00, 0x30, 0x00, 0x00, // MOVL $0x3000, SI - 0xba, 0x0c, 0x00, 0x00, 0x00, // MOVL $12, DX - 0x0f, 0x05, // SYSCALL - 0xb8, 0x01, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX - 0xbf, 0x00, 0x00, 0x00, 0x00, // MOVL $0, DI - 0x0f, 0x05, // SYSCALL - 0xf4, // HLT - }, - make([]byte, 0x1000-35), - []byte("hello world\n"), - ), - Sections: []*Section{ - { - Name: "text", - VirtAddr: 0x1000, - Size: 35, - Align: 64, - }, - { - Name: "rodata", - VirtAddr: 0x2000, - Size: 12, - Align: 64, - }, - }, - }, - }, - }, - }, -} - -func concat(xs ...[]byte) []byte { - var out []byte - for _, x := range xs { - out = append(out, x...) - } - return out -} - -func TestMachoWrite(t *testing.T) { - for _, tt := range machoWriteTests { - name := tt.prog.GOARCH + "." + tt.name - prog := cloneProg(tt.prog) - prog.init() - var f machoFormat - vsize, fsize := f.headerSize(prog) - shiftProg(prog, vsize, fsize) - var buf bytes.Buffer - f.write(&buf, prog) - if false { // enable to debug - ioutil.WriteFile("a.out", buf.Bytes(), 0777) - } - read, err := machoRead(machoArches[tt.prog.GOARCH], buf.Bytes()) - if err != nil { - t.Errorf("%s: reading mach-o output:\n\t%v", name, err) - continue - } - diffs := diffProg(read, prog) - if diffs != nil { - t.Errorf("%s: mismatched prog:\n\t%s", name, strings.Join(diffs, "\n\t")) - continue - } - if !tt.golden { - continue - } - checkGolden(t, buf.Bytes(), "testdata/macho."+name) - } -} - -// machoRead reads the mach-o file in data and returns a corresponding prog. -func machoRead(arch machoArch, data []byte) (*Prog, error) { - f, err := macho.NewFile(bytes.NewReader(data)) - if err != nil { - return nil, err - } - - var errors []string - errorf := func(format string, args ...interface{}) { - errors = append(errors, fmt.Sprintf(format, args...)) - } - - magic := uint32(0xFEEDFACE) - if arch.CPU&macho64Bit != 0 { - magic |= 1 - } - if f.Magic != magic { - errorf("header: Magic = %#x, want %#x", f.Magic, magic) - } - if f.Cpu != macho.CpuAmd64 { - errorf("header: CPU = %#x, want %#x", f.Cpu, macho.CpuAmd64) - } - if f.SubCpu != 3 { - errorf("header: SubCPU = %#x, want %#x", f.SubCpu, 3) - } - if f.Type != 2 { - errorf("header: FileType = %d, want %d", f.Type, 2) - } - if f.Flags != 1 { - errorf("header: Flags = %d, want %d", f.Flags, 1) - } - - msects := f.Sections - var limit uint64 - prog := new(Prog) - for _, load := range f.Loads { - switch load := load.(type) { - default: - errorf("unexpected macho load %T %x", load, load.Raw()) - - case macho.LoadBytes: - if len(load) < 8 || len(load)%4 != 0 { - errorf("unexpected load length %d", len(load)) - continue - } - cmd := f.ByteOrder.Uint32(load) - switch macho.LoadCmd(cmd) { - default: - errorf("unexpected macho load cmd %s", macho.LoadCmd(cmd)) - case macho.LoadCmdUnixThread: - data := make([]uint32, len(load[8:])/4) - binary.Read(bytes.NewReader(load[8:]), f.ByteOrder, data) - if len(data) != 44 { - errorf("macho thread len(data) = %d, want 42", len(data)) - continue - } - if data[0] != 4 { - errorf("macho thread type = %d, want 4", data[0]) - } - if data[1] != uint32(len(data))-2 { - errorf("macho thread desc len = %d, want %d", data[1], uint32(len(data))-2) - continue - } - for i, val := range data[2:] { - switch i { - default: - if val != 0 { - errorf("macho thread data[%d] = %#x, want 0", i, val) - } - case 32: - prog.Entry = Addr(val) - case 33: - prog.Entry |= Addr(val) << 32 - } - } - } - - case *macho.Segment: - if load.Addr < limit { - errorf("segments out of order: %q at %#x after %#x", load.Name, load.Addr, limit) - } - limit = load.Addr + load.Memsz - if load.Name == "__PAGEZERO" || load.Addr == 0 && load.Filesz == 0 { - if load.Name != "__PAGEZERO" { - errorf("segment with Addr=0, Filesz=0 is named %q, want %q", load.Name, "__PAGEZERO") - } else if load.Addr != 0 || load.Filesz != 0 { - errorf("segment %q has Addr=%#x, Filesz=%d, want Addr=%#x, Filesz=%d", load.Name, load.Addr, load.Filesz, 0, 0) - } - prog.UnmappedSize = Addr(load.Memsz) - continue - } - - if !strings.HasPrefix(load.Name, "__") { - errorf("segment name %q does not begin with %q", load.Name, "__") - } - if strings.ToUpper(load.Name) != load.Name { - errorf("segment name %q is not all upper case", load.Name) - } - - seg := &Segment{ - Name: strings.ToLower(strings.TrimPrefix(load.Name, "__")), - VirtAddr: Addr(load.Addr), - VirtSize: Addr(load.Memsz), - FileOffset: Addr(load.Offset), - FileSize: Addr(load.Filesz), - } - prog.Segments = append(prog.Segments, seg) - - data, err := load.Data() - if err != nil { - errorf("loading data from %q: %v", load.Name, err) - } - seg.Data = data - - var maxprot, prot uint32 - if load.Name == "__TEXT" { - maxprot, prot = 7, 5 - } else { - maxprot, prot = 3, 3 - } - if load.Maxprot != maxprot || load.Prot != prot { - errorf("segment %q protection is %d, %d, want %d, %d", - load.Name, load.Maxprot, load.Prot, maxprot, prot) - } - - for len(msects) > 0 && msects[0].Addr < load.Addr+load.Memsz { - msect := msects[0] - msects = msects[1:] - - if msect.Offset > 0 && prog.HeaderSize == 0 { - prog.HeaderSize = Addr(msect.Offset) - if seg.FileOffset != 0 { - errorf("initial segment %q does not map header", load.Name) - } - seg.VirtAddr += prog.HeaderSize - seg.VirtSize -= prog.HeaderSize - seg.FileOffset += prog.HeaderSize - seg.FileSize -= prog.HeaderSize - seg.Data = seg.Data[prog.HeaderSize:] - } - - if msect.Addr < load.Addr { - errorf("section %q at address %#x is missing segment", msect.Name, msect.Addr) - continue - } - - if !strings.HasPrefix(msect.Name, "__") { - errorf("section name %q does not begin with %q", msect.Name, "__") - } - if strings.ToLower(msect.Name) != msect.Name { - errorf("section name %q is not all lower case", msect.Name) - } - if msect.Seg != load.Name { - errorf("section %q is lists segment name %q, want %q", - msect.Name, msect.Seg, load.Name) - } - if uint64(msect.Offset) != uint64(load.Offset)+msect.Addr-load.Addr { - errorf("section %q file offset is %#x, want %#x", - msect.Name, msect.Offset, load.Offset+msect.Addr-load.Addr) - } - if msect.Reloff != 0 || msect.Nreloc != 0 { - errorf("section %q has reloff %d,%d, want %d,%d", - msect.Name, msect.Reloff, msect.Nreloc, 0, 0) - } - flags := uint32(0) - if msect.Name == "__text" { - flags = 0x400 - } - if msect.Offset == 0 { - flags = 1 - } - if msect.Flags != flags { - errorf("section %q flags = %#x, want %#x", msect.Name, msect.Flags, flags) - } - sect := &Section{ - Name: strings.ToLower(strings.TrimPrefix(msect.Name, "__")), - VirtAddr: Addr(msect.Addr), - Size: Addr(msect.Size), - Align: 1 << msect.Align, - } - seg.Sections = append(seg.Sections, sect) - } - } - } - - for _, msect := range msects { - errorf("section %q has no segment", msect.Name) - } - - limit = 0 - for _, msect := range f.Sections { - if msect.Addr < limit { - errorf("sections out of order: %q at %#x after %#x", msect.Name, msect.Addr, limit) - } - limit = msect.Addr + msect.Size - } - - err = nil - if errors != nil { - err = fmt.Errorf("%s", strings.Join(errors, "\n\t")) - } - return prog, err -} diff --git a/src/cmd/link/main.go b/src/cmd/link/main.go deleted file mode 100644 index b23f3f87b0..0000000000 --- a/src/cmd/link/main.go +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2014 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. - -// Placeholder to keep build building. - -package main - -func main() {} diff --git a/src/cmd/link/pclntab.go b/src/cmd/link/pclntab.go deleted file mode 100644 index 7f8bf52a9b..0000000000 --- a/src/cmd/link/pclntab.go +++ /dev/null @@ -1,480 +0,0 @@ -// Copyright 2014 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. - -// Generation of runtime function information (pclntab). - -package main - -import ( - "cmd/internal/goobj" - "cmd/internal/obj" - "encoding/binary" - "os" - "sort" -) - -var zerofunc goobj.Func - -// pclntab collects the runtime function data for each function that will -// be listed in the binary and builds a single table describing all functions. -// This table is used at run time for stack traces and to look up PC-specific -// information during garbage collection. The symbol created is named -// "pclntab" for historical reasons; the scope of the table has grown to -// include more than just PC/line number correspondences. -// The table format is documented at http://golang.org/s/go12symtab. -func (p *Prog) pclntab() { - // Count number of functions going into the binary, - // so that we can size the initial index correctly. - nfunc := 0 - for _, sym := range p.SymOrder { - if sym.Kind != goobj.STEXT { - continue - } - nfunc++ - } - - // Table header. - buf := new(SymBuffer) - buf.Init(p) - buf.SetSize(8 + p.ptrsize) - off := 0 - off = buf.Uint32(off, 0xfffffffb) - off = buf.Uint8(off, 0) - off = buf.Uint8(off, 0) - off = buf.Uint8(off, uint8(p.pcquantum)) - off = buf.Uint8(off, uint8(p.ptrsize)) - off = buf.Uint(off, uint64(nfunc), p.ptrsize) - indexOff := off - off += (nfunc*2 + 1) * p.ptrsize // function index, to be filled in - off += 4 // file table start offset, to be filled in - buf.SetSize(off) - - // One-file cache for reading PCData tables from package files. - // TODO(rsc): Better I/O strategy. - var ( - file *os.File - fname string - ) - - // Files gives the file numbering for source file names recorded - // in the binary. - files := make(map[string]int) - - // Build the table, build the index, and build the file name numbering. - // The loop here must visit functions in the same order that they will - // be stored in the binary, or else binary search over the index will fail. - // The runtime checks that the index is sorted properly at program start time. - var lastSym *Sym - for _, sym := range p.SymOrder { - if sym.Kind != goobj.STEXT { - continue - } - lastSym = sym - - // Treat no recorded function information same as all zeros. - f := sym.Func - if f == nil { - f = &zerofunc - } - - // Open package file if needed, for reading PC data. - if fname != sym.Package.File { - if file != nil { - file.Close() - } - var err error - file, err = os.Open(sym.Package.File) - if err != nil { - p.errorf("%v: %v", sym, err) - return - } - fname = sym.Package.File - } - - // off is the offset of the table entry where we're going to write - // the encoded form of Func. - // indexOff is the current position in the table index; - // we add an entry in the index pointing at off. - off = (buf.Size() + p.ptrsize - 1) &^ (p.ptrsize - 1) - indexOff = buf.Addr(indexOff, sym.SymID, 0) - indexOff = buf.Uint(indexOff, uint64(off), p.ptrsize) - - // The Func encoding starts with a header giving offsets - // to data blobs, and then the data blobs themselves. - // end gives the current write position for the data blobs. - end := off + p.ptrsize + 3*4 + 5*4 + len(f.PCData)*4 + len(f.FuncData)*p.ptrsize - if len(f.FuncData) > 0 { - end += -end & (p.ptrsize - 1) - } - buf.SetSize(end) - - // entry uintptr - // name int32 - // args int32 - // frame int32 - // - // The frame recorded in the object file is - // the frame size used in an assembly listing, which does - // not include the caller PC on the stack. - // The frame size we want to list here is the delta from - // this function's SP to its caller's SP, which does include - // the caller PC. Add p.ptrsize to f.Frame to adjust. - // TODO(rsc): Record the same frame size in the object file. - off = buf.Addr(off, sym.SymID, 0) - off = buf.Uint32(off, uint32(addString(buf, sym.Name))) - off = buf.Uint32(off, uint32(f.Args)) - off = buf.Uint32(off, uint32(f.Frame+p.ptrsize)) - - // pcdata - off = buf.Uint32(off, uint32(addPCTable(p, buf, file, f.PCSP))) - off = buf.Uint32(off, uint32(addPCFileTable(p, buf, file, f.PCFile, sym, files))) - off = buf.Uint32(off, uint32(addPCTable(p, buf, file, f.PCLine))) - off = buf.Uint32(off, uint32(len(f.PCData))) - off = buf.Uint32(off, uint32(len(f.FuncData))) - for _, pcdata := range f.PCData { - off = buf.Uint32(off, uint32(addPCTable(p, buf, file, pcdata))) - } - - // funcdata - if len(f.FuncData) > 0 { - off += -off & (p.ptrsize - 1) // must be pointer-aligned - for _, funcdata := range f.FuncData { - if funcdata.Sym.Name == "" { - off = buf.Uint(off, uint64(funcdata.Offset), p.ptrsize) - } else { - off = buf.Addr(off, funcdata.Sym, funcdata.Offset) - } - } - } - - if off != end { - p.errorf("internal error: invalid math in pclntab: off=%#x end=%#x", off, end) - break - } - } - if file != nil { - file.Close() - } - - // Final entry of index is end PC of last function. - indexOff = buf.Addr(indexOff, lastSym.SymID, int64(lastSym.Size)) - - // Start file table. - // Function index is immediately followed by offset to file table. - off = (buf.Size() + p.ptrsize - 1) &^ (p.ptrsize - 1) - buf.Uint32(indexOff, uint32(off)) - - // File table is an array of uint32s. - // The first entry gives 1+n, the size of the array. - // The following n entries hold offsets to string data. - // File number n uses the string pointed at by entry n. - // File number 0 is invalid. - buf.SetSize(off + (1+len(files))*4) - buf.Uint32(off, uint32(1+len(files))) - var filestr []string - for file := range files { - filestr = append(filestr, file) - } - sort.Strings(filestr) - for _, file := range filestr { - id := files[file] - buf.Uint32(off+4*id, uint32(addString(buf, file))) - } - - pclntab := &Sym{ - Sym: &goobj.Sym{ - SymID: goobj.SymID{Name: "runtime.pclntab"}, - Kind: goobj.SPCLNTAB, - Size: buf.Size(), - Reloc: buf.Reloc(), - }, - Bytes: buf.Bytes(), - } - p.addSym(pclntab) -} - -// addString appends the string s to the buffer b. -// It returns the offset of the beginning of the string in the buffer. -func addString(b *SymBuffer, s string) int { - off := b.Size() - b.SetSize(off + len(s) + 1) - copy(b.data[off:], s) - return off -} - -// addPCTable appends the PC-data table stored in the file f at the location loc -// to the symbol buffer b. It returns the offset of the beginning of the table -// in the buffer. -func addPCTable(p *Prog, b *SymBuffer, f *os.File, loc goobj.Data) int { - if loc.Size == 0 { - return 0 - } - off := b.Size() - b.SetSize(off + int(loc.Size)) - _, err := f.ReadAt(b.data[off:off+int(loc.Size)], loc.Offset) - if err != nil { - p.errorf("%v", err) - } - return off -} - -// addPCFileTable is like addPCTable, but it renumbers the file names referred to by the table -// to use the global numbering maintained in the files map. It adds new files to the -// map as necessary. -func addPCFileTable(p *Prog, b *SymBuffer, f *os.File, loc goobj.Data, sym *Sym, files map[string]int) int { - if loc.Size == 0 { - return 0 - } - off := b.Size() - - src := make([]byte, loc.Size) - _, err := f.ReadAt(src, loc.Offset) - if err != nil { - p.errorf("%v", err) - return 0 - } - - filenum := make([]int, len(sym.Func.File)) - for i, name := range sym.Func.File { - num := files[name] - if num == 0 { - num = len(files) + 1 - files[name] = num - } - filenum[i] = num - } - - var dst []byte - newval := int32(-1) - var it PCIter - for it.Init(p, src); !it.Done; it.Next() { - // value delta - oldval := it.Value - val := oldval - if oldval != -1 { - if oldval < 0 || int(oldval) >= len(filenum) { - p.errorf("%s: corrupt pc-file table", sym) - break - } - val = int32(filenum[oldval]) - } - dv := val - newval - newval = val - uv := uint32(dv<<1) ^ uint32(dv>>31) - dst = appendVarint(dst, uv) - - // pc delta - dst = appendVarint(dst, it.NextPC-it.PC) - } - if it.Corrupt { - p.errorf("%s: corrupt pc-file table", sym) - } - - // terminating value delta - dst = appendVarint(dst, 0) - - b.SetSize(off + len(dst)) - copy(b.data[off:], dst) - return off -} - -// A SymBuffer is a buffer for preparing the data image of a -// linker-generated symbol. -type SymBuffer struct { - data []byte - reloc []goobj.Reloc - order binary.ByteOrder - ptrsize int -} - -// Init initializes the buffer for writing. -func (b *SymBuffer) Init(p *Prog) { - b.data = nil - b.reloc = nil - b.order = p.byteorder - b.ptrsize = p.ptrsize -} - -// Bytes returns the buffer data. -func (b *SymBuffer) Bytes() []byte { - return b.data -} - -// SetSize sets the buffer's data size to n bytes. -func (b *SymBuffer) SetSize(n int) { - for cap(b.data) < n { - b.data = append(b.data[:cap(b.data)], 0) - } - b.data = b.data[:n] -} - -// Size returns the buffer's data size. -func (b *SymBuffer) Size() int { - return len(b.data) -} - -// Reloc returns the buffered relocations. -func (b *SymBuffer) Reloc() []goobj.Reloc { - return b.reloc -} - -// Uint8 sets the uint8 at offset off to v. -// It returns the offset just beyond v. -func (b *SymBuffer) Uint8(off int, v uint8) int { - b.data[off] = v - return off + 1 -} - -// Uint16 sets the uint16 at offset off to v. -// It returns the offset just beyond v. -func (b *SymBuffer) Uint16(off int, v uint16) int { - b.order.PutUint16(b.data[off:], v) - return off + 2 -} - -// Uint32 sets the uint32 at offset off to v. -// It returns the offset just beyond v. -func (b *SymBuffer) Uint32(off int, v uint32) int { - b.order.PutUint32(b.data[off:], v) - return off + 4 -} - -// Uint64 sets the uint64 at offset off to v. -// It returns the offset just beyond v. -func (b *SymBuffer) Uint64(off int, v uint64) int { - b.order.PutUint64(b.data[off:], v) - return off + 8 -} - -// Uint sets the size-byte unsigned integer at offset off to v. -// It returns the offset just beyond v. -func (b *SymBuffer) Uint(off int, v uint64, size int) int { - switch size { - case 1: - return b.Uint8(off, uint8(v)) - case 2: - return b.Uint16(off, uint16(v)) - case 4: - return b.Uint32(off, uint32(v)) - case 8: - return b.Uint64(off, v) - } - panic("invalid use of SymBuffer.SetUint") -} - -// Addr sets the pointer-sized address at offset off to refer -// to symoff bytes past the start of sym. It returns the offset -// just beyond the address. -func (b *SymBuffer) Addr(off int, sym goobj.SymID, symoff int64) int { - b.reloc = append(b.reloc, goobj.Reloc{ - Offset: off, - Size: b.ptrsize, - Sym: sym, - Add: int(symoff), - Type: obj.R_ADDR, - }) - return off + b.ptrsize -} - -// A PCIter implements iteration over PC-data tables. -// -// var it PCIter -// for it.Init(p, data); !it.Done; it.Next() { -// it.Value holds from it.PC up to (but not including) it.NextPC -// } -// if it.Corrupt { -// data was malformed -// } -// -type PCIter struct { - PC uint32 - NextPC uint32 - Value int32 - Done bool - Corrupt bool - p []byte - start bool - pcquantum uint32 -} - -// Init initializes the iteration. -// On return, if it.Done is true, the iteration is over. -// Otherwise it.Value applies in the pc range [it.PC, it.NextPC). -func (it *PCIter) Init(p *Prog, buf []byte) { - it.p = buf - it.PC = 0 - it.NextPC = 0 - it.Value = -1 - it.start = true - it.pcquantum = uint32(p.pcquantum) - it.Done = false - it.Next() -} - -// Next steps forward one entry in the table. -// On return, if it.Done is true, the iteration is over. -// Otherwise it.Value applies in the pc range [it.PC, it.NextPC). -func (it *PCIter) Next() { - it.PC = it.NextPC - if it.Done { - return - } - if len(it.p) == 0 { - it.Done = true - return - } - - // value delta - uv, p, ok := decodeVarint(it.p) - if !ok { - it.Done = true - it.Corrupt = true - return - } - it.p = p - if uv == 0 && !it.start { - it.Done = true - return - } - it.start = false - sv := int32(uv>>1) ^ int32(uv<<31)>>31 - it.Value += sv - - // pc delta - uv, it.p, ok = decodeVarint(it.p) - if !ok { - it.Done = true - it.Corrupt = true - return - } - it.NextPC = it.PC + uv*it.pcquantum -} - -// decodeVarint decodes an unsigned varint from p, -// reporting the value, the remainder of the data, and -// whether the decoding was successful. -func decodeVarint(p []byte) (v uint32, rest []byte, ok bool) { - for shift := uint(0); ; shift += 7 { - if len(p) == 0 { - return - } - c := uint32(p[0]) - p = p[1:] - v |= (c & 0x7F) << shift - if c&0x80 == 0 { - break - } - } - return v, p, true -} - -// appendVarint appends an unsigned varint encoding of v to p -// and returns the resulting slice. -func appendVarint(p []byte, v uint32) []byte { - for ; v >= 0x80; v >>= 7 { - p = append(p, byte(v)|0x80) - } - p = append(p, byte(v)) - return p -} diff --git a/src/cmd/link/pclntab_test.go b/src/cmd/link/pclntab_test.go deleted file mode 100644 index ea80806742..0000000000 --- a/src/cmd/link/pclntab_test.go +++ /dev/null @@ -1,340 +0,0 @@ -// Copyright 2014 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 ( - "bytes" - "cmd/internal/goobj" - "fmt" - "math/rand" - "sort" - "strings" - "testing" -) - -// Test of pcln table encoding. -// testdata/genpcln.go generates an assembly file with -// pseudorandom values for the data that pclntab stores. -// This test recomputes the same pseudorandom stream -// and checks that the final linked binary uses those values -// as well. -func TestPclntab(t *testing.T) { - p := &Prog{ - GOOS: "darwin", - GOARCH: "amd64", - Error: func(s string) { t.Error(s) }, - StartSym: "start", - omitRuntime: true, - } - var buf bytes.Buffer - p.link(&buf, "testdata/pclntab.6") - if p.NumError > 0 { - return - } - - // The algorithm for computing values here must match - // the one in testdata/genpcln.go. - for f := 0; f < 3; f++ { - file := "input" - line := 1 - rnd := rand.New(rand.NewSource(int64(f))) - args := rnd.Intn(100) * 8 - frame := 32 + rnd.Intn(32)/8*8 - size := 200 + rnd.Intn(100)*8 - - name := fmt.Sprintf("func%d", f) - r, off, fargs, fframe, ok := findFunc(t, p, name) - if !ok { - continue // error already printed - } - if fargs != args { - t.Errorf("%s: args=%d, want %d", name, fargs, args) - } - if fframe != frame+8 { - t.Errorf("%s: frame=%d, want %d", name, fframe, frame+8) - } - - // Check FUNCDATA 1. - fdata, ok := loadFuncdata(t, r, name, off, 1) - if ok { - fsym := p.Syms[goobj.SymID{Name: fmt.Sprintf("funcdata%d", f)}] - if fsym == nil { - t.Errorf("funcdata%d is missing in binary", f) - } else if fdata != fsym.Addr { - t.Errorf("%s: funcdata 1 = %#x, want %#x", name, fdata, fsym.Addr) - } - } - - // Walk code checking pcdata values. - spadj := 0 - pcdata1 := -1 - pcdata2 := -1 - - checkPCSP(t, r, name, off, 0, 0) - checkPCData(t, r, name, off, 0, 0, -1) - checkPCData(t, r, name, off, 0, 1, -1) - checkPCData(t, r, name, off, 0, 2, -1) - - firstpc := 4 - for i := 0; i < size; i++ { - pc := firstpc + i // skip SP adjustment to allocate frame - if i >= 0x100 && t.Failed() { - break - } - // Possible SP adjustment. - checkPCSP(t, r, name, off, pc, frame+spadj) - if rnd.Intn(100) == 0 { - checkPCFileLine(t, r, name, off, pc, file, line) - checkPCData(t, r, name, off, pc, 1, pcdata1) - checkPCData(t, r, name, off, pc, 2, pcdata2) - i += 1 - pc = firstpc + i - checkPCFileLine(t, r, name, off, pc-1, file, line) - checkPCData(t, r, name, off, pc-1, 1, pcdata1) - checkPCData(t, r, name, off, pc-1, 2, pcdata2) - checkPCSP(t, r, name, off, pc-1, frame+spadj) - - if spadj <= -32 || spadj < 32 && rnd.Intn(2) == 0 { - spadj += 8 - } else { - spadj -= 8 - } - checkPCSP(t, r, name, off, pc, frame+spadj) - } - - // Possible PCFile change. - if rnd.Intn(100) == 0 { - file = fmt.Sprintf("file%d.s", rnd.Intn(10)) - line = rnd.Intn(100) + 1 - } - - // Possible PCLine change. - if rnd.Intn(10) == 0 { - line = rnd.Intn(1000) + 1 - } - - // Possible PCData $1 change. - if rnd.Intn(100) == 0 { - pcdata1 = rnd.Intn(1000) - } - - // Possible PCData $2 change. - if rnd.Intn(100) == 0 { - pcdata2 = rnd.Intn(1000) - } - - if i == 0 { - checkPCFileLine(t, r, name, off, 0, file, line) - checkPCFileLine(t, r, name, off, pc-1, file, line) - } - checkPCFileLine(t, r, name, off, pc, file, line) - checkPCData(t, r, name, off, pc, 1, pcdata1) - checkPCData(t, r, name, off, pc, 2, pcdata2) - } - } -} - -// findFunc finds the function information in the pclntab of p -// for the function with the given name. -// It returns a symbol reader for pclntab, the offset of the function information -// within that symbol, and the args and frame values read out of the information. -func findFunc(t *testing.T, p *Prog, name string) (r *SymReader, off, args, frame int, ok bool) { - tabsym := p.Syms[goobj.SymID{Name: "runtime.pclntab"}] - if tabsym == nil { - t.Errorf("pclntab is missing in binary") - return - } - - r = new(SymReader) - r.Init(p, tabsym) - - // pclntab must with 8-byte header - if r.Uint32(0) != 0xfffffffb || r.Uint8(4) != 0 || r.Uint8(5) != 0 || r.Uint8(6) != uint8(p.pcquantum) || r.Uint8(7) != uint8(p.ptrsize) { - t.Errorf("pclntab has incorrect header %.8x", r.data[:8]) - return - } - - sym := p.Syms[goobj.SymID{Name: name}] - if sym == nil { - t.Errorf("%s is missing in the binary", name) - return - } - - // index is nfunc addr0 off0 addr1 off1 ... addr_nfunc (sentinel) - nfunc := int(r.Addr(8)) - i := sort.Search(nfunc, func(i int) bool { - return r.Addr(8+p.ptrsize*(1+2*i)) >= sym.Addr - }) - if entry := r.Addr(8 + p.ptrsize*(1+2*i)); entry != sym.Addr { - indexTab := make([]Addr, 2*nfunc+1) - for j := range indexTab { - indexTab[j] = r.Addr(8 + p.ptrsize*(1+j)) - } - t.Errorf("pclntab is missing entry for %s (%#x): %#x", name, sym.Addr, indexTab) - return - } - - off = int(r.Addr(8 + p.ptrsize*(1+2*i+1))) - - // func description at off is - // entry addr - // nameoff uint32 - // args uint32 - // frame uint32 - // pcspoff uint32 - // pcfileoff uint32 - // pclineoff uint32 - // npcdata uint32 - // nfuncdata uint32 - // pcdata npcdata*uint32 - // funcdata nfuncdata*addr - // - if entry := r.Addr(off); entry != sym.Addr { - t.Errorf("pclntab inconsistent: entry for %s addr=%#x has entry=%#x", name, sym.Addr, entry) - return - } - nameoff := int(r.Uint32(off + p.ptrsize)) - args = int(r.Uint32(off + p.ptrsize + 1*4)) - frame = int(r.Uint32(off + p.ptrsize + 2*4)) - - fname := r.String(nameoff) - if fname != name { - t.Errorf("pclntab inconsistent: entry for %s addr=%#x has name %q", name, sym.Addr, fname) - } - - ok = true // off, args, frame are usable - return -} - -// loadFuncdata returns the funcdata #fnum value -// loaded from the function information for name. -func loadFuncdata(t *testing.T, r *SymReader, name string, off int, fnum int) (Addr, bool) { - npcdata := int(r.Uint32(off + r.p.ptrsize + 6*4)) - nfuncdata := int(r.Uint32(off + r.p.ptrsize + 7*4)) - if fnum >= nfuncdata { - t.Errorf("pclntab(%s): no funcdata %d (only < %d)", name, fnum, nfuncdata) - return 0, false - } - fdataoff := off + r.p.ptrsize + (8+npcdata)*4 + fnum*r.p.ptrsize - fdataoff += fdataoff & 4 - return r.Addr(fdataoff), true -} - -// checkPCSP checks that the PCSP table in the function information at off -// lists spadj as the sp delta for pc. -func checkPCSP(t *testing.T, r *SymReader, name string, off, pc, spadj int) { - pcoff := r.Uint32(off + r.p.ptrsize + 3*4) - pcval, ok := readPCData(t, r, name, "PCSP", pcoff, pc) - if !ok { - return - } - if pcval != spadj { - t.Errorf("pclntab(%s): at pc=+%#x, pcsp=%d, want %d", name, pc, pcval, spadj) - } -} - -// checkPCSP checks that the PCFile and PCLine tables in the function information at off -// list file, line as the file name and line number for pc. -func checkPCFileLine(t *testing.T, r *SymReader, name string, off, pc int, file string, line int) { - pcfileoff := r.Uint32(off + r.p.ptrsize + 4*4) - pclineoff := r.Uint32(off + r.p.ptrsize + 5*4) - pcfilenum, ok1 := readPCData(t, r, name, "PCFile", pcfileoff, pc) - pcline, ok2 := readPCData(t, r, name, "PCLine", pclineoff, pc) - if !ok1 || !ok2 { - return - } - nfunc := int(r.Addr(8)) - filetaboff := r.Uint32(8 + r.p.ptrsize*2*(nfunc+1)) - nfile := int(r.Uint32(int(filetaboff))) - if pcfilenum <= 0 || pcfilenum >= nfile { - t.Errorf("pclntab(%s): at pc=+%#x, filenum=%d (invalid; nfile=%d)", name, pc, pcfilenum, nfile) - } - pcfile := r.String(int(r.Uint32(int(filetaboff) + pcfilenum*4))) - if !strings.HasSuffix(pcfile, file) { - t.Errorf("pclntab(%s): at pc=+%#x, file=%q, want %q", name, pc, pcfile, file) - } - if pcline != line { - t.Errorf("pclntab(%s): at pc=+%#x, line=%d, want %d", name, pc, pcline, line) - } -} - -// checkPCData checks that the PCData#pnum table in the function information at off -// list val as the value for pc. -func checkPCData(t *testing.T, r *SymReader, name string, off, pc, pnum, val int) { - pcoff := r.Uint32(off + r.p.ptrsize + (8+pnum)*4) - pcval, ok := readPCData(t, r, name, fmt.Sprintf("PCData#%d", pnum), pcoff, pc) - if !ok { - return - } - if pcval != val { - t.Errorf("pclntab(%s): at pc=+%#x, pcdata#%d=%d, want %d", name, pc, pnum, pcval, val) - } -} - -// readPCData reads the PCData table offset off -// to obtain and return the value associated with pc. -func readPCData(t *testing.T, r *SymReader, name, pcdataname string, pcoff uint32, pc int) (int, bool) { - // "If pcsp, pcfile, pcln, or any of the pcdata offsets is zero, - // that table is considered missing, and all PCs take value -1." - if pcoff == 0 { - return -1, true - } - - var it PCIter - for it.Init(r.p, r.data[pcoff:]); !it.Done; it.Next() { - if it.PC <= uint32(pc) && uint32(pc) < it.NextPC { - return int(it.Value), true - } - } - if it.Corrupt { - t.Errorf("pclntab(%s): %s: corrupt pcdata table", name, pcdataname) - } - return 0, false -} - -// A SymReader provides typed access to the data for a symbol. -type SymReader struct { - p *Prog - data []byte -} - -func (r *SymReader) Init(p *Prog, sym *Sym) { - seg := sym.Section.Segment - off := sym.Addr - seg.VirtAddr - data := seg.Data[off : off+Addr(sym.Size)] - r.p = p - r.data = data -} - -func (r *SymReader) Uint8(off int) uint8 { - return r.data[off] -} - -func (r *SymReader) Uint16(off int) uint16 { - return r.p.byteorder.Uint16(r.data[off:]) -} - -func (r *SymReader) Uint32(off int) uint32 { - return r.p.byteorder.Uint32(r.data[off:]) -} - -func (r *SymReader) Uint64(off int) uint64 { - return r.p.byteorder.Uint64(r.data[off:]) -} - -func (r *SymReader) Addr(off int) Addr { - if r.p.ptrsize == 4 { - return Addr(r.Uint32(off)) - } - return Addr(r.Uint64(off)) -} - -func (r *SymReader) String(off int) string { - end := off - for r.data[end] != '\x00' { - end++ - } - return string(r.data[off:end]) -} diff --git a/src/cmd/link/prog.go b/src/cmd/link/prog.go deleted file mode 100644 index 77fb1ece5b..0000000000 --- a/src/cmd/link/prog.go +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright 2014 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/goobj" - "encoding/binary" - "fmt" - "go/build" - "io" - "os" - "runtime" -) - -// A Prog holds state for constructing an executable (program) image. -// -// The usual sequence of operations on a Prog is: -// -// p.init() -// p.scan(file) -// p.dead() -// p.runtime() -// p.layout() -// p.load() -// p.debug() -// p.write(w) -// -// p.init is in this file. The rest of the methods are in files -// named for the method. The convenience method p.link runs -// this sequence. -// -type Prog struct { - // Context - GOOS string // target operating system - GOARCH string // target architecture - Format string // desired file format ("elf", "macho", ...) - Error func(string) // called to report an error (if set) - NumError int // number of errors printed - StartSym string - - // Derived context - arch - formatter formatter - startSym goobj.SymID - pkgdir string - omitRuntime bool // do not load runtime package - - // Input - Packages map[string]*Package // loaded packages, by import path - Syms map[goobj.SymID]*Sym // defined symbols, by symbol ID - Missing map[goobj.SymID]bool // missing symbols - Dead map[goobj.SymID]bool // symbols removed as dead - SymOrder []*Sym // order syms were scanned - MaxVersion int // max SymID.Version, for generating fresh symbol IDs - - // Output - UnmappedSize Addr // size of unmapped region at address 0 - HeaderSize Addr // size of object file header - Entry Addr // virtual address where execution begins - Segments []*Segment // loaded memory segments -} - -// An arch describes architecture-dependent settings. -type arch struct { - byteorder binary.ByteOrder - ptrsize int - pcquantum int -} - -// A formatter takes care of the details of generating a particular -// kind of executable file. -type formatter interface { - // headerSize returns the footprint of the header for p - // in both virtual address space and file bytes. - // The footprint does not include any bytes stored at the - // end of the file. - headerSize(p *Prog) (virt, file Addr) - - // write writes the executable file for p to w. - write(w io.Writer, p *Prog) -} - -// An Addr represents a virtual memory address, a file address, or a size. -// It must be a uint64, not a uintptr, so that a 32-bit linker can still generate a 64-bit binary. -// It must be unsigned in order to link programs placed at very large start addresses. -// Math involving Addrs must be checked carefully not to require negative numbers. -type Addr uint64 - -// A Package is a Go package loaded from a file. -type Package struct { - *goobj.Package // table of contents - File string // file name for reopening - Syms []*Sym // symbols defined by this package -} - -// A Sym is a symbol defined in a loaded package. -type Sym struct { - *goobj.Sym // symbol metadata from package file - Package *Package // package defining symbol - Section *Section // section where symbol is placed in output program - Addr Addr // virtual address of symbol in output program - Bytes []byte // symbol data, for internally defined symbols -} - -// A Segment is a loaded memory segment. -// A Prog is expected to have segments named "text" and optionally "data", -// in that order, before any other segments. -type Segment struct { - Name string // name of segment: "text", "data", ... - VirtAddr Addr // virtual memory address of segment base - VirtSize Addr // size of segment in memory - FileOffset Addr // file offset of segment base - FileSize Addr // size of segment in file; can be less than VirtSize - Sections []*Section // sections inside segment - Data []byte // raw data of segment image -} - -// A Section is part of a loaded memory segment. -type Section struct { - Name string // name of section: "text", "rodata", "noptrbss", and so on - VirtAddr Addr // virtual memory address of section base - Size Addr // size of section in memory - Align Addr // required alignment - InFile bool // section has image data in file (like data, unlike bss) - Syms []*Sym // symbols stored in section - Segment *Segment // segment containing section -} - -func (p *Prog) errorf(format string, args ...interface{}) { - if p.Error != nil { - p.Error(fmt.Sprintf(format, args...)) - } else { - fmt.Fprintf(os.Stderr, format+"\n", args...) - } - p.NumError++ -} - -// link is the one-stop convenience method for running a link. -// It writes to w the object file generated from using mainFile as the main package. -func (p *Prog) link(w io.Writer, mainFile string) { - p.init() - p.scan(mainFile) - if p.NumError > 0 { - return - } - p.dead() - p.runtime() - p.autoData() - p.layout() - p.autoConst() - if p.NumError > 0 { - return - } - p.load() - if p.NumError > 0 { - return - } - p.debug() - if p.NumError > 0 { - return - } - p.write(w) -} - -// init initializes p for use by the other methods. -func (p *Prog) init() { - // Set default context if not overridden. - if p.GOOS == "" { - p.GOOS = build.Default.GOOS - } - if p.GOARCH == "" { - p.GOARCH = build.Default.GOARCH - } - if p.Format == "" { - p.Format = goosFormat[p.GOOS] - if p.Format == "" { - p.errorf("no default file format for GOOS %q", p.GOOS) - return - } - } - if p.StartSym == "" { - p.StartSym = fmt.Sprintf("_rt0_%s_%s", p.GOARCH, p.GOOS) - } - - // Derive internal context. - p.formatter = formatters[p.Format] - if p.formatter == nil { - p.errorf("unknown output file format %q", p.Format) - return - } - p.startSym = goobj.SymID{Name: p.StartSym} - arch, ok := arches[p.GOARCH] - if !ok { - p.errorf("unknown GOOS %q", p.GOOS) - return - } - p.arch = arch - - p.pkgdir = fmt.Sprintf("%s/pkg/%s_%s", runtime.GOROOT(), p.GOOS, p.GOARCH) -} - -// goosFormat records the default format for each known GOOS value. -var goosFormat = map[string]string{ - "darwin": "darwin", -} - -// formatters records the format implementation for each known format value. -var formatters = map[string]formatter{ - "darwin": machoFormat{}, -} - -var arches = map[string]arch{ - "amd64": { - byteorder: binary.LittleEndian, - ptrsize: 8, - pcquantum: 1, - }, -} diff --git a/src/cmd/link/prog_test.go b/src/cmd/link/prog_test.go deleted file mode 100644 index 0853506957..0000000000 --- a/src/cmd/link/prog_test.go +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2014 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 ( - "bytes" - "fmt" - "io/ioutil" - "testing" -) - -// shiftProg adjusts the addresses in p. -// It adds vdelta to all virtual addresses and fdelta to all file offsets. -func shiftProg(p *Prog, vdelta Addr, fdelta Addr) { - p.Entry += vdelta - for _, seg := range p.Segments { - seg.FileOffset += fdelta - seg.VirtAddr += vdelta - for _, sect := range seg.Sections { - sect.VirtAddr += vdelta - for _, sym := range sect.Syms { - sym.Addr += vdelta - } - } - } -} - -// diffProg returns a list of differences between p and q, -// assuming p is being checked and q is the correct answer. -func diffProg(p, q *Prog) []string { - var errors []string - if p.UnmappedSize != q.UnmappedSize { - errors = append(errors, fmt.Sprintf("p.UnmappedSize = %#x, want %#x", p.UnmappedSize, q.UnmappedSize)) - } - if p.HeaderSize != q.HeaderSize { - errors = append(errors, fmt.Sprintf("p.HeaderSize = %#x, want %#x", p.HeaderSize, q.HeaderSize)) - } - if p.Entry != q.Entry { - errors = append(errors, fmt.Sprintf("p.Entry = %#x, want %#x", p.Entry, q.Entry)) - } - for i := 0; i < len(p.Segments) || i < len(q.Segments); i++ { - if i >= len(p.Segments) { - errors = append(errors, fmt.Sprintf("p missing segment %q", q.Segments[i].Name)) - continue - } - if i >= len(q.Segments) { - errors = append(errors, fmt.Sprintf("p has extra segment %q", p.Segments[i].Name)) - continue - } - pseg := p.Segments[i] - qseg := q.Segments[i] - if pseg.Name != qseg.Name { - errors = append(errors, fmt.Sprintf("segment %d Name = %q, want %q", i, pseg.Name, qseg.Name)) - continue // probably out of sync - } - if pseg.VirtAddr != qseg.VirtAddr { - errors = append(errors, fmt.Sprintf("segment %q VirtAddr = %#x, want %#x", pseg.Name, pseg.VirtAddr, qseg.VirtAddr)) - } - if pseg.VirtSize != qseg.VirtSize { - errors = append(errors, fmt.Sprintf("segment %q VirtSize = %#x, want %#x", pseg.Name, pseg.VirtSize, qseg.VirtSize)) - } - if pseg.FileOffset != qseg.FileOffset { - errors = append(errors, fmt.Sprintf("segment %q FileOffset = %#x, want %#x", pseg.Name, pseg.FileOffset, qseg.FileOffset)) - } - if pseg.FileSize != qseg.FileSize { - errors = append(errors, fmt.Sprintf("segment %q FileSize = %#x, want %#x", pseg.Name, pseg.FileSize, qseg.FileSize)) - } - if len(pseg.Data) != len(qseg.Data) { - errors = append(errors, fmt.Sprintf("segment %q len(Data) = %d, want %d", pseg.Name, len(pseg.Data), len(qseg.Data))) - } else if !bytes.Equal(pseg.Data, qseg.Data) { - errors = append(errors, fmt.Sprintf("segment %q Data mismatch:\n\thave %x\n\twant %x", pseg.Name, pseg.Data, qseg.Data)) - } - - for j := 0; j < len(pseg.Sections) || j < len(qseg.Sections); j++ { - if j >= len(pseg.Sections) { - errors = append(errors, fmt.Sprintf("segment %q missing section %q", pseg.Name, qseg.Sections[i].Name)) - continue - } - if j >= len(qseg.Sections) { - errors = append(errors, fmt.Sprintf("segment %q has extra section %q", pseg.Name, pseg.Sections[i].Name)) - continue - } - psect := pseg.Sections[j] - qsect := qseg.Sections[j] - if psect.Name != qsect.Name { - errors = append(errors, fmt.Sprintf("segment %q, section %d Name = %q, want %q", pseg.Name, j, psect.Name, qsect.Name)) - continue // probably out of sync - } - - if psect.VirtAddr != qsect.VirtAddr { - errors = append(errors, fmt.Sprintf("segment %q section %q VirtAddr = %#x, want %#x", pseg.Name, psect.Name, psect.VirtAddr, qsect.VirtAddr)) - } - if psect.Size != qsect.Size { - errors = append(errors, fmt.Sprintf("segment %q section %q Size = %#x, want %#x", pseg.Name, psect.Name, psect.Size, qsect.Size)) - } - if psect.Align != qsect.Align { - errors = append(errors, fmt.Sprintf("segment %q section %q Align = %#x, want %#x", pseg.Name, psect.Name, psect.Align, qsect.Align)) - } - } - } - - return errors -} - -// cloneProg returns a deep copy of p. -func cloneProg(p *Prog) *Prog { - q := new(Prog) - *q = *p - q.Segments = make([]*Segment, len(p.Segments)) - for i, seg := range p.Segments { - q.Segments[i] = cloneSegment(seg) - } - return q -} - -// cloneSegment returns a deep copy of seg. -func cloneSegment(seg *Segment) *Segment { - t := new(Segment) - *t = *seg - t.Sections = make([]*Section, len(seg.Sections)) - for i, sect := range seg.Sections { - t.Sections[i] = cloneSection(sect) - } - t.Data = make([]byte, len(seg.Data)) - copy(t.Data, seg.Data) - return t -} - -// cloneSection returns a deep copy of section. -func cloneSection(sect *Section) *Section { - // At the moment, there's nothing we need to make a deep copy of. - t := new(Section) - *t = *sect - return t -} - -const saveMismatch = true - -// checkGolden checks that data matches the named file. -// If not, it reports the error to the test. -func checkGolden(t *testing.T, data []byte, name string) { - golden := mustParseHexdumpFile(t, name) - if !bytes.Equal(data, golden) { - if saveMismatch { - ioutil.WriteFile(name+".raw", data, 0666) - ioutil.WriteFile(name+".hex", []byte(hexdump(data)), 0666) - } - // TODO(rsc): A better diff would be nice, as needed. - i := 0 - for i < len(data) && i < len(golden) && data[i] == golden[i] { - i++ - } - if i >= len(data) { - t.Errorf("%s: output file shorter than expected: have %d bytes, want %d", name, len(data), len(golden)) - } else if i >= len(golden) { - t.Errorf("%s: output file larger than expected: have %d bytes, want %d", name, len(data), len(golden)) - } else { - t.Errorf("%s: output file differs at byte %d: have %#02x, want %#02x", name, i, data[i], golden[i]) - } - } -} diff --git a/src/cmd/link/runtime.go b/src/cmd/link/runtime.go deleted file mode 100644 index acda2d24d6..0000000000 --- a/src/cmd/link/runtime.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2014 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. - -// Generation of runtime-accessible data structures. -// See also debug.go. - -package main - -import "cmd/internal/goobj" - -func (p *Prog) runtime() { - p.pclntab() - - // TODO: Implement garbage collection data. - p.addSym(&Sym{ - Sym: &goobj.Sym{ - SymID: goobj.SymID{Name: "runtime.gcdata"}, - Kind: goobj.SRODATA, - }, - }) - p.addSym(&Sym{ - Sym: &goobj.Sym{ - SymID: goobj.SymID{Name: "runtime.gcbss"}, - Kind: goobj.SRODATA, - }, - }) -} diff --git a/src/cmd/link/scan.go b/src/cmd/link/scan.go deleted file mode 100644 index 7feb0d8900..0000000000 --- a/src/cmd/link/scan.go +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright 2014 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. - -// Initial scan of packages making up a program. - -// TODO(rsc): Rename goobj.SymID.Version to StaticID to avoid confusion with the ELF meaning of version. -// TODO(rsc): Fix file format so that SBSS/SNOPTRBSS with data is listed as SDATA/SNOPTRDATA. -// TODO(rsc): Parallelize scan to overlap file i/o where possible. - -package main - -import ( - "cmd/internal/goobj" - "os" - "sort" - "strings" -) - -// scan scans all packages making up the program, starting with package main defined in mainfile. -func (p *Prog) scan(mainfile string) { - p.initScan() - p.scanFile("main", mainfile) - if len(p.Missing) > 0 && !p.omitRuntime { - p.scanImport("runtime") - } - - var missing []string - for sym := range p.Missing { - if !p.isAuto(sym) { - missing = append(missing, sym.String()) - } - } - - if missing != nil { - sort.Strings(missing) - for _, sym := range missing { - p.errorf("undefined: %s", sym) - } - } - - // TODO(rsc): Walk import graph to diagnose cycles. -} - -// initScan initializes the Prog fields needed by scan. -func (p *Prog) initScan() { - p.Packages = make(map[string]*Package) - p.Syms = make(map[goobj.SymID]*Sym) - p.Missing = make(map[goobj.SymID]bool) - p.Missing[p.startSym] = true -} - -// scanFile reads file to learn about the package with the given import path. -func (p *Prog) scanFile(pkgpath string, file string) { - pkg := &Package{ - File: file, - } - p.Packages[pkgpath] = pkg - - f, err := os.Open(file) - if err != nil { - p.errorf("%v", err) - return - } - gp, err := goobj.Parse(f, pkgpath) - f.Close() - if err != nil { - p.errorf("reading %s: %v", file, err) - return - } - - // TODO(rsc): Change cmd/internal/goobj to record package name as gp.Name. - // TODO(rsc): If pkgpath == "main", check that gp.Name == "main". - - pkg.Package = gp - - for _, gs := range gp.Syms { - // TODO(rsc): Fix file format instead of this workaround. - if gs.Data.Size > 0 { - switch gs.Kind { - case goobj.SBSS: - gs.Kind = goobj.SDATA - case goobj.SNOPTRBSS: - gs.Kind = goobj.SNOPTRDATA - } - } - - if gs.Version != 0 { - gs.Version += p.MaxVersion - } - for i := range gs.Reloc { - r := &gs.Reloc[i] - if r.Sym.Version != 0 { - r.Sym.Version += p.MaxVersion - } - if p.Syms[r.Sym] == nil { - p.Missing[r.Sym] = true - } - } - if gs.Func != nil { - for i := range gs.Func.FuncData { - fdata := &gs.Func.FuncData[i] - if fdata.Sym.Name != "" { - if fdata.Sym.Version != 0 { - fdata.Sym.Version += p.MaxVersion - } - if p.Syms[fdata.Sym] == nil { - p.Missing[fdata.Sym] = true - } - } - } - } - if old := p.Syms[gs.SymID]; old != nil { - // Duplicate definition of symbol. Is it okay? - // TODO(rsc): Write test for this code. - switch { - // If both symbols are BSS (no data), take max of sizes - // but otherwise ignore second symbol. - case old.Data.Size == 0 && gs.Data.Size == 0: - if old.Size < gs.Size { - old.Size = gs.Size - } - continue - - // If one is in BSS and one is not, use the one that is not. - case old.Data.Size > 0 && gs.Data.Size == 0: - continue - case gs.Data.Size > 0 && old.Data.Size == 0: - break // install gs as new symbol below - - // If either is marked as DupOK, we can keep either one. - // Keep the one that we saw first. - case old.DupOK || gs.DupOK: - continue - - // Otherwise, there's an actual conflict: - default: - p.errorf("symbol %s defined in both %s and %s %v %v", gs.SymID, old.Package.File, file, old.Data, gs.Data) - continue - } - } - s := &Sym{ - Sym: gs, - Package: pkg, - } - p.addSym(s) - delete(p.Missing, gs.SymID) - - if s.Data.Size > int64(s.Size) { - p.errorf("%s: initialized data larger than symbol (%d > %d)", s, s.Data.Size, s.Size) - } - } - p.MaxVersion += pkg.MaxVersion - - for i, pkgpath := range pkg.Imports { - // TODO(rsc): Fix file format to drop .a from recorded import path. - pkgpath = strings.TrimSuffix(pkgpath, ".a") - pkg.Imports[i] = pkgpath - - p.scanImport(pkgpath) - } -} - -func (p *Prog) addSym(s *Sym) { - pkg := s.Package - if pkg == nil { - pkg = p.Packages[""] - if pkg == nil { - pkg = &Package{} - p.Packages[""] = pkg - } - s.Package = pkg - } - pkg.Syms = append(pkg.Syms, s) - p.Syms[s.SymID] = s - p.SymOrder = append(p.SymOrder, s) -} - -// scanImport finds the object file for the given import path and then scans it. -func (p *Prog) scanImport(pkgpath string) { - if p.Packages[pkgpath] != nil { - return // already loaded - } - - // TODO(rsc): Implement correct search to find file. - p.scanFile(pkgpath, p.pkgdir+"/"+pkgpath+".a") -} diff --git a/src/cmd/link/testdata/Makefile b/src/cmd/link/testdata/Makefile deleted file mode 100644 index 83e8a05195..0000000000 --- a/src/cmd/link/testdata/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -ALL=\ - autosection.6\ - autoweak.6\ - dead.6\ - hello.6\ - layout.6\ - pclntab.6\ - -all: $(ALL) - -%.6: %.s - GOARCH=amd64 GOOS=darwin go tool asm -I $(shell go env GOROOT)/pkg/include -trimpath=$(shell pwd) $*.s - -pclntab.s: genpcln.go - go run genpcln.go >pclntab.s diff --git a/src/cmd/link/testdata/autosection.6 b/src/cmd/link/testdata/autosection.6 deleted file mode 100644 index 34aa5d3dbb..0000000000 Binary files a/src/cmd/link/testdata/autosection.6 and /dev/null differ diff --git a/src/cmd/link/testdata/autosection.s b/src/cmd/link/testdata/autosection.s deleted file mode 100644 index e0cb21723e..0000000000 --- a/src/cmd/link/testdata/autosection.s +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2014 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 of section-named symbols. - -#include "textflag.h" - -TEXT start(SB),7,$0 - MOVQ $autotab(SB),AX - MOVQ $autoptr(SB),AX - RET - -GLOBL zero(SB), $8 - -GLOBL zeronoptr(SB), NOPTR, $16 - -// text -DATA autotab+0x00(SB)/8, $runtime·text(SB) -DATA autotab+0x08(SB)/8, $start(SB) -DATA autotab+0x10(SB)/8, $runtime·etext(SB) -DATA autotab+0x18(SB)/8, $start+16(SB) - -// data -DATA autotab+0x20(SB)/8, $runtime·data(SB) -DATA autotab+0x28(SB)/8, $autotab(SB) -DATA autotab+0x30(SB)/8, $runtime·edata(SB) -DATA autotab+0x38(SB)/8, $nonzero+4(SB) - -// bss -DATA autotab+0x40(SB)/8, $runtime·bss(SB) -DATA autotab+0x48(SB)/8, $zero(SB) -DATA autotab+0x50(SB)/8, $runtime·ebss(SB) -DATA autotab+0x58(SB)/8, $zero+8(SB) - -// noptrdata -DATA autotab+0x60(SB)/8, $runtime·noptrdata(SB) -DATA autotab+0x68(SB)/8, $nonzeronoptr(SB) -DATA autotab+0x70(SB)/8, $runtime·enoptrdata(SB) -DATA autotab+0x78(SB)/8, $nonzeronoptr+8(SB) - -// noptrbss -DATA autotab+0x80(SB)/8, $runtime·noptrbss(SB) -DATA autotab+0x88(SB)/8, $zeronoptr(SB) -DATA autotab+0x90(SB)/8, $runtime·enoptrbss(SB) -DATA autotab+0x98(SB)/8, $zeronoptr+16(SB) - -// end -DATA autotab+0xa0(SB)/8, $runtime·end(SB) -DATA autotab+0xa8(SB)/8, $zeronoptr+16(SB) - -GLOBL autotab(SB), $0xb0 - -DATA nonzero(SB)/4, $1 -GLOBL nonzero(SB), $4 - -DATA nonzeronoptr(SB)/8, $2 -GLOBL nonzeronoptr(SB), NOPTR, $8 - -GLOBL autoptr(SB), $0 diff --git a/src/cmd/link/testdata/autoweak.6 b/src/cmd/link/testdata/autoweak.6 deleted file mode 100644 index a86b841171..0000000000 Binary files a/src/cmd/link/testdata/autoweak.6 and /dev/null differ diff --git a/src/cmd/link/testdata/autoweak.s b/src/cmd/link/testdata/autoweak.s deleted file mode 100644 index 2d11330db2..0000000000 --- a/src/cmd/link/testdata/autoweak.s +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2014 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 of go.weak symbols. - -TEXT start(SB),7,$0 - MOVQ $autotab(SB),AX - MOVQ $autoptr(SB),AX - RET - -// go.weak.sym should resolve to sym, because sym is in the binary. -DATA autotab+0(SB)/8, $go·weak·sym(SB) -DATA autotab+8(SB)/8, $sym(SB) - -// go.weak.missingsym should resolve to 0, because missingsym is not in the binary. -DATA autotab+16(SB)/8, $go·weak·missingsym(SB) -DATA autotab+24(SB)/8, $0 - -// go.weak.deadsym should resolve to 0, because deadsym is discarded during dead code removal -DATA autotab+32(SB)/8, $go·weak·deadsym(SB) -DATA autotab+40(SB)/8, $0 - -GLOBL autotab(SB), $48 - -GLOBL sym(SB), $1 - -GLOBL deadsym(SB), $1 - -GLOBL autoptr(SB), $0 diff --git a/src/cmd/link/testdata/dead.6 b/src/cmd/link/testdata/dead.6 deleted file mode 100644 index 5e9b09c03e..0000000000 Binary files a/src/cmd/link/testdata/dead.6 and /dev/null differ diff --git a/src/cmd/link/testdata/dead.s b/src/cmd/link/testdata/dead.s deleted file mode 100644 index 86f31360fa..0000000000 --- a/src/cmd/link/testdata/dead.s +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2014 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 of dead code removal. -// Symbols with names beginning with dead_ should be discarded. -// Others should be kept. - -TEXT start(SB),7,$0 // start symbol - MOVQ $data1<>(SB), AX - CALL text1(SB) - MOVQ $text2(SB), BX - RET - -TEXT text1(SB),7,$0 - FUNCDATA $1, funcdata+4(SB) - RET - -TEXT text2(SB),7,$0 - MOVQ $runtime·edata(SB),BX - RET - -DATA data1<>+0(SB)/8, $data2(SB) -DATA data1<>+8(SB)/8, $data3(SB) -GLOBL data1<>(SB), $16 -GLOBL data2(SB), $1 -GLOBL data3(SB), $1 -GLOBL funcdata(SB), $8 - -TEXT dead_start(SB),7,$0 - MOVQ $dead_data1(SB), AX - CALL dead_text1(SB) - MOVQ $dead_text2(SB), BX - RET - -TEXT dead_text1(SB),7,$0 - FUNCDATA $1, dead_funcdata+4(SB) - RET - -TEXT dead_text2(SB),7,$0 - RET - -DATA dead_data1+0(SB)/8, $dead_data2(SB) -DATA dead_data1+8(SB)/8, $dead_data3(SB) -GLOBL dead_data1(SB), $16 -GLOBL dead_data2(SB), $1 -GLOBL dead_data3(SB), $1 -GLOBL dead_funcdata(SB), $8 diff --git a/src/cmd/link/testdata/genpcln.go b/src/cmd/link/testdata/genpcln.go deleted file mode 100644 index c10eaeae91..0000000000 --- a/src/cmd/link/testdata/genpcln.go +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2014 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. - -// This program generates a .s file using a pseudorandom -// value stream for the runtime function data. -// The pclntab test checks that the linked copy -// still has the same pseudorandom value stream. - -package main - -import ( - "fmt" - "math/rand" -) - -func main() { - fmt.Printf("// generated by genpcln.go; do not edit\n\n") - for f := 0; f < 3; f++ { - r := rand.New(rand.NewSource(int64(f))) - file := "input" - line := 1 - args := r.Intn(100) * 8 - frame := 32 + r.Intn(32)/8*8 - fmt.Printf("#line %d %q\n", line, file) - fmt.Printf("TEXT func%d(SB),7,$%d-%d\n", f, frame, args) - fmt.Printf("\tFUNCDATA $1, funcdata%d(SB)\n", f) - fmt.Printf("#line %d %q\n", line, file) - size := 200 + r.Intn(100)*8 - spadj := 0 - flushed := 0 - firstpc := 4 - flush := func(i int) { - for i-flushed >= 10 { - fmt.Printf("#line %d %q\n", line, file) - fmt.Printf("/*%#04x*/\tMOVQ $0x123456789, AX\n", firstpc+flushed) - flushed += 10 - } - for i-flushed >= 5 { - fmt.Printf("#line %d %q\n", line, file) - fmt.Printf("/*%#04x*/\tMOVL $0x1234567, AX\n", firstpc+flushed) - flushed += 5 - } - for i-flushed > 0 { - fmt.Printf("#line %d %q\n", line, file) - fmt.Printf("/*%#04x*/\tBYTE $0\n", firstpc+flushed) - flushed++ - } - } - for i := 0; i < size; i++ { - // Possible SP adjustment. - if r.Intn(100) == 0 { - flush(i) - fmt.Printf("#line %d %q\n", line, file) - if spadj <= -32 || spadj < 32 && r.Intn(2) == 0 { - spadj += 8 - fmt.Printf("/*%#04x*/\tPUSHQ AX\n", firstpc+i) - } else { - spadj -= 8 - fmt.Printf("/*%#04x*/\tPOPQ AX\n", firstpc+i) - } - i += 1 - flushed = i - } - - // Possible PCFile change. - if r.Intn(100) == 0 { - flush(i) - file = fmt.Sprintf("file%d.s", r.Intn(10)) - line = r.Intn(100) + 1 - } - - // Possible PCLine change. - if r.Intn(10) == 0 { - flush(i) - line = r.Intn(1000) + 1 - } - - // Possible PCData $1 change. - if r.Intn(100) == 0 { - flush(i) - fmt.Printf("/*%6s*/\tPCDATA $1, $%d\n", "", r.Intn(1000)) - } - - // Possible PCData $2 change. - if r.Intn(100) == 0 { - flush(i) - fmt.Printf("/*%6s*/\tPCDATA $2, $%d\n", "", r.Intn(1000)) - } - } - flush(size) - for spadj < 0 { - fmt.Printf("\tPUSHQ AX\n") - spadj += 8 - } - for spadj > 0 { - fmt.Printf("\tPOPQ AX\n") - spadj -= 8 - } - fmt.Printf("\tRET\n") - - fmt.Printf("\n") - fmt.Printf("GLOBL funcdata%d(SB), $16\n", f) - } - - fmt.Printf("\nTEXT start(SB),7,$0\n") - for f := 0; f < 3; f++ { - fmt.Printf("\tCALL func%d(SB)\n", f) - } - fmt.Printf("\tMOVQ $runtime·pclntab(SB), AX\n") - fmt.Printf("\n\tRET\n") -} diff --git a/src/cmd/link/testdata/hello.6 b/src/cmd/link/testdata/hello.6 deleted file mode 100644 index 890cbbd016..0000000000 Binary files a/src/cmd/link/testdata/hello.6 and /dev/null differ diff --git a/src/cmd/link/testdata/hello.s b/src/cmd/link/testdata/hello.s deleted file mode 100644 index 32ed675033..0000000000 --- a/src/cmd/link/testdata/hello.s +++ /dev/null @@ -1,15 +0,0 @@ -TEXT _rt0_go(SB),7,$0 - MOVL $1, DI - MOVL $hello<>(SB), SI - MOVL $12, DX - MOVL $0x2000004, AX - SYSCALL - MOVL $0, DI - MOVL $0x2000001, AX - SYSCALL - RET - -DATA hello<>+0(SB)/4, $"hell" -DATA hello<>+4(SB)/4, $"o wo" -DATA hello<>+8(SB)/4, $"rld\n" -GLOBL hello<>(SB), $12 diff --git a/src/cmd/link/testdata/layout.6 b/src/cmd/link/testdata/layout.6 deleted file mode 100644 index 748c836255..0000000000 Binary files a/src/cmd/link/testdata/layout.6 and /dev/null differ diff --git a/src/cmd/link/testdata/layout.s b/src/cmd/link/testdata/layout.s deleted file mode 100644 index c3e55ef49a..0000000000 --- a/src/cmd/link/testdata/layout.s +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2014 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 of section assignment in layout.go. -// Each symbol should end up in the section named by the symbol name prefix (up to the underscore). - -#include "textflag.h" - -TEXT text_start(SB),7,$0 - MOVQ $rodata_sym(SB), AX - MOVQ $noptrdata_sym(SB), AX - MOVQ $data_sym(SB), AX - MOVQ $bss_sym(SB), AX - MOVQ $noptrbss_sym(SB), AX - RET - -DATA rodata_sym(SB)/4, $1 -GLOBL rodata_sym(SB), RODATA, $4 - -DATA noptrdata_sym(SB)/4, $1 -GLOBL noptrdata_sym(SB), NOPTR, $4 - -DATA data_sym(SB)/4, $1 -GLOBL data_sym(SB), $4 - -GLOBL bss_sym(SB), $4 - -GLOBL noptrbss_sym(SB), NOPTR, $4 diff --git a/src/cmd/link/testdata/link.hello.darwin.amd64 b/src/cmd/link/testdata/link.hello.darwin.amd64 deleted file mode 100644 index 4c62eb19b4..0000000000 --- a/src/cmd/link/testdata/link.hello.darwin.amd64 +++ /dev/null @@ -1,55 +0,0 @@ -00000000 cf fa ed fe 07 00 00 01 03 00 00 00 02 00 00 00 |................| -00000010 04 00 00 00 d0 02 00 00 01 00 00 00 00 00 00 00 |................| -00000020 19 00 00 00 48 00 00 00 5f 5f 50 41 47 45 5a 45 |....H...__PAGEZE| -00000030 52 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |RO..............| -00000040 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| -* -00000060 00 00 00 00 00 00 00 00 19 00 00 00 38 01 00 00 |............8...| -00000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| -00000080 00 10 00 00 00 00 00 00 c0 10 00 00 00 00 00 00 |................| -00000090 00 00 00 00 00 00 00 00 c0 10 00 00 00 00 00 00 |................| -000000a0 07 00 00 00 05 00 00 00 03 00 00 00 00 00 00 00 |................| -000000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 |__text..........| -000000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| -000000d0 00 20 00 00 00 00 00 00 30 00 00 00 00 00 00 00 |. ......0.......| -000000e0 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| -000000f0 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| -00000100 5f 5f 72 6f 64 61 74 61 00 00 00 00 00 00 00 00 |__rodata........| -00000110 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| -00000120 30 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |0 ..............| -00000130 30 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |0...............| -* -00000150 5f 5f 66 75 6e 63 74 61 62 00 00 00 00 00 00 00 |__functab.......| -00000160 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| -00000170 30 20 00 00 00 00 00 00 90 00 00 00 00 00 00 00 |0 ..............| -00000180 30 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |0...............| -* -000001a0 19 00 00 00 98 00 00 00 5f 5f 44 41 54 41 00 00 |........__DATA..| -000001b0 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 |.........0......| -000001c0 0c 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......| -000001d0 0c 00 00 00 00 00 00 00 03 00 00 00 03 00 00 00 |................| -000001e0 01 00 00 00 00 00 00 00 5f 5f 64 61 74 61 00 00 |........__data..| -000001f0 00 00 00 00 00 00 00 00 5f 5f 44 41 54 41 00 00 |........__DATA..| -00000200 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 |.........0......| -00000210 0c 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......| -* -00000230 00 00 00 00 00 00 00 00 05 00 00 00 b8 00 00 00 |................| -00000240 04 00 00 00 2a 00 00 00 00 00 00 00 00 00 00 00 |....*...........| -* -000002c0 00 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......| -* -00001000 bf 01 00 00 00 8d 35 f5 0f 00 00 ba 0c 00 00 00 |......5.........| -00001010 b8 04 00 00 02 0f 05 31 ff b8 01 00 00 02 0f 05 |.......1........| -00001020 c3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| -00001030 fb ff ff ff 00 00 01 08 01 00 00 00 00 00 00 00 |................| -00001040 00 20 00 00 00 00 00 00 30 00 00 00 00 00 00 00 |. ......0.......| -00001050 30 20 00 00 00 00 00 00 80 00 00 00 00 00 00 00 |0 ..............| -00001060 00 20 00 00 00 00 00 00 58 00 00 00 00 00 00 80 |. ......X.......| -00001070 08 00 00 00 60 00 00 00 63 00 00 00 66 00 00 00 |....`...c...f...| -00001080 00 00 00 00 00 00 00 00 5f 72 74 30 5f 67 6f 00 |........_rt0_go.| -00001090 02 30 00 04 30 00 06 05 02 06 02 05 02 05 02 02 |.0..0...........| -000010a0 02 02 02 05 02 02 02 10 00 00 00 00 00 00 00 00 |................| -000010b0 02 00 00 00 88 00 00 00 68 65 6c 6c 6f 2e 73 00 |........hello.s.| -* -00002000 68 65 6c 6c 6f 20 77 6f 72 6c 64 0a |hello world.| -0000200c diff --git a/src/cmd/link/testdata/macho.amd64.exit9 b/src/cmd/link/testdata/macho.amd64.exit9 deleted file mode 100644 index 1adc814c33..0000000000 --- a/src/cmd/link/testdata/macho.amd64.exit9 +++ /dev/null @@ -1,24 +0,0 @@ -00000000 cf fa ed fe 07 00 00 01 03 00 00 00 02 00 00 00 |................| -00000010 03 00 00 00 98 01 00 00 01 00 00 00 00 00 00 00 |................| -00000020 19 00 00 00 48 00 00 00 5f 5f 50 41 47 45 5a 45 |....H...__PAGEZE| -00000030 52 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |RO..............| -00000040 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| -00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| -00000060 00 00 00 00 00 00 00 00 19 00 00 00 98 00 00 00 |................| -00000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| -00000080 00 10 00 00 00 00 00 00 0d 10 00 00 00 00 00 00 |................| -00000090 00 00 00 00 00 00 00 00 0d 10 00 00 00 00 00 00 |................| -000000a0 07 00 00 00 05 00 00 00 01 00 00 00 00 00 00 00 |................| -000000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 |__text..........| -000000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| -000000d0 00 20 00 00 00 00 00 00 0d 00 00 00 00 00 00 00 |. ..............| -000000e0 00 10 00 00 06 00 00 00 00 00 00 00 00 00 00 00 |................| -000000f0 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| -00000100 05 00 00 00 b8 00 00 00 04 00 00 00 2a 00 00 00 |............*...| -00000110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| -* -00000190 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |. ..............| -000001a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| -* -00001000 b8 01 00 00 02 bf 09 00 00 00 0f 05 f4 |.............| -0000100d diff --git a/src/cmd/link/testdata/macho.amd64.hello b/src/cmd/link/testdata/macho.amd64.hello deleted file mode 100644 index 45e70d0ac5..0000000000 --- a/src/cmd/link/testdata/macho.amd64.hello +++ /dev/null @@ -1,39 +0,0 @@ -00000000 cf fa ed fe 07 00 00 01 03 00 00 00 02 00 00 00 |................| -00000010 04 00 00 00 30 02 00 00 01 00 00 00 00 00 00 00 |....0...........| -00000020 19 00 00 00 48 00 00 00 5f 5f 50 41 47 45 5a 45 |....H...__PAGEZE| -00000030 52 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |RO..............| -00000040 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| -00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| -00000060 00 00 00 00 00 00 00 00 19 00 00 00 98 00 00 00 |................| -00000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| -00000080 00 10 00 00 00 00 00 00 23 10 00 00 00 00 00 00 |........#.......| -00000090 00 00 00 00 00 00 00 00 23 10 00 00 00 00 00 00 |........#.......| -000000a0 07 00 00 00 05 00 00 00 01 00 00 00 00 00 00 00 |................| -000000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 |__text..........| -000000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| -000000d0 00 20 00 00 00 00 00 00 23 00 00 00 00 00 00 00 |. ......#.......| -000000e0 00 10 00 00 06 00 00 00 00 00 00 00 00 00 00 00 |................| -000000f0 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| -00000100 19 00 00 00 98 00 00 00 5f 5f 44 41 54 41 00 00 |........__DATA..| -00000110 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 |.........0......| -00000120 0c 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......| -00000130 0c 00 00 00 00 00 00 00 03 00 00 00 03 00 00 00 |................| -00000140 01 00 00 00 00 00 00 00 5f 5f 64 61 74 61 00 00 |........__data..| -00000150 00 00 00 00 00 00 00 00 5f 5f 44 41 54 41 00 00 |........__DATA..| -00000160 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 |.........0......| -00000170 0c 00 00 00 00 00 00 00 00 20 00 00 06 00 00 00 |......... ......| -00000180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| -00000190 00 00 00 00 00 00 00 00 05 00 00 00 b8 00 00 00 |................| -000001a0 04 00 00 00 2a 00 00 00 00 00 00 00 00 00 00 00 |....*...........| -000001b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| -* -00000220 00 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......| -00000230 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| -* -00001000 b8 04 00 00 02 bf 01 00 00 00 be 00 30 00 00 ba |............0...| -00001010 0c 00 00 00 0f 05 b8 01 00 00 02 bf 09 00 00 00 |................| -00001020 0f 05 f4 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| -00001030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| -* -00002000 68 65 6c 6c 6f 20 77 6f 72 6c 64 0a |hello world.| -0000200c diff --git a/src/cmd/link/testdata/macho.amd64.helloro b/src/cmd/link/testdata/macho.amd64.helloro deleted file mode 100644 index 4b70fbd0fa..0000000000 --- a/src/cmd/link/testdata/macho.amd64.helloro +++ /dev/null @@ -1,34 +0,0 @@ -00000000 cf fa ed fe 07 00 00 01 03 00 00 00 02 00 00 00 |................| -00000010 03 00 00 00 e8 01 00 00 01 00 00 00 00 00 00 00 |................| -00000020 19 00 00 00 48 00 00 00 5f 5f 50 41 47 45 5a 45 |....H...__PAGEZE| -00000030 52 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |RO..............| -00000040 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| -00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| -00000060 00 00 00 00 00 00 00 00 19 00 00 00 e8 00 00 00 |................| -00000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| -00000080 00 10 00 00 00 00 00 00 0c 20 00 00 00 00 00 00 |......... ......| -00000090 00 00 00 00 00 00 00 00 0c 20 00 00 00 00 00 00 |......... ......| -000000a0 07 00 00 00 05 00 00 00 02 00 00 00 00 00 00 00 |................| -000000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 |__text..........| -000000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| -000000d0 00 20 00 00 00 00 00 00 23 00 00 00 00 00 00 00 |. ......#.......| -000000e0 00 10 00 00 06 00 00 00 00 00 00 00 00 00 00 00 |................| -000000f0 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| -00000100 5f 5f 72 6f 64 61 74 61 00 00 00 00 00 00 00 00 |__rodata........| -00000110 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| -00000120 00 30 00 00 00 00 00 00 0c 00 00 00 00 00 00 00 |.0..............| -00000130 00 20 00 00 06 00 00 00 00 00 00 00 00 00 00 00 |. ..............| -00000140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| -00000150 05 00 00 00 b8 00 00 00 04 00 00 00 2a 00 00 00 |............*...| -00000160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| -* -000001e0 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |. ..............| -000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| -* -00001000 b8 04 00 00 02 bf 01 00 00 00 be 00 30 00 00 ba |............0...| -00001010 0c 00 00 00 0f 05 b8 01 00 00 02 bf 00 00 00 00 |................| -00001020 0f 05 f4 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| -00001030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| -* -00002000 68 65 6c 6c 6f 20 77 6f 72 6c 64 0a |hello world.| -0000200c diff --git a/src/cmd/link/testdata/pclntab.6 b/src/cmd/link/testdata/pclntab.6 deleted file mode 100644 index 8156698265..0000000000 Binary files a/src/cmd/link/testdata/pclntab.6 and /dev/null differ diff --git a/src/cmd/link/testdata/pclntab.s b/src/cmd/link/testdata/pclntab.s deleted file mode 100644 index 12dac70b0c..0000000000 --- a/src/cmd/link/testdata/pclntab.s +++ /dev/null @@ -1,1751 +0,0 @@ -// generated by genpcln.go; do not edit - -#line 1 "input" -TEXT func0(SB),7,$48-592 - FUNCDATA $1, funcdata0(SB) -#line 1 "input" -#line 1 "input" -/*0x0004*/ MOVL $0x1234567, AX -#line 1 "input" -/*0x0009*/ BYTE $0 -/* */ PCDATA $1, $110 -#line 1 "input" -/*0x000a*/ BYTE $0 -#line 1 "input" -/*0x000b*/ BYTE $0 -/* */ PCDATA $1, $422 -#line 1 "input" -/*0x000c*/ BYTE $0 -#line 1 "input" -/*0x000d*/ BYTE $0 -#line 1 "input" -/*0x000e*/ POPQ AX -#line 1 "input" -/*0x000f*/ MOVL $0x1234567, AX -#line 1 "input" -/*0x0014*/ BYTE $0 -#line 1 "input" -/*0x0015*/ BYTE $0 -#line 1 "input" -/*0x0016*/ BYTE $0 -#line 542 "input" -/*0x0017*/ BYTE $0 -#line 960 "input" -/*0x0018*/ MOVL $0x1234567, AX -#line 960 "input" -/*0x001d*/ BYTE $0 -#line 960 "input" -/*0x001e*/ PUSHQ AX -#line 960 "input" -/*0x001f*/ BYTE $0 -#line 960 "input" -/*0x0020*/ BYTE $0 -#line 594 "input" -/*0x0021*/ BYTE $0 -#line 671 "input" -/*0x0022*/ MOVL $0x1234567, AX -#line 671 "input" -/*0x0027*/ BYTE $0 -#line 671 "input" -/*0x0028*/ BYTE $0 -#line 230 "input" -/*0x0029*/ BYTE $0 -#line 230 "input" -/*0x002a*/ BYTE $0 -#line 413 "input" -/*0x002b*/ BYTE $0 -#line 413 "input" -/*0x002c*/ BYTE $0 -#line 413 "input" -/*0x002d*/ BYTE $0 -#line 729 "input" -/*0x002e*/ BYTE $0 -#line 729 "input" -/*0x002f*/ BYTE $0 -#line 729 "input" -/*0x0030*/ BYTE $0 -#line 948 "input" -/*0x0031*/ BYTE $0 -#line 11 "input" -/*0x0032*/ MOVQ $0x123456789, AX -#line 11 "input" -/*0x003c*/ MOVL $0x1234567, AX -#line 11 "input" -/*0x0041*/ BYTE $0 -#line 11 "input" -/*0x0042*/ BYTE $0 -#line 11 "input" -/*0x0043*/ POPQ AX -/* */ PCDATA $2, $342 -#line 11 "input" -/*0x0044*/ MOVQ $0x123456789, AX -#line 11 "input" -/*0x004e*/ MOVQ $0x123456789, AX -#line 11 "input" -/*0x0058*/ MOVL $0x1234567, AX -#line 11 "input" -/*0x005d*/ BYTE $0 -#line 11 "input" -/*0x005e*/ BYTE $0 -#line 70 "input" -/*0x005f*/ BYTE $0 -#line 70 "input" -/*0x0060*/ BYTE $0 -#line 70 "input" -/*0x0061*/ BYTE $0 -#line 18 "input" -/*0x0062*/ MOVQ $0x123456789, AX -#line 18 "input" -/*0x006c*/ MOVL $0x1234567, AX -#line 18 "input" -/*0x0071*/ BYTE $0 -#line 814 "input" -/*0x0072*/ BYTE $0 -#line 814 "input" -/*0x0073*/ PUSHQ AX -#line 763 "input" -/*0x0074*/ BYTE $0 -#line 763 "input" -/*0x0075*/ BYTE $0 -#line 763 "input" -/*0x0076*/ BYTE $0 -#line 530 "input" -/*0x0077*/ BYTE $0 -#line 530 "input" -/*0x0078*/ BYTE $0 -#line 530 "input" -/*0x0079*/ BYTE $0 -#line 530 "input" -/*0x007a*/ BYTE $0 -#line 985 "input" -/*0x007b*/ BYTE $0 -#line 985 "input" -/*0x007c*/ BYTE $0 -#line 985 "input" -/*0x007d*/ BYTE $0 -#line 985 "input" -/*0x007e*/ PUSHQ AX -#line 985 "input" -/*0x007f*/ MOVL $0x1234567, AX -#line 958 "input" -/*0x0084*/ BYTE $0 -#line 368 "input" -/*0x0085*/ MOVQ $0x123456789, AX -#line 368 "input" -/*0x008f*/ MOVL $0x1234567, AX -#line 368 "input" -/*0x0094*/ BYTE $0 -#line 368 "input" -/*0x0095*/ BYTE $0 -#line 368 "input" -/*0x0096*/ BYTE $0 -#line 368 "input" -/*0x0097*/ BYTE $0 -#line 75 "file0.s" -/*0x0098*/ BYTE $0 -#line 75 "file0.s" -/*0x0099*/ BYTE $0 -#line 75 "file0.s" -/*0x009a*/ BYTE $0 -#line 75 "file0.s" -/*0x009b*/ BYTE $0 -#line 588 "file0.s" -/*0x009c*/ MOVQ $0x123456789, AX -#line 187 "file0.s" -/*0x00a6*/ MOVQ $0x123456789, AX -#line 187 "file0.s" -/*0x00b0*/ BYTE $0 -#line 202 "file0.s" -/*0x00b1*/ MOVL $0x1234567, AX -#line 202 "file0.s" -/*0x00b6*/ BYTE $0 -#line 887 "file0.s" -/*0x00b7*/ MOVL $0x1234567, AX -#line 887 "file0.s" -/*0x00bc*/ BYTE $0 -#line 887 "file0.s" -/*0x00bd*/ BYTE $0 -#line 480 "file0.s" -/*0x00be*/ MOVL $0x1234567, AX -#line 480 "file0.s" -/*0x00c3*/ BYTE $0 -#line 40 "file8.s" -/*0x00c4*/ BYTE $0 -#line 17 "file0.s" -/*0x00c5*/ MOVQ $0x123456789, AX -#line 17 "file0.s" -/*0x00cf*/ BYTE $0 -#line 17 "file0.s" -/*0x00d0*/ BYTE $0 -#line 17 "file0.s" -/*0x00d1*/ BYTE $0 -#line 17 "file0.s" -/*0x00d2*/ BYTE $0 -#line 436 "file0.s" -/*0x00d3*/ MOVL $0x1234567, AX -#line 436 "file0.s" -/*0x00d8*/ BYTE $0 -#line 436 "file0.s" -/*0x00d9*/ BYTE $0 -#line 346 "file0.s" -/*0x00da*/ BYTE $0 -#line 346 "file0.s" -/*0x00db*/ BYTE $0 -#line 346 "file0.s" -/*0x00dc*/ BYTE $0 -#line 812 "file0.s" -/*0x00dd*/ BYTE $0 -#line 812 "file0.s" -/*0x00de*/ BYTE $0 -#line 812 "file0.s" -/*0x00df*/ BYTE $0 -#line 812 "file0.s" -/*0x00e0*/ BYTE $0 -#line 94 "file1.s" -/*0x00e1*/ BYTE $0 -#line 94 "file1.s" -/*0x00e2*/ BYTE $0 -#line 165 "file1.s" -/*0x00e3*/ MOVL $0x1234567, AX -#line 165 "file1.s" -/*0x00e8*/ BYTE $0 -#line 456 "file1.s" -/*0x00e9*/ BYTE $0 -#line 810 "file1.s" -/*0x00ea*/ BYTE $0 -#line 722 "file1.s" -/*0x00eb*/ BYTE $0 -#line 722 "file1.s" -/*0x00ec*/ BYTE $0 -#line 722 "file1.s" -/*0x00ed*/ BYTE $0 -#line 722 "file1.s" -/*0x00ee*/ BYTE $0 -#line 722 "file1.s" -/*0x00ef*/ PUSHQ AX -#line 722 "file1.s" -/*0x00f0*/ BYTE $0 -#line 722 "file1.s" -/*0x00f1*/ BYTE $0 -#line 722 "file1.s" -/*0x00f2*/ BYTE $0 -#line 722 "file1.s" -/*0x00f3*/ BYTE $0 -/* */ PCDATA $2, $246 -#line 722 "file1.s" -/*0x00f4*/ MOVQ $0x123456789, AX -#line 722 "file1.s" -/*0x00fe*/ MOVQ $0x123456789, AX -#line 722 "file1.s" -/*0x0108*/ MOVL $0x1234567, AX -#line 722 "file1.s" -/*0x010d*/ BYTE $0 -#line 722 "file1.s" -/*0x010e*/ BYTE $0 -#line 497 "file1.s" -/*0x010f*/ MOVQ $0x123456789, AX -#line 497 "file1.s" -/*0x0119*/ MOVQ $0x123456789, AX -#line 497 "file1.s" -/*0x0123*/ MOVQ $0x123456789, AX -#line 497 "file1.s" -/*0x012d*/ MOVL $0x1234567, AX -#line 497 "file1.s" -/*0x0132*/ BYTE $0 -#line 686 "file1.s" -/*0x0133*/ BYTE $0 -#line 686 "file1.s" -/*0x0134*/ BYTE $0 -#line 248 "file1.s" -/*0x0135*/ BYTE $0 -#line 248 "file1.s" -/*0x0136*/ BYTE $0 -#line 248 "file1.s" -/*0x0137*/ BYTE $0 -#line 248 "file1.s" -/*0x0138*/ BYTE $0 -#line 307 "file1.s" -/*0x0139*/ BYTE $0 -#line 220 "file1.s" -/*0x013a*/ MOVL $0x1234567, AX -#line 220 "file1.s" -/*0x013f*/ BYTE $0 -#line 220 "file1.s" -/*0x0140*/ BYTE $0 -#line 467 "file1.s" -/*0x0141*/ MOVQ $0x123456789, AX -#line 467 "file1.s" -/*0x014b*/ BYTE $0 -#line 467 "file1.s" -/*0x014c*/ BYTE $0 -#line 467 "file1.s" -/*0x014d*/ BYTE $0 -#line 467 "file1.s" -/*0x014e*/ BYTE $0 -#line 786 "file1.s" -/*0x014f*/ MOVL $0x1234567, AX -#line 251 "file1.s" -/*0x0154*/ BYTE $0 -/* */ PCDATA $2, $64 -#line 251 "file1.s" -/*0x0155*/ BYTE $0 -#line 251 "file1.s" -/*0x0156*/ BYTE $0 -#line 251 "file1.s" -/*0x0157*/ BYTE $0 -#line 618 "file1.s" -/*0x0158*/ MOVQ $0x123456789, AX -/* */ PCDATA $1, $686 -#line 618 "file1.s" -/*0x0162*/ BYTE $0 -#line 618 "file1.s" -/*0x0163*/ BYTE $0 -#line 618 "file1.s" -/*0x0164*/ BYTE $0 -#line 618 "file1.s" -/*0x0165*/ PUSHQ AX -/* */ PCDATA $2, $915 -#line 618 "file1.s" -/*0x0166*/ BYTE $0 -#line 618 "file1.s" -/*0x0167*/ BYTE $0 -#line 618 "file1.s" -/*0x0168*/ BYTE $0 -#line 618 "file1.s" -/*0x0169*/ BYTE $0 -#line 230 "file1.s" -/*0x016a*/ BYTE $0 -#line 823 "file1.s" -/*0x016b*/ BYTE $0 -#line 145 "file1.s" -/*0x016c*/ MOVQ $0x123456789, AX -#line 145 "file1.s" -/*0x0176*/ BYTE $0 -#line 145 "file1.s" -/*0x0177*/ BYTE $0 -#line 675 "file1.s" -/*0x0178*/ BYTE $0 -#line 62 "file9.s" -/*0x0179*/ BYTE $0 -/* */ PCDATA $2, $768 -#line 62 "file9.s" -/*0x017a*/ BYTE $0 -#line 62 "file9.s" -/*0x017b*/ BYTE $0 -#line 29 "file9.s" -/*0x017c*/ BYTE $0 -#line 29 "file9.s" -/*0x017d*/ BYTE $0 -#line 29 "file9.s" -/*0x017e*/ BYTE $0 -#line 29 "file9.s" -/*0x017f*/ BYTE $0 -#line 65 "file4.s" -/*0x0180*/ BYTE $0 -#line 77 "file3.s" -/*0x0181*/ MOVL $0x1234567, AX -#line 77 "file3.s" -/*0x0186*/ BYTE $0 -#line 77 "file3.s" -/*0x0187*/ BYTE $0 -#line 77 "file3.s" -/*0x0188*/ BYTE $0 -#line 307 "file3.s" -/*0x0189*/ MOVQ $0x123456789, AX -#line 307 "file3.s" -/*0x0193*/ BYTE $0 -#line 654 "file3.s" -/*0x0194*/ BYTE $0 -#line 654 "file3.s" -/*0x0195*/ BYTE $0 -#line 115 "file3.s" -/*0x0196*/ MOVL $0x1234567, AX -#line 115 "file3.s" -/*0x019b*/ BYTE $0 -#line 115 "file3.s" -/*0x019c*/ BYTE $0 -#line 115 "file3.s" -/*0x019d*/ BYTE $0 -#line 115 "file3.s" -/*0x019e*/ BYTE $0 -#line 154 "file3.s" -/*0x019f*/ MOVQ $0x123456789, AX -#line 166 "file3.s" -/*0x01a9*/ BYTE $0 -#line 166 "file3.s" -/*0x01aa*/ BYTE $0 -#line 166 "file3.s" -/*0x01ab*/ BYTE $0 -/* */ PCDATA $1, $523 -#line 166 "file3.s" -/*0x01ac*/ MOVL $0x1234567, AX -#line 166 "file3.s" -/*0x01b1*/ BYTE $0 -#line 779 "file3.s" -/*0x01b2*/ BYTE $0 -#line 779 "file3.s" -/*0x01b3*/ BYTE $0 -#line 515 "file3.s" -/*0x01b4*/ BYTE $0 -#line 515 "file3.s" -/*0x01b5*/ BYTE $0 -#line 369 "file3.s" -/*0x01b6*/ MOVL $0x1234567, AX -#line 369 "file3.s" -/*0x01bb*/ BYTE $0 -#line 369 "file3.s" -/*0x01bc*/ BYTE $0 -#line 369 "file3.s" -/*0x01bd*/ BYTE $0 -#line 680 "file3.s" -/*0x01be*/ BYTE $0 -#line 680 "file3.s" -/*0x01bf*/ BYTE $0 -#line 680 "file3.s" -/*0x01c0*/ BYTE $0 -#line 680 "file3.s" -/*0x01c1*/ BYTE $0 -#line 131 "file3.s" -/*0x01c2*/ MOVQ $0x123456789, AX -#line 131 "file3.s" -/*0x01cc*/ BYTE $0 -#line 131 "file3.s" -/*0x01cd*/ BYTE $0 -#line 131 "file3.s" -/*0x01ce*/ BYTE $0 -#line 131 "file3.s" -/*0x01cf*/ BYTE $0 -#line 848 "file3.s" -/*0x01d0*/ BYTE $0 -#line 848 "file3.s" -/*0x01d1*/ BYTE $0 -#line 848 "file3.s" -/*0x01d2*/ POPQ AX -#line 848 "file3.s" -/*0x01d3*/ BYTE $0 -#line 848 "file3.s" -/*0x01d4*/ BYTE $0 -#line 848 "file3.s" -/*0x01d5*/ BYTE $0 -/* */ PCDATA $1, $86 -#line 848 "file3.s" -/*0x01d6*/ MOVL $0x1234567, AX -#line 438 "file3.s" -/*0x01db*/ MOVQ $0x123456789, AX -#line 438 "file3.s" -/*0x01e5*/ MOVL $0x1234567, AX -#line 5 "file3.s" -/*0x01ea*/ BYTE $0 -#line 5 "file3.s" -/*0x01eb*/ BYTE $0 -#line 531 "file3.s" -/*0x01ec*/ MOVQ $0x123456789, AX -#line 531 "file3.s" -/*0x01f6*/ MOVQ $0x123456789, AX -#line 531 "file3.s" -/*0x0200*/ MOVQ $0x123456789, AX -#line 531 "file3.s" -/*0x020a*/ MOVL $0x1234567, AX -#line 863 "file3.s" -/*0x020f*/ BYTE $0 -#line 733 "file3.s" -/*0x0210*/ MOVQ $0x123456789, AX -#line 166 "file3.s" -/*0x021a*/ MOVQ $0x123456789, AX -#line 166 "file3.s" -/*0x0224*/ BYTE $0 -#line 166 "file3.s" -/*0x0225*/ BYTE $0 -#line 166 "file3.s" -/*0x0226*/ BYTE $0 -#line 166 "file3.s" -/*0x0227*/ BYTE $0 -#line 54 "file3.s" -/*0x0228*/ MOVQ $0x123456789, AX -#line 54 "file3.s" -/*0x0232*/ BYTE $0 -#line 54 "file3.s" -/*0x0233*/ BYTE $0 -#line 54 "file3.s" -/*0x0234*/ BYTE $0 -#line 20 "file4.s" -/*0x0235*/ BYTE $0 -#line 20 "file4.s" -/*0x0236*/ BYTE $0 -#line 546 "file4.s" -/*0x0237*/ BYTE $0 -#line 546 "file4.s" -/*0x0238*/ BYTE $0 -#line 74 "file4.s" -/*0x0239*/ BYTE $0 -#line 31 "file4.s" -/*0x023a*/ BYTE $0 -#line 31 "file4.s" -/*0x023b*/ BYTE $0 -#line 31 "file4.s" -/*0x023c*/ BYTE $0 -#line 31 "file4.s" -/*0x023d*/ BYTE $0 -#line 834 "file4.s" -/*0x023e*/ BYTE $0 -#line 834 "file4.s" -/*0x023f*/ BYTE $0 -#line 519 "file4.s" -/*0x0240*/ MOVL $0x1234567, AX -#line 342 "file4.s" -/*0x0245*/ BYTE $0 -#line 342 "file4.s" -/*0x0246*/ BYTE $0 -#line 342 "file4.s" -/*0x0247*/ BYTE $0 -#line 458 "file4.s" -/*0x0248*/ BYTE $0 -#line 458 "file4.s" -/*0x0249*/ BYTE $0 -#line 458 "file4.s" -/*0x024a*/ BYTE $0 -#line 458 "file4.s" -/*0x024b*/ BYTE $0 -#line 13 "file9.s" -/*0x024c*/ BYTE $0 -#line 13 "file9.s" -/*0x024d*/ BYTE $0 -#line 13 "file9.s" -/*0x024e*/ BYTE $0 -#line 365 "file9.s" -/*0x024f*/ BYTE $0 -#line 749 "file9.s" -/*0x0250*/ MOVL $0x1234567, AX -#line 749 "file9.s" -/*0x0255*/ BYTE $0 -#line 749 "file9.s" -/*0x0256*/ BYTE $0 -#line 41 "file0.s" -/*0x0257*/ MOVL $0x1234567, AX -#line 41 "file0.s" -/*0x025c*/ BYTE $0 -#line 41 "file0.s" -/*0x025d*/ BYTE $0 -#line 41 "file0.s" -/*0x025e*/ BYTE $0 -#line 869 "file0.s" -/*0x025f*/ BYTE $0 -#line 881 "file0.s" -/*0x0260*/ MOVQ $0x123456789, AX -#line 881 "file0.s" -/*0x026a*/ MOVQ $0x123456789, AX - POPQ AX - POPQ AX - RET - -GLOBL funcdata0(SB), $16 -#line 1 "input" -TEXT func1(SB),7,$40-648 - FUNCDATA $1, funcdata1(SB) -#line 1 "input" -#line 1 "input" -/*0x0004*/ BYTE $0 -#line 12 "file4.s" -/*0x0005*/ MOVL $0x1234567, AX -#line 12 "file4.s" -/*0x000a*/ BYTE $0 -#line 12 "file4.s" -/*0x000b*/ BYTE $0 -#line 12 "file4.s" -/*0x000c*/ BYTE $0 -/* */ PCDATA $2, $705 -#line 12 "file4.s" -/*0x000d*/ MOVQ $0x123456789, AX -#line 12 "file4.s" -/*0x0017*/ BYTE $0 -#line 633 "file4.s" -/*0x0018*/ MOVQ $0x123456789, AX -#line 633 "file4.s" -/*0x0022*/ MOVL $0x1234567, AX -#line 633 "file4.s" -/*0x0027*/ POPQ AX -#line 633 "file4.s" -/*0x0028*/ BYTE $0 -#line 633 "file4.s" -/*0x0029*/ BYTE $0 -#line 633 "file4.s" -/*0x002a*/ BYTE $0 -#line 633 "file4.s" -/*0x002b*/ PUSHQ AX -#line 633 "file4.s" -/*0x002c*/ MOVL $0x1234567, AX -#line 997 "file4.s" -/*0x0031*/ BYTE $0 -#line 997 "file4.s" -/*0x0032*/ BYTE $0 -#line 997 "file4.s" -/*0x0033*/ BYTE $0 -#line 997 "file4.s" -/*0x0034*/ BYTE $0 -#line 997 "file4.s" -/*0x0035*/ POPQ AX -#line 997 "file4.s" -/*0x0036*/ BYTE $0 -#line 997 "file4.s" -/*0x0037*/ BYTE $0 -#line 1 "file4.s" -/*0x0038*/ MOVQ $0x123456789, AX -#line 1 "file4.s" -/*0x0042*/ MOVQ $0x123456789, AX -#line 1 "file4.s" -/*0x004c*/ MOVQ $0x123456789, AX -#line 1 "file4.s" -/*0x0056*/ MOVQ $0x123456789, AX -#line 1 "file4.s" -/*0x0060*/ BYTE $0 -#line 922 "file4.s" -/*0x0061*/ BYTE $0 -#line 375 "file4.s" -/*0x0062*/ MOVL $0x1234567, AX -/* */ PCDATA $1, $51 -#line 31 "file4.s" -/*0x0067*/ MOVQ $0x123456789, AX -#line 31 "file4.s" -/*0x0071*/ BYTE $0 -#line 620 "file4.s" -/*0x0072*/ MOVL $0x1234567, AX -#line 620 "file4.s" -/*0x0077*/ BYTE $0 -#line 695 "file4.s" -/*0x0078*/ MOVL $0x1234567, AX -#line 799 "file4.s" -/*0x007d*/ MOVL $0x1234567, AX -#line 41 "file4.s" -/*0x0082*/ BYTE $0 -#line 795 "file4.s" -/*0x0083*/ MOVL $0x1234567, AX -#line 908 "file4.s" -/*0x0088*/ BYTE $0 -#line 905 "file4.s" -/*0x0089*/ BYTE $0 -#line 905 "file4.s" -/*0x008a*/ BYTE $0 -#line 905 "file4.s" -/*0x008b*/ BYTE $0 -#line 905 "file4.s" -/*0x008c*/ BYTE $0 -#line 861 "file4.s" -/*0x008d*/ MOVL $0x1234567, AX -#line 861 "file4.s" -/*0x0092*/ BYTE $0 -#line 861 "file4.s" -/*0x0093*/ BYTE $0 -#line 861 "file4.s" -/*0x0094*/ BYTE $0 -#line 861 "file4.s" -/*0x0095*/ BYTE $0 -/* */ PCDATA $1, $192 -#line 861 "file4.s" -/*0x0096*/ MOVQ $0x123456789, AX -/* */ PCDATA $1, $60 -#line 861 "file4.s" -/*0x00a0*/ MOVL $0x1234567, AX -#line 861 "file4.s" -/*0x00a5*/ BYTE $0 -#line 861 "file4.s" -/*0x00a6*/ BYTE $0 -#line 56 "file6.s" -/*0x00a7*/ MOVQ $0x123456789, AX -#line 56 "file6.s" -/*0x00b1*/ BYTE $0 -#line 56 "file6.s" -/*0x00b2*/ BYTE $0 -#line 56 "file6.s" -/*0x00b3*/ BYTE $0 -#line 204 "file6.s" -/*0x00b4*/ BYTE $0 -#line 204 "file6.s" -/*0x00b5*/ BYTE $0 -#line 204 "file6.s" -/*0x00b6*/ BYTE $0 -#line 204 "file6.s" -/*0x00b7*/ BYTE $0 -#line 979 "file6.s" -/*0x00b8*/ MOVL $0x1234567, AX -#line 979 "file6.s" -/*0x00bd*/ BYTE $0 -#line 979 "file6.s" -/*0x00be*/ BYTE $0 -#line 979 "file6.s" -/*0x00bf*/ BYTE $0 -#line 58 "file6.s" -/*0x00c0*/ MOVL $0x1234567, AX -#line 238 "file6.s" -/*0x00c5*/ MOVL $0x1234567, AX -#line 238 "file6.s" -/*0x00ca*/ BYTE $0 -#line 238 "file6.s" -/*0x00cb*/ BYTE $0 -#line 699 "file2.s" -/*0x00cc*/ MOVQ $0x123456789, AX -#line 699 "file2.s" -/*0x00d6*/ BYTE $0 -#line 699 "file2.s" -/*0x00d7*/ BYTE $0 -#line 514 "file2.s" -/*0x00d8*/ MOVL $0x1234567, AX -#line 514 "file2.s" -/*0x00dd*/ BYTE $0 -#line 514 "file2.s" -/*0x00de*/ BYTE $0 -#line 108 "file2.s" -/*0x00df*/ MOVQ $0x123456789, AX -#line 108 "file2.s" -/*0x00e9*/ MOVQ $0x123456789, AX -#line 108 "file2.s" -/*0x00f3*/ MOVL $0x1234567, AX -#line 108 "file2.s" -/*0x00f8*/ BYTE $0 -#line 108 "file2.s" -/*0x00f9*/ BYTE $0 -#line 918 "file2.s" -/*0x00fa*/ BYTE $0 -#line 918 "file2.s" -/*0x00fb*/ BYTE $0 -#line 785 "file2.s" -/*0x00fc*/ BYTE $0 -#line 3 "file5.s" -/*0x00fd*/ BYTE $0 -#line 918 "file5.s" -/*0x00fe*/ BYTE $0 -#line 918 "file5.s" -/*0x00ff*/ BYTE $0 -#line 670 "file5.s" -/*0x0100*/ MOVL $0x1234567, AX -#line 670 "file5.s" -/*0x0105*/ BYTE $0 -#line 720 "file5.s" -/*0x0106*/ BYTE $0 -#line 384 "file5.s" -/*0x0107*/ MOVQ $0x123456789, AX -#line 384 "file5.s" -/*0x0111*/ MOVQ $0x123456789, AX -#line 384 "file5.s" -/*0x011b*/ MOVQ $0x123456789, AX -#line 384 "file5.s" -/*0x0125*/ BYTE $0 -#line 384 "file5.s" -/*0x0126*/ BYTE $0 -#line 263 "file5.s" -/*0x0127*/ BYTE $0 -#line 750 "file5.s" -/*0x0128*/ MOVL $0x1234567, AX -#line 750 "file5.s" -/*0x012d*/ BYTE $0 -#line 679 "file5.s" -/*0x012e*/ MOVL $0x1234567, AX -#line 679 "file5.s" -/*0x0133*/ BYTE $0 -#line 679 "file5.s" -/*0x0134*/ BYTE $0 -#line 679 "file5.s" -/*0x0135*/ BYTE $0 -#line 679 "file5.s" -/*0x0136*/ POPQ AX -#line 171 "file5.s" -/*0x0137*/ MOVL $0x1234567, AX -#line 171 "file5.s" -/*0x013c*/ BYTE $0 -#line 34 "file2.s" -/*0x013d*/ BYTE $0 -#line 34 "file2.s" -/*0x013e*/ BYTE $0 -#line 34 "file2.s" -/*0x013f*/ BYTE $0 -#line 732 "file2.s" -/*0x0140*/ BYTE $0 -#line 732 "file2.s" -/*0x0141*/ PUSHQ AX -#line 485 "file2.s" -/*0x0142*/ BYTE $0 -#line 485 "file2.s" -/*0x0143*/ BYTE $0 -#line 485 "file2.s" -/*0x0144*/ BYTE $0 -#line 222 "file2.s" -/*0x0145*/ BYTE $0 -#line 222 "file2.s" -/*0x0146*/ BYTE $0 -/* */ PCDATA $1, $462 -#line 222 "file2.s" -/*0x0147*/ MOVQ $0x123456789, AX -#line 222 "file2.s" -/*0x0151*/ MOVL $0x1234567, AX -#line 222 "file2.s" -/*0x0156*/ BYTE $0 -#line 677 "file2.s" -/*0x0157*/ BYTE $0 -#line 117 "file2.s" -/*0x0158*/ MOVL $0x1234567, AX -#line 117 "file2.s" -/*0x015d*/ BYTE $0 -#line 117 "file2.s" -/*0x015e*/ BYTE $0 -#line 361 "file2.s" -/*0x015f*/ MOVL $0x1234567, AX -#line 590 "file2.s" -/*0x0164*/ BYTE $0 -#line 590 "file2.s" -/*0x0165*/ BYTE $0 -#line 58 "file2.s" -/*0x0166*/ MOVL $0x1234567, AX -#line 58 "file2.s" -/*0x016b*/ BYTE $0 -#line 58 "file2.s" -/*0x016c*/ BYTE $0 -#line 58 "file2.s" -/*0x016d*/ BYTE $0 -#line 58 "file2.s" -/*0x016e*/ BYTE $0 -#line 983 "file2.s" -/*0x016f*/ BYTE $0 -#line 983 "file2.s" -/*0x0170*/ BYTE $0 -#line 983 "file2.s" -/*0x0171*/ BYTE $0 -#line 983 "file2.s" -/*0x0172*/ BYTE $0 -#line 727 "file2.s" -/*0x0173*/ MOVL $0x1234567, AX -#line 450 "file2.s" -/*0x0178*/ BYTE $0 -#line 450 "file2.s" -/*0x0179*/ BYTE $0 -#line 450 "file2.s" -/*0x017a*/ BYTE $0 -#line 450 "file2.s" -/*0x017b*/ BYTE $0 -#line 334 "file2.s" -/*0x017c*/ BYTE $0 -#line 334 "file2.s" -/*0x017d*/ BYTE $0 -#line 334 "file2.s" -/*0x017e*/ BYTE $0 -#line 334 "file2.s" -/*0x017f*/ BYTE $0 -#line 465 "file2.s" -/*0x0180*/ MOVL $0x1234567, AX -/* */ PCDATA $1, $518 -#line 465 "file2.s" -/*0x0185*/ MOVL $0x1234567, AX -#line 465 "file2.s" -/*0x018a*/ BYTE $0 -#line 465 "file2.s" -/*0x018b*/ BYTE $0 -#line 465 "file2.s" -/*0x018c*/ BYTE $0 -#line 465 "file2.s" -/*0x018d*/ BYTE $0 -#line 682 "file2.s" -/*0x018e*/ MOVL $0x1234567, AX -#line 682 "file2.s" -/*0x0193*/ BYTE $0 -#line 682 "file2.s" -/*0x0194*/ BYTE $0 -#line 846 "file2.s" -/*0x0195*/ MOVQ $0x123456789, AX -#line 846 "file2.s" -/*0x019f*/ BYTE $0 -#line 846 "file2.s" -/*0x01a0*/ BYTE $0 -#line 846 "file2.s" -/*0x01a1*/ POPQ AX -#line 846 "file2.s" -/*0x01a2*/ BYTE $0 -#line 846 "file2.s" -/*0x01a3*/ BYTE $0 -#line 49 "file2.s" -/*0x01a4*/ BYTE $0 -#line 49 "file2.s" -/*0x01a5*/ BYTE $0 -#line 49 "file2.s" -/*0x01a6*/ BYTE $0 -#line 726 "file2.s" -/*0x01a7*/ MOVQ $0x123456789, AX -#line 726 "file2.s" -/*0x01b1*/ MOVL $0x1234567, AX -#line 726 "file2.s" -/*0x01b6*/ BYTE $0 -#line 726 "file2.s" -/*0x01b7*/ BYTE $0 -#line 726 "file2.s" -/*0x01b8*/ BYTE $0 -#line 726 "file2.s" -/*0x01b9*/ PUSHQ AX -#line 726 "file2.s" -/*0x01ba*/ BYTE $0 -#line 726 "file2.s" -/*0x01bb*/ BYTE $0 -#line 726 "file2.s" -/*0x01bc*/ BYTE $0 -#line 726 "file2.s" -/*0x01bd*/ BYTE $0 -#line 13 "file1.s" -/*0x01be*/ BYTE $0 -#line 13 "file1.s" -/*0x01bf*/ BYTE $0 -#line 13 "file1.s" -/*0x01c0*/ BYTE $0 -#line 827 "file1.s" -/*0x01c1*/ MOVL $0x1234567, AX -#line 827 "file1.s" -/*0x01c6*/ BYTE $0 -#line 827 "file1.s" -/*0x01c7*/ BYTE $0 -#line 827 "file1.s" -/*0x01c8*/ BYTE $0 -#line 827 "file1.s" -/*0x01c9*/ BYTE $0 -#line 783 "file1.s" -/*0x01ca*/ BYTE $0 -#line 783 "file1.s" -/*0x01cb*/ BYTE $0 -#line 783 "file1.s" -/*0x01cc*/ BYTE $0 -#line 783 "file1.s" -/*0x01cd*/ BYTE $0 -#line 367 "file1.s" -/*0x01ce*/ MOVQ $0x123456789, AX -#line 367 "file1.s" -/*0x01d8*/ MOVL $0x1234567, AX -#line 367 "file1.s" -/*0x01dd*/ BYTE $0 -#line 367 "file1.s" -/*0x01de*/ BYTE $0 -#line 367 "file1.s" -/*0x01df*/ BYTE $0 -#line 367 "file1.s" -/*0x01e0*/ BYTE $0 -#line 581 "file1.s" -/*0x01e1*/ BYTE $0 -#line 581 "file1.s" -/*0x01e2*/ BYTE $0 -#line 581 "file1.s" -/*0x01e3*/ BYTE $0 -#line 638 "file1.s" -/*0x01e4*/ MOVQ $0x123456789, AX -#line 638 "file1.s" -/*0x01ee*/ MOVL $0x1234567, AX -#line 638 "file1.s" -/*0x01f3*/ BYTE $0 -#line 638 "file1.s" -/*0x01f4*/ BYTE $0 -#line 638 "file1.s" -/*0x01f5*/ BYTE $0 -#line 359 "file1.s" -/*0x01f6*/ BYTE $0 -#line 449 "file1.s" -/*0x01f7*/ BYTE $0 -#line 449 "file1.s" -/*0x01f8*/ BYTE $0 -#line 449 "file1.s" -/*0x01f9*/ BYTE $0 -#line 298 "file1.s" -/*0x01fa*/ BYTE $0 -#line 298 "file1.s" -/*0x01fb*/ BYTE $0 -#line 257 "file1.s" -/*0x01fc*/ BYTE $0 -#line 257 "file1.s" -/*0x01fd*/ BYTE $0 -#line 257 "file1.s" -/*0x01fe*/ BYTE $0 -#line 512 "file1.s" -/*0x01ff*/ MOVL $0x1234567, AX -#line 512 "file1.s" -/*0x0204*/ BYTE $0 -#line 512 "file1.s" -/*0x0205*/ BYTE $0 -#line 617 "file1.s" -/*0x0206*/ MOVL $0x1234567, AX -#line 478 "file1.s" -/*0x020b*/ BYTE $0 -/* */ PCDATA $2, $351 -#line 478 "file1.s" -/*0x020c*/ BYTE $0 -#line 958 "file1.s" -/*0x020d*/ BYTE $0 -#line 958 "file1.s" -/*0x020e*/ BYTE $0 -#line 958 "file1.s" -/*0x020f*/ BYTE $0 -#line 958 "file1.s" -/*0x0210*/ BYTE $0 -#line 371 "file1.s" -/*0x0211*/ MOVQ $0x123456789, AX -#line 371 "file1.s" -/*0x021b*/ BYTE $0 -#line 371 "file1.s" -/*0x021c*/ BYTE $0 -#line 56 "file3.s" -/*0x021d*/ MOVL $0x1234567, AX -#line 56 "file3.s" -/*0x0222*/ BYTE $0 -#line 56 "file3.s" -/*0x0223*/ BYTE $0 -#line 56 "file9.s" -/*0x0224*/ MOVQ $0x123456789, AX -#line 56 "file9.s" -/*0x022e*/ BYTE $0 -#line 56 "file9.s" -/*0x022f*/ BYTE $0 -#line 56 "file9.s" -/*0x0230*/ BYTE $0 -#line 56 "file9.s" -/*0x0231*/ BYTE $0 -#line 684 "file9.s" -/*0x0232*/ MOVQ $0x123456789, AX -#line 684 "file9.s" -/*0x023c*/ BYTE $0 -#line 684 "file9.s" -/*0x023d*/ POPQ AX -#line 407 "file9.s" -/*0x023e*/ MOVL $0x1234567, AX -#line 407 "file9.s" -/*0x0243*/ BYTE $0 - PUSHQ AX - PUSHQ AX - RET - -GLOBL funcdata1(SB), $16 -#line 1 "input" -TEXT func2(SB),7,$40-688 - FUNCDATA $1, funcdata2(SB) -#line 1 "input" -#line 1 "input" -/*0x0004*/ BYTE $0 -#line 860 "input" -/*0x0005*/ BYTE $0 -#line 860 "input" -/*0x0006*/ BYTE $0 -#line 860 "input" -/*0x0007*/ BYTE $0 -#line 860 "input" -/*0x0008*/ BYTE $0 -#line 85 "input" -/*0x0009*/ BYTE $0 -#line 85 "input" -/*0x000a*/ BYTE $0 -#line 355 "input" -/*0x000b*/ MOVQ $0x123456789, AX -#line 355 "input" -/*0x0015*/ MOVL $0x1234567, AX -#line 355 "input" -/*0x001a*/ BYTE $0 -#line 355 "input" -/*0x001b*/ BYTE $0 -#line 355 "input" -/*0x001c*/ BYTE $0 -#line 840 "input" -/*0x001d*/ MOVL $0x1234567, AX -#line 840 "input" -/*0x0022*/ BYTE $0 -#line 294 "input" -/*0x0023*/ MOVQ $0x123456789, AX -#line 294 "input" -/*0x002d*/ MOVQ $0x123456789, AX -#line 294 "input" -/*0x0037*/ MOVQ $0x123456789, AX -#line 294 "input" -/*0x0041*/ BYTE $0 -#line 294 "input" -/*0x0042*/ BYTE $0 -#line 294 "input" -/*0x0043*/ BYTE $0 -#line 294 "input" -/*0x0044*/ BYTE $0 -/* */ PCDATA $1, $385 -#line 294 "input" -/*0x0045*/ BYTE $0 -#line 294 "input" -/*0x0046*/ BYTE $0 -#line 294 "input" -/*0x0047*/ BYTE $0 -#line 81 "file9.s" -/*0x0048*/ MOVL $0x1234567, AX -#line 81 "file9.s" -/*0x004d*/ BYTE $0 -#line 81 "file9.s" -/*0x004e*/ BYTE $0 -#line 81 "file9.s" -/*0x004f*/ POPQ AX -#line 81 "file9.s" -/*0x0050*/ MOVL $0x1234567, AX -#line 81 "file9.s" -/*0x0055*/ BYTE $0 -/* */ PCDATA $1, $701 -#line 81 "file9.s" -/*0x0056*/ MOVL $0x1234567, AX -#line 81 "file9.s" -/*0x005b*/ BYTE $0 -#line 81 "file9.s" -/*0x005c*/ BYTE $0 -#line 81 "file9.s" -/*0x005d*/ BYTE $0 -#line 81 "file9.s" -/*0x005e*/ BYTE $0 -#line 290 "file9.s" -/*0x005f*/ BYTE $0 -#line 290 "file9.s" -/*0x0060*/ BYTE $0 -#line 290 "file9.s" -/*0x0061*/ BYTE $0 -#line 197 "file9.s" -/*0x0062*/ MOVL $0x1234567, AX -#line 197 "file9.s" -/*0x0067*/ BYTE $0 -#line 608 "file9.s" -/*0x0068*/ MOVQ $0x123456789, AX -#line 608 "file9.s" -/*0x0072*/ MOVQ $0x123456789, AX -#line 608 "file9.s" -/*0x007c*/ BYTE $0 -/* */ PCDATA $1, $562 -#line 608 "file9.s" -/*0x007d*/ BYTE $0 -#line 608 "file9.s" -/*0x007e*/ BYTE $0 -#line 189 "file9.s" -/*0x007f*/ MOVL $0x1234567, AX -#line 189 "file9.s" -/*0x0084*/ BYTE $0 -#line 189 "file9.s" -/*0x0085*/ BYTE $0 -#line 189 "file9.s" -/*0x0086*/ BYTE $0 -#line 189 "file9.s" -/*0x0087*/ BYTE $0 -#line 472 "file9.s" -/*0x0088*/ MOVL $0x1234567, AX -#line 472 "file9.s" -/*0x008d*/ BYTE $0 -#line 472 "file9.s" -/*0x008e*/ BYTE $0 -#line 472 "file9.s" -/*0x008f*/ PUSHQ AX -#line 472 "file9.s" -/*0x0090*/ MOVQ $0x123456789, AX -#line 472 "file9.s" -/*0x009a*/ MOVL $0x1234567, AX -#line 472 "file9.s" -/*0x009f*/ BYTE $0 -#line 472 "file9.s" -/*0x00a0*/ BYTE $0 -#line 472 "file9.s" -/*0x00a1*/ BYTE $0 -#line 472 "file9.s" -/*0x00a2*/ BYTE $0 -#line 148 "file9.s" -/*0x00a3*/ MOVQ $0x123456789, AX -#line 148 "file9.s" -/*0x00ad*/ BYTE $0 -#line 148 "file9.s" -/*0x00ae*/ BYTE $0 -#line 148 "file9.s" -/*0x00af*/ BYTE $0 -#line 148 "file9.s" -/*0x00b0*/ BYTE $0 -#line 562 "file9.s" -/*0x00b1*/ MOVL $0x1234567, AX -#line 562 "file9.s" -/*0x00b6*/ BYTE $0 -#line 562 "file9.s" -/*0x00b7*/ PUSHQ AX -#line 562 "file9.s" -/*0x00b8*/ BYTE $0 -#line 532 "file9.s" -/*0x00b9*/ MOVQ $0x123456789, AX -#line 532 "file9.s" -/*0x00c3*/ MOVQ $0x123456789, AX -#line 282 "file9.s" -/*0x00cd*/ BYTE $0 -#line 282 "file9.s" -/*0x00ce*/ BYTE $0 -#line 282 "file9.s" -/*0x00cf*/ BYTE $0 -/* */ PCDATA $2, $861 -#line 282 "file9.s" -/*0x00d0*/ BYTE $0 -#line 282 "file9.s" -/*0x00d1*/ BYTE $0 -/* */ PCDATA $2, $310 -#line 282 "file9.s" -/*0x00d2*/ BYTE $0 -#line 416 "file9.s" -/*0x00d3*/ MOVQ $0x123456789, AX -#line 416 "file9.s" -/*0x00dd*/ MOVL $0x1234567, AX -#line 780 "file9.s" -/*0x00e2*/ BYTE $0 -#line 780 "file9.s" -/*0x00e3*/ BYTE $0 -#line 765 "file9.s" -/*0x00e4*/ MOVL $0x1234567, AX -#line 523 "file9.s" -/*0x00e9*/ BYTE $0 -#line 523 "file9.s" -/*0x00ea*/ BYTE $0 -#line 523 "file9.s" -/*0x00eb*/ BYTE $0 -#line 733 "file9.s" -/*0x00ec*/ MOVQ $0x123456789, AX -#line 378 "file9.s" -/*0x00f6*/ BYTE $0 -#line 378 "file9.s" -/*0x00f7*/ BYTE $0 -#line 378 "file9.s" -/*0x00f8*/ BYTE $0 -#line 378 "file9.s" -/*0x00f9*/ BYTE $0 -#line 540 "file9.s" -/*0x00fa*/ BYTE $0 -#line 540 "file9.s" -/*0x00fb*/ BYTE $0 -#line 57 "file9.s" -/*0x00fc*/ BYTE $0 -#line 57 "file9.s" -/*0x00fd*/ BYTE $0 -#line 57 "file9.s" -/*0x00fe*/ BYTE $0 -#line 417 "file9.s" -/*0x00ff*/ BYTE $0 -/* */ PCDATA $1, $932 -#line 417 "file9.s" -/*0x0100*/ BYTE $0 -#line 417 "file9.s" -/*0x0101*/ BYTE $0 -#line 417 "file9.s" -/*0x0102*/ BYTE $0 -#line 417 "file9.s" -/*0x0103*/ BYTE $0 -#line 713 "file9.s" -/*0x0104*/ MOVL $0x1234567, AX -#line 610 "file0.s" -/*0x0109*/ MOVQ $0x123456789, AX -#line 610 "file0.s" -/*0x0113*/ MOVL $0x1234567, AX -#line 610 "file0.s" -/*0x0118*/ BYTE $0 -#line 787 "file0.s" -/*0x0119*/ MOVQ $0x123456789, AX -#line 829 "file0.s" -/*0x0123*/ BYTE $0 -#line 829 "file0.s" -/*0x0124*/ BYTE $0 -#line 829 "file0.s" -/*0x0125*/ BYTE $0 -#line 849 "file0.s" -/*0x0126*/ BYTE $0 -#line 849 "file0.s" -/*0x0127*/ BYTE $0 -#line 849 "file0.s" -/*0x0128*/ BYTE $0 -/* */ PCDATA $2, $356 -#line 849 "file0.s" -/*0x0129*/ BYTE $0 -#line 849 "file0.s" -/*0x012a*/ BYTE $0 -#line 88 "file0.s" -/*0x012b*/ MOVL $0x1234567, AX -#line 88 "file0.s" -/*0x0130*/ BYTE $0 -#line 88 "file0.s" -/*0x0131*/ BYTE $0 -#line 88 "file0.s" -/*0x0132*/ BYTE $0 -#line 684 "file0.s" -/*0x0133*/ BYTE $0 -#line 684 "file0.s" -/*0x0134*/ BYTE $0 -#line 684 "file0.s" -/*0x0135*/ BYTE $0 -#line 684 "file0.s" -/*0x0136*/ BYTE $0 -#line 238 "file0.s" -/*0x0137*/ BYTE $0 -#line 238 "file0.s" -/*0x0138*/ BYTE $0 -#line 238 "file0.s" -/*0x0139*/ PUSHQ AX -#line 238 "file0.s" -/*0x013a*/ BYTE $0 -#line 238 "file0.s" -/*0x013b*/ BYTE $0 -#line 603 "file0.s" -/*0x013c*/ BYTE $0 -#line 981 "file0.s" -/*0x013d*/ BYTE $0 -#line 981 "file0.s" -/*0x013e*/ POPQ AX -#line 616 "file0.s" -/*0x013f*/ BYTE $0 -#line 616 "file0.s" -/*0x0140*/ BYTE $0 -#line 616 "file0.s" -/*0x0141*/ BYTE $0 -#line 616 "file0.s" -/*0x0142*/ BYTE $0 -#line 716 "file0.s" -/*0x0143*/ MOVL $0x1234567, AX -#line 716 "file0.s" -/*0x0148*/ BYTE $0 -#line 716 "file0.s" -/*0x0149*/ BYTE $0 -#line 34 "file0.s" -/*0x014a*/ BYTE $0 -#line 34 "file0.s" -/*0x014b*/ BYTE $0 -#line 34 "file0.s" -/*0x014c*/ BYTE $0 -#line 90 "file0.s" -/*0x014d*/ MOVL $0x1234567, AX -#line 316 "file0.s" -/*0x0152*/ MOVQ $0x123456789, AX -#line 230 "file0.s" -/*0x015c*/ MOVQ $0x123456789, AX -#line 799 "file0.s" -/*0x0166*/ MOVQ $0x123456789, AX -#line 799 "file0.s" -/*0x0170*/ BYTE $0 -#line 799 "file0.s" -/*0x0171*/ BYTE $0 -/* */ PCDATA $1, $247 -#line 799 "file0.s" -/*0x0172*/ BYTE $0 -#line 799 "file0.s" -/*0x0173*/ BYTE $0 -#line 799 "file0.s" -/*0x0174*/ BYTE $0 -#line 655 "file0.s" -/*0x0175*/ MOVL $0x1234567, AX -#line 655 "file0.s" -/*0x017a*/ BYTE $0 -#line 551 "file0.s" -/*0x017b*/ BYTE $0 -#line 551 "file0.s" -/*0x017c*/ BYTE $0 -#line 271 "file0.s" -/*0x017d*/ MOVQ $0x123456789, AX -#line 271 "file0.s" -/*0x0187*/ MOVQ $0x123456789, AX -#line 271 "file0.s" -/*0x0191*/ MOVL $0x1234567, AX -#line 271 "file0.s" -/*0x0196*/ BYTE $0 -#line 271 "file0.s" -/*0x0197*/ BYTE $0 -#line 271 "file0.s" -/*0x0198*/ BYTE $0 -#line 233 "file0.s" -/*0x0199*/ MOVL $0x1234567, AX -#line 233 "file0.s" -/*0x019e*/ BYTE $0 -#line 233 "file0.s" -/*0x019f*/ BYTE $0 -#line 233 "file0.s" -/*0x01a0*/ BYTE $0 -#line 233 "file0.s" -/*0x01a1*/ BYTE $0 -#line 738 "file0.s" -/*0x01a2*/ MOVL $0x1234567, AX -#line 738 "file0.s" -/*0x01a7*/ BYTE $0 -#line 800 "file0.s" -/*0x01a8*/ BYTE $0 -#line 800 "file0.s" -/*0x01a9*/ BYTE $0 -#line 646 "file0.s" -/*0x01aa*/ MOVQ $0x123456789, AX -#line 646 "file0.s" -/*0x01b4*/ BYTE $0 -#line 646 "file0.s" -/*0x01b5*/ BYTE $0 -#line 646 "file0.s" -/*0x01b6*/ BYTE $0 -#line 709 "file0.s" -/*0x01b7*/ BYTE $0 -#line 709 "file0.s" -/*0x01b8*/ BYTE $0 -#line 709 "file0.s" -/*0x01b9*/ BYTE $0 -#line 709 "file0.s" -/*0x01ba*/ BYTE $0 -#line 296 "file0.s" -/*0x01bb*/ BYTE $0 -#line 296 "file0.s" -/*0x01bc*/ BYTE $0 -#line 296 "file0.s" -/*0x01bd*/ BYTE $0 -#line 71 "file0.s" -/*0x01be*/ BYTE $0 -#line 71 "file0.s" -/*0x01bf*/ BYTE $0 -#line 71 "file0.s" -/*0x01c0*/ BYTE $0 -#line 7 "file2.s" -/*0x01c1*/ BYTE $0 -#line 747 "file2.s" -/*0x01c2*/ BYTE $0 -#line 177 "file2.s" -/*0x01c3*/ MOVQ $0x123456789, AX -#line 177 "file2.s" -/*0x01cd*/ MOVQ $0x123456789, AX -#line 177 "file2.s" -/*0x01d7*/ MOVL $0x1234567, AX -#line 177 "file2.s" -/*0x01dc*/ BYTE $0 -#line 177 "file2.s" -/*0x01dd*/ BYTE $0 -#line 177 "file2.s" -/*0x01de*/ BYTE $0 -#line 72 "file2.s" -/*0x01df*/ BYTE $0 -#line 215 "file2.s" -/*0x01e0*/ MOVL $0x1234567, AX -#line 215 "file2.s" -/*0x01e5*/ BYTE $0 -#line 215 "file2.s" -/*0x01e6*/ BYTE $0 -#line 215 "file2.s" -/*0x01e7*/ BYTE $0 -#line 946 "file2.s" -/*0x01e8*/ BYTE $0 -#line 946 "file2.s" -/*0x01e9*/ BYTE $0 -#line 946 "file2.s" -/*0x01ea*/ BYTE $0 -#line 946 "file2.s" -/*0x01eb*/ BYTE $0 -#line 263 "file2.s" -/*0x01ec*/ MOVL $0x1234567, AX -#line 263 "file2.s" -/*0x01f1*/ BYTE $0 -#line 897 "file2.s" -/*0x01f2*/ MOVQ $0x123456789, AX -#line 897 "file2.s" -/*0x01fc*/ MOVQ $0x123456789, AX -#line 897 "file2.s" -/*0x0206*/ BYTE $0 -#line 897 "file2.s" -/*0x0207*/ BYTE $0 -#line 897 "file2.s" -/*0x0208*/ BYTE $0 -#line 229 "file2.s" -/*0x0209*/ BYTE $0 -#line 229 "file2.s" -/*0x020a*/ BYTE $0 -#line 229 "file2.s" -/*0x020b*/ BYTE $0 -#line 229 "file2.s" -/*0x020c*/ BYTE $0 -/* */ PCDATA $1, $7 -#line 229 "file2.s" -/*0x020d*/ MOVL $0x1234567, AX -#line 229 "file2.s" -/*0x0212*/ BYTE $0 -#line 353 "file2.s" -/*0x0213*/ BYTE $0 -#line 353 "file2.s" -/*0x0214*/ BYTE $0 -#line 353 "file2.s" -/*0x0215*/ BYTE $0 -#line 353 "file2.s" -/*0x0216*/ BYTE $0 -#line 852 "file2.s" -/*0x0217*/ BYTE $0 -#line 852 "file2.s" -/*0x0218*/ BYTE $0 -#line 852 "file2.s" -/*0x0219*/ BYTE $0 -#line 852 "file2.s" -/*0x021a*/ BYTE $0 -#line 852 "file2.s" -/*0x021b*/ PUSHQ AX -#line 852 "file2.s" -/*0x021c*/ BYTE $0 -#line 852 "file2.s" -/*0x021d*/ BYTE $0 -#line 852 "file2.s" -/*0x021e*/ BYTE $0 -#line 904 "file2.s" -/*0x021f*/ MOVQ $0x123456789, AX -#line 904 "file2.s" -/*0x0229*/ BYTE $0 -#line 904 "file2.s" -/*0x022a*/ BYTE $0 -#line 882 "file2.s" -/*0x022b*/ BYTE $0 -#line 905 "file2.s" -/*0x022c*/ BYTE $0 -#line 410 "file2.s" -/*0x022d*/ MOVQ $0x123456789, AX -#line 410 "file2.s" -/*0x0237*/ BYTE $0 -#line 410 "file2.s" -/*0x0238*/ BYTE $0 -#line 410 "file2.s" -/*0x0239*/ POPQ AX -#line 410 "file2.s" -/*0x023a*/ BYTE $0 -#line 410 "file2.s" -/*0x023b*/ BYTE $0 -#line 410 "file2.s" -/*0x023c*/ BYTE $0 -/* */ PCDATA $2, $173 -#line 410 "file2.s" -/*0x023d*/ MOVL $0x1234567, AX -#line 410 "file2.s" -/*0x0242*/ BYTE $0 -/* */ PCDATA $1, $396 -#line 410 "file2.s" -/*0x0243*/ BYTE $0 -#line 410 "file2.s" -/*0x0244*/ BYTE $0 -#line 666 "file2.s" -/*0x0245*/ BYTE $0 -#line 129 "file2.s" -/*0x0246*/ MOVQ $0x123456789, AX -#line 129 "file2.s" -/*0x0250*/ BYTE $0 -#line 391 "file2.s" -/*0x0251*/ BYTE $0 -#line 696 "file2.s" -/*0x0252*/ MOVL $0x1234567, AX -#line 940 "file2.s" -/*0x0257*/ BYTE $0 -#line 940 "file2.s" -/*0x0258*/ BYTE $0 -#line 606 "file2.s" -/*0x0259*/ MOVL $0x1234567, AX -#line 606 "file2.s" -/*0x025e*/ BYTE $0 -#line 648 "file2.s" -/*0x025f*/ MOVQ $0x123456789, AX -#line 648 "file2.s" -/*0x0269*/ BYTE $0 -#line 648 "file2.s" -/*0x026a*/ BYTE $0 -/* */ PCDATA $2, $84 -#line 648 "file2.s" -/*0x026b*/ MOVQ $0x123456789, AX -#line 648 "file2.s" -/*0x0275*/ MOVQ $0x123456789, AX -#line 648 "file2.s" -/*0x027f*/ MOVQ $0x123456789, AX -#line 648 "file2.s" -/*0x0289*/ MOVQ $0x123456789, AX -#line 648 "file2.s" -/*0x0293*/ MOVQ $0x123456789, AX -#line 648 "file2.s" -/*0x029d*/ MOVL $0x1234567, AX -#line 648 "file2.s" -/*0x02a2*/ PUSHQ AX -#line 648 "file2.s" -/*0x02a3*/ MOVL $0x1234567, AX -#line 648 "file2.s" -/*0x02a8*/ BYTE $0 -#line 648 "file2.s" -/*0x02a9*/ BYTE $0 -#line 648 "file2.s" -/*0x02aa*/ BYTE $0 -#line 648 "file2.s" -/*0x02ab*/ BYTE $0 -#line 449 "file2.s" -/*0x02ac*/ MOVQ $0x123456789, AX -#line 449 "file2.s" -/*0x02b6*/ MOVL $0x1234567, AX -#line 284 "file2.s" -/*0x02bb*/ BYTE $0 -#line 284 "file2.s" -/*0x02bc*/ BYTE $0 -#line 284 "file2.s" -/*0x02bd*/ BYTE $0 -#line 284 "file2.s" -/*0x02be*/ BYTE $0 -#line 26 "file2.s" -/*0x02bf*/ MOVQ $0x123456789, AX -#line 26 "file2.s" -/*0x02c9*/ MOVL $0x1234567, AX -#line 26 "file2.s" -/*0x02ce*/ BYTE $0 -#line 26 "file2.s" -/*0x02cf*/ BYTE $0 -#line 26 "file2.s" -/*0x02d0*/ BYTE $0 -#line 605 "file2.s" -/*0x02d1*/ MOVL $0x1234567, AX -#line 605 "file2.s" -/*0x02d6*/ BYTE $0 -#line 605 "file2.s" -/*0x02d7*/ BYTE $0 -#line 605 "file2.s" -/*0x02d8*/ BYTE $0 -#line 593 "file2.s" -/*0x02d9*/ MOVL $0x1234567, AX -#line 541 "file2.s" -/*0x02de*/ MOVL $0x1234567, AX -#line 541 "file2.s" -/*0x02e3*/ BYTE $0 -#line 541 "file2.s" -/*0x02e4*/ BYTE $0 -#line 181 "file2.s" -/*0x02e5*/ MOVQ $0x123456789, AX -#line 181 "file2.s" -/*0x02ef*/ BYTE $0 -#line 907 "file2.s" -/*0x02f0*/ MOVQ $0x123456789, AX -#line 704 "file2.s" -/*0x02fa*/ MOVQ $0x123456789, AX -#line 704 "file2.s" -/*0x0304*/ MOVQ $0x123456789, AX -#line 704 "file2.s" -/*0x030e*/ MOVL $0x1234567, AX -#line 704 "file2.s" -/*0x0313*/ BYTE $0 -#line 704 "file2.s" -/*0x0314*/ BYTE $0 -#line 704 "file2.s" -/*0x0315*/ BYTE $0 -#line 704 "file2.s" -/*0x0316*/ BYTE $0 -#line 859 "file2.s" -/*0x0317*/ MOVL $0x1234567, AX -#line 407 "file2.s" -/*0x031c*/ BYTE $0 -#line 407 "file2.s" -/*0x031d*/ BYTE $0 -/* */ PCDATA $2, $569 -#line 407 "file2.s" -/*0x031e*/ MOVL $0x1234567, AX -#line 407 "file2.s" -/*0x0323*/ BYTE $0 -#line 407 "file2.s" -/*0x0324*/ BYTE $0 -#line 407 "file2.s" -/*0x0325*/ BYTE $0 -/* */ PCDATA $1, $937 -#line 407 "file2.s" -/*0x0326*/ MOVL $0x1234567, AX -#line 407 "file2.s" -/*0x032b*/ BYTE $0 -#line 774 "file2.s" -/*0x032c*/ MOVQ $0x123456789, AX -#line 774 "file2.s" -/*0x0336*/ BYTE $0 -#line 547 "file2.s" -/*0x0337*/ BYTE $0 -#line 547 "file2.s" -/*0x0338*/ BYTE $0 -#line 547 "file2.s" -/*0x0339*/ BYTE $0 -#line 547 "file2.s" -/*0x033a*/ PUSHQ AX -#line 547 "file2.s" -/*0x033b*/ MOVL $0x1234567, AX -#line 427 "file2.s" -/*0x0340*/ MOVL $0x1234567, AX -/* */ PCDATA $1, $462 -#line 427 "file2.s" -/*0x0345*/ MOVQ $0x123456789, AX -#line 427 "file2.s" -/*0x034f*/ MOVL $0x1234567, AX -#line 427 "file2.s" -/*0x0354*/ BYTE $0 -#line 427 "file2.s" -/*0x0355*/ BYTE $0 -#line 427 "file2.s" -/*0x0356*/ BYTE $0 -#line 427 "file2.s" -/*0x0357*/ BYTE $0 -/* */ PCDATA $2, $303 -#line 427 "file2.s" -/*0x0358*/ MOVQ $0x123456789, AX -#line 427 "file2.s" -/*0x0362*/ BYTE $0 -#line 427 "file2.s" -/*0x0363*/ BYTE $0 -#line 708 "file2.s" -/*0x0364*/ BYTE $0 -#line 708 "file2.s" -/*0x0365*/ BYTE $0 -#line 708 "file2.s" -/*0x0366*/ BYTE $0 -#line 708 "file2.s" -/*0x0367*/ BYTE $0 -#line 218 "file2.s" -/*0x0368*/ MOVL $0x1234567, AX -#line 44 "file2.s" -/*0x036d*/ BYTE $0 -#line 915 "file2.s" -/*0x036e*/ BYTE $0 -#line 915 "file2.s" -/*0x036f*/ BYTE $0 -#line 915 "file2.s" -/*0x0370*/ BYTE $0 -#line 915 "file2.s" -/*0x0371*/ BYTE $0 -#line 122 "file2.s" -/*0x0372*/ MOVQ $0x123456789, AX -#line 122 "file2.s" -/*0x037c*/ MOVL $0x1234567, AX -#line 122 "file2.s" -/*0x0381*/ BYTE $0 -#line 122 "file2.s" -/*0x0382*/ BYTE $0 -#line 266 "file2.s" -/*0x0383*/ BYTE $0 -#line 266 "file2.s" -/*0x0384*/ BYTE $0 -#line 368 "file2.s" -/*0x0385*/ BYTE $0 -#line 368 "file2.s" -/*0x0386*/ BYTE $0 -#line 368 "file2.s" -/*0x0387*/ BYTE $0 -#line 368 "file2.s" -/*0x0388*/ BYTE $0 -#line 775 "file2.s" -/*0x0389*/ BYTE $0 -#line 10 "file8.s" -/*0x038a*/ BYTE $0 -#line 10 "file8.s" -/*0x038b*/ BYTE $0 -#line 422 "file8.s" -/*0x038c*/ MOVL $0x1234567, AX -#line 422 "file8.s" -/*0x0391*/ BYTE $0 -#line 613 "file8.s" -/*0x0392*/ BYTE $0 -#line 613 "file8.s" -/*0x0393*/ BYTE $0 -#line 613 "file8.s" -/*0x0394*/ BYTE $0 -#line 697 "file8.s" -/*0x0395*/ MOVQ $0x123456789, AX -#line 697 "file8.s" -/*0x039f*/ MOVQ $0x123456789, AX -#line 697 "file8.s" -/*0x03a9*/ BYTE $0 -#line 697 "file8.s" -/*0x03aa*/ BYTE $0 -#line 697 "file8.s" -/*0x03ab*/ BYTE $0 - POPQ AX - POPQ AX - POPQ AX - RET - -GLOBL funcdata2(SB), $16 - -TEXT start(SB),7,$0 - CALL func0(SB) - CALL func1(SB) - CALL func2(SB) - MOVQ $runtime·pclntab(SB), AX - - RET diff --git a/src/cmd/link/util.go b/src/cmd/link/util.go deleted file mode 100644 index b8a6b2cb30..0000000000 --- a/src/cmd/link/util.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2014 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 - -// round returns size rounded up to the next multiple of align; -// align must be a power of two. -func round(size, align Addr) Addr { - return (size + align - 1) &^ (align - 1) -} diff --git a/src/cmd/link/write.go b/src/cmd/link/write.go deleted file mode 100644 index 7e11b2fe71..0000000000 --- a/src/cmd/link/write.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2014 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. - -// Writing of executable and (for hostlink mode) object files. - -package main - -import "io" - -func (p *Prog) write(w io.Writer) { - p.Entry = p.Syms[p.startSym].Addr - p.formatter.write(w, p) -} diff --git a/src/cmd/newlink/auto.go b/src/cmd/newlink/auto.go new file mode 100644 index 0000000000..21f6d60827 --- /dev/null +++ b/src/cmd/newlink/auto.go @@ -0,0 +1,117 @@ +// Copyright 2014 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. + +// Automatic symbol generation. + +// TODO(rsc): Handle go.typelink, go.track symbols. +// TODO(rsc): Do not handle $f64. and $f32. symbols. Instead, generate those +// from the compiler and assemblers as dupok data, and then remove autoData below. +package main + +import ( + "cmd/internal/goobj" + "strconv" + "strings" +) + +// linkerDefined lists the symbols supplied by other parts of the linker +// (runtime.go and layout.go). +var linkerDefined = map[string]bool{ + "runtime.bss": true, + "runtime.data": true, + "runtime.ebss": true, + "runtime.edata": true, + "runtime.efunctab": true, + "runtime.end": true, + "runtime.enoptrbss": true, + "runtime.enoptrdata": true, + "runtime.erodata": true, + "runtime.etext": true, + "runtime.etypelink": true, + "runtime.functab": true, + "runtime.gcbss": true, + "runtime.gcdata": true, + "runtime.noptrbss": true, + "runtime.noptrdata": true, + "runtime.pclntab": true, + "runtime.rodata": true, + "runtime.text": true, + "runtime.typelink": true, +} + +// isAuto reports whether sym is an automatically-generated data or constant symbol. +func (p *Prog) isAuto(sym goobj.SymID) bool { + return strings.HasPrefix(sym.Name, "go.weak.") || + strings.HasPrefix(sym.Name, "$f64.") || + strings.HasPrefix(sym.Name, "$f32.") || + linkerDefined[sym.Name] +} + +// autoData defines the automatically generated data symbols needed by p. +func (p *Prog) autoData() { + for sym := range p.Missing { + switch { + // Floating-point constants that need to be loaded from memory are + // written as $f64.{16 hex digits} or $f32.{8 hex digits}; the hex digits + // give the IEEE bit pattern of the constant. As far as the layout into + // memory is concerned, we interpret these as uint64 or uint32 constants. + case strings.HasPrefix(sym.Name, "$f64."), strings.HasPrefix(sym.Name, "$f32."): + size := 64 + if sym.Name[2:4] == "32" { + size = 32 + } + delete(p.Missing, sym) + fbits, err := strconv.ParseUint(sym.Name[len("$f64."):], 16, size) + if err != nil { + p.errorf("unexpected floating point symbol %s", sym) + continue + } + data := make([]byte, size/8) + if size == 64 { + p.byteorder.PutUint64(data, fbits) + } else { + p.byteorder.PutUint32(data, uint32(fbits)) + } + p.addSym(&Sym{ + Sym: &goobj.Sym{ + SymID: sym, + Kind: goobj.SRODATA, + Size: size / 8, + }, + Bytes: data, + }) + } + } +} + +// autoConst defines the automatically generated constant symbols needed by p. +func (p *Prog) autoConst() { + for sym := range p.Missing { + switch { + case strings.HasPrefix(sym.Name, "go.weak."): + // weak symbol resolves to actual symbol if present, or else nil. + delete(p.Missing, sym) + targ := sym + targ.Name = sym.Name[len("go.weak."):] + var addr Addr + if s := p.Syms[targ]; s != nil { + addr = s.Addr + } + p.defineConst(sym.Name, addr) + } + } +} + +// defineConst defines a new symbol with the given name and constant address. +func (p *Prog) defineConst(name string, addr Addr) { + sym := goobj.SymID{Name: name} + p.addSym(&Sym{ + Sym: &goobj.Sym{ + SymID: sym, + Kind: goobj.SCONST, + }, + Package: nil, + Addr: addr, + }) +} diff --git a/src/cmd/newlink/auto_test.go b/src/cmd/newlink/auto_test.go new file mode 100644 index 0000000000..f99e097a9c --- /dev/null +++ b/src/cmd/newlink/auto_test.go @@ -0,0 +1,72 @@ +// Copyright 2014 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 for auto-generated symbols. + +// There is no test for $f64. and $f32. symbols, because those are +// not possible to write in the assembler syntax. Instead of changing +// the assembler to allow that, we plan to change the compilers +// not to generate such symbols (plain dupok data is sufficient). + +package main + +import ( + "bytes" + "cmd/internal/goobj" + "testing" +) + +// Each test case is an object file, generated from a corresponding .s file. +// The image of the autotab symbol should be a sequence of pairs of +// identical 8-byte sequences. +var autoTests = []string{ + "testdata/autosection.6", + "testdata/autoweak.6", +} + +func TestAuto(t *testing.T) { + for _, obj := range autoTests { + p := Prog{GOOS: "darwin", GOARCH: "amd64", StartSym: "start"} + p.omitRuntime = true + p.Error = func(s string) { t.Error(s) } + var buf bytes.Buffer + p.link(&buf, obj) + if p.NumError > 0 { + continue // already reported + } + + const name = "autotab" + sym := p.Syms[goobj.SymID{Name: name}] + if sym == nil { + t.Errorf("%s is missing %s symbol", obj, name) + return + } + if sym.Size == 0 { + return + } + + seg := sym.Section.Segment + off := sym.Addr - seg.VirtAddr + data := seg.Data[off : off+Addr(sym.Size)] + if len(data)%16 != 0 { + t.Errorf("%s: %s.Size = %d, want multiple of 16", obj, name, len(data)) + return + } + Data: + for i := 0; i < len(data); i += 16 { + have := p.byteorder.Uint64(data[i : i+8]) + want := p.byteorder.Uint64(data[i+8 : i+16]) + if have != want { + // Look for relocation so we can explain what went wrong. + for _, r := range sym.Reloc { + if r.Offset == i { + t.Errorf("%s: %s+%#x: %s: have %#x want %#x", obj, name, i, r.Sym, have, want) + continue Data + } + } + t.Errorf("%s: %s+%#x: have %#x want %#x", obj, name, i, have, want) + } + } + } +} diff --git a/src/cmd/newlink/dead.go b/src/cmd/newlink/dead.go new file mode 100644 index 0000000000..ee23a61f81 --- /dev/null +++ b/src/cmd/newlink/dead.go @@ -0,0 +1,74 @@ +// Copyright 2014 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. + +// Removal of dead code and data. + +package main + +import "cmd/internal/goobj" + +// dead removes unreachable code and data from the program. +// It is basically a mark-sweep garbage collection: traverse all the +// symbols reachable from the entry (startSymID) and then delete +// the rest. +func (p *Prog) dead() { + p.Dead = make(map[goobj.SymID]bool) + reachable := make(map[goobj.SymID]bool) + p.walkDead(p.startSym, reachable) + + for sym := range p.Syms { + if !reachable[sym] { + delete(p.Syms, sym) + p.Dead[sym] = true + } + } + + for sym := range p.Missing { + if !reachable[sym] { + delete(p.Missing, sym) + p.Dead[sym] = true + } + } + + p.SymOrder = removeDead(p.SymOrder, reachable) + + for _, pkg := range p.Packages { + pkg.Syms = removeDead(pkg.Syms, reachable) + } +} + +// walkDead traverses the symbols reachable from sym, adding them to reachable. +// The caller has verified that reachable[sym] = false. +func (p *Prog) walkDead(sym goobj.SymID, reachable map[goobj.SymID]bool) { + reachable[sym] = true + s := p.Syms[sym] + if s == nil { + return + } + for i := range s.Reloc { + r := &s.Reloc[i] + if !reachable[r.Sym] { + p.walkDead(r.Sym, reachable) + } + } + if s.Func != nil { + for _, fdata := range s.Func.FuncData { + if fdata.Sym.Name != "" && !reachable[fdata.Sym] { + p.walkDead(fdata.Sym, reachable) + } + } + } +} + +// removeDead removes unreachable (dead) symbols from syms, +// returning a shortened slice using the same underlying array. +func removeDead(syms []*Sym, reachable map[goobj.SymID]bool) []*Sym { + keep := syms[:0] + for _, sym := range syms { + if reachable[sym.SymID] { + keep = append(keep, sym) + } + } + return keep +} diff --git a/src/cmd/newlink/dead_test.go b/src/cmd/newlink/dead_test.go new file mode 100644 index 0000000000..eb34d0580d --- /dev/null +++ b/src/cmd/newlink/dead_test.go @@ -0,0 +1,97 @@ +// Copyright 2014 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/goobj" + "reflect" + "strings" + "testing" +) + +// Each test case is an object file, generated from a corresponding .s file. +// The symbols in the object file with a dead_ prefix are the ones that +// should be removed from the program. +var deadTests = []string{ + "testdata/dead.6", +} + +func TestDead(t *testing.T) { + for _, obj := range deadTests { + p := Prog{GOOS: "darwin", GOARCH: "amd64", StartSym: "start"} + p.omitRuntime = true + p.Error = func(s string) { t.Error(s) } + p.init() + p.scan(obj) + if p.NumError > 0 { + continue // already reported + } + origSyms := copyMap(p.Syms) + origMissing := copyMap(p.Missing) + origSymOrder := copySlice(p.SymOrder) + origPkgSyms := copySlice(p.Packages["main"].Syms) + p.dead() + checkDeadMap(t, obj, "p.Syms", origSyms, p.Syms) + checkDeadMap(t, obj, "p.Missing", origMissing, p.Missing) + checkDeadSlice(t, obj, "p.SymOrder", origSymOrder, p.SymOrder) + checkDeadSlice(t, obj, `p.Packages["main"].Syms`, origPkgSyms, p.Packages["main"].Syms) + } +} + +func copyMap(m interface{}) interface{} { + v := reflect.ValueOf(m) + out := reflect.MakeMap(v.Type()) + for _, key := range v.MapKeys() { + out.SetMapIndex(key, v.MapIndex(key)) + } + return out.Interface() +} + +func checkDeadMap(t *testing.T, obj, name string, old, new interface{}) { + vold := reflect.ValueOf(old) + vnew := reflect.ValueOf(new) + for _, vid := range vold.MapKeys() { + id := vid.Interface().(goobj.SymID) + if strings.HasPrefix(id.Name, "dead_") { + if vnew.MapIndex(vid).IsValid() { + t.Errorf("%s: %s contains unnecessary symbol %s", obj, name, id) + } + } else { + if !vnew.MapIndex(vid).IsValid() { + t.Errorf("%s: %s is missing symbol %s", obj, name, id) + } + } + } + for _, vid := range vnew.MapKeys() { + id := vid.Interface().(goobj.SymID) + if !vold.MapIndex(vid).IsValid() { + t.Errorf("%s: %s contains unexpected symbol %s", obj, name, id) + } + } +} + +func copySlice(x []*Sym) (out []*Sym) { + return append(out, x...) +} + +func checkDeadSlice(t *testing.T, obj, name string, old, new []*Sym) { + for i, s := range old { + if strings.HasPrefix(s.Name, "dead_") { + continue + } + if len(new) == 0 { + t.Errorf("%s: %s is missing symbol %s\nhave%v\nwant%v", obj, name, s, new, old[i:]) + return + } + if new[0].SymID != s.SymID { + t.Errorf("%s: %s is incorrect: have %s, want %s\nhave%v\nwant%v", obj, name, new[0].SymID, s.SymID, new, old[i:]) + return + } + new = new[1:] + } + if len(new) > 0 { + t.Errorf("%s: %s has unexpected symbols: %v", obj, name, new) + } +} diff --git a/src/cmd/newlink/debug.go b/src/cmd/newlink/debug.go new file mode 100644 index 0000000000..ee20644fd0 --- /dev/null +++ b/src/cmd/newlink/debug.go @@ -0,0 +1,11 @@ +// Copyright 2014 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. + +// Generation of debug data structures (in the executable but not mapped at run time). +// See also runtime.go. + +package main + +func (p *Prog) debug() { +} diff --git a/src/cmd/newlink/hex_test.go b/src/cmd/newlink/hex_test.go new file mode 100644 index 0000000000..b76b266558 --- /dev/null +++ b/src/cmd/newlink/hex_test.go @@ -0,0 +1,74 @@ +// Copyright 2014 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 ( + "encoding/hex" + "fmt" + "io/ioutil" + "regexp" + "strconv" + "strings" + "testing" +) + +// mustParseHexdumpFile returns a block of data generated by +// parsing the hex dump in the named file. +// If the file cannot be read or does not contain a valid hex dump, +// mustParseHexdumpFile calls t.Fatal. +func mustParseHexdumpFile(t *testing.T, file string) []byte { + hex, err := ioutil.ReadFile(file) + if err != nil { + t.Fatal(err) + } + data, err := parseHexdump(string(hex)) + if err != nil { + t.Fatal(err) + } + return data +} + +// parseHexdump parses the hex dump in text, which should be the +// output of "hexdump -C" or Plan 9's "xd -b", +// and returns the original data used to produce the dump. +// It is meant to enable storing golden binary files as text, so that +// changes to the golden files can be seen during code reviews. +func parseHexdump(text string) ([]byte, error) { + var out []byte + for _, line := range strings.Split(text, "\n") { + if i := strings.Index(line, "|"); i >= 0 { // remove text dump + line = line[:i] + } + f := strings.Fields(line) + if len(f) > 1+16 { + return nil, fmt.Errorf("parsing hex dump: too many fields on line %q", line) + } + if len(f) == 0 || len(f) == 1 && f[0] == "*" { // all zeros block omitted + continue + } + addr64, err := strconv.ParseUint(f[0], 16, 0) + if err != nil { + return nil, fmt.Errorf("parsing hex dump: invalid address %q", f[0]) + } + addr := int(addr64) + if len(out) < addr { + out = append(out, make([]byte, addr-len(out))...) + } + for _, x := range f[1:] { + val, err := strconv.ParseUint(x, 16, 8) + if err != nil { + return nil, fmt.Errorf("parsing hexdump: invalid hex byte %q", x) + } + out = append(out, byte(val)) + } + } + return out, nil +} + +func hexdump(data []byte) string { + text := hex.Dump(data) + fmt.Sprintf("%08x\n", len(data)) + text = regexp.MustCompile(`\n([0-9a-f]+(\s+00){16}.*\n)+`).ReplaceAllString(text, "\n*\n") + return text +} diff --git a/src/cmd/newlink/layout.go b/src/cmd/newlink/layout.go new file mode 100644 index 0000000000..d5c291e255 --- /dev/null +++ b/src/cmd/newlink/layout.go @@ -0,0 +1,180 @@ +// Copyright 2014 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. + +// Executable image layout - address assignment. + +package main + +import ( + "cmd/internal/goobj" +) + +// A layoutSection describes a single section to add to the +// final executable. Go binaries only have a fixed set of possible +// sections, and the symbol kind determines the section. +type layoutSection struct { + Segment string + Section string + Kind goobj.SymKind + Index int +} + +// layout defines the layout of the generated Go executable. +// The order of entries here is the order in the executable. +// Entries with the same Segment name must be contiguous. +var layout = []layoutSection{ + {Segment: "text", Section: "text", Kind: goobj.STEXT}, + {Segment: "rodata", Section: "rodata", Kind: goobj.SRODATA}, + {Segment: "rodata", Section: "functab", Kind: goobj.SPCLNTAB}, + {Segment: "rodata", Section: "typelink", Kind: goobj.STYPELINK}, + {Segment: "data", Section: "noptrdata", Kind: goobj.SNOPTRDATA}, + {Segment: "data", Section: "data", Kind: goobj.SDATA}, + {Segment: "data", Section: "bss", Kind: goobj.SBSS}, + {Segment: "data", Section: "noptrbss", Kind: goobj.SNOPTRBSS}, + + // Later: + // {"rodata", "type", goobj.STYPE}, + // {"rodata", "string", goobj.SSTRING}, + // {"rodata", "gostring", goobj.SGOSTRING}, + // {"rodata", "gofunc", goobj.SGOFUNC}, +} + +// layoutByKind maps from SymKind to an entry in layout. +var layoutByKind []*layoutSection + +func init() { + // Build index from symbol type to layout entry. + max := 0 + for _, sect := range layout { + if max <= int(sect.Kind) { + max = int(sect.Kind) + 1 + } + } + layoutByKind = make([]*layoutSection, max) + for i := range layout { + sect := &layout[i] + layoutByKind[sect.Kind] = sect + sect.Index = i + } +} + +// layout arranges symbols into sections and sections into segments, +// and then it assigns addresses to segments, sections, and symbols. +func (p *Prog) layout() { + sections := make([]*Section, len(layout)) + + // Assign symbols to sections using index, creating sections as needed. + // Could keep sections separated by type during input instead. + for _, sym := range p.SymOrder { + kind := sym.Kind + if kind < 0 || int(kind) >= len(layoutByKind) || layoutByKind[kind] == nil { + p.errorf("%s: unexpected symbol kind %v", sym.SymID, kind) + continue + } + lsect := layoutByKind[kind] + sect := sections[lsect.Index] + if sect == nil { + sect = &Section{ + Name: lsect.Section, + Align: 1, + } + sections[lsect.Index] = sect + } + if sym.Data.Size > 0 || len(sym.Bytes) > 0 { + sect.InFile = true + } + sym.Section = sect + sect.Syms = append(sect.Syms, sym) + + // TODO(rsc): Incorporate alignment information. + // First that information needs to be added to the object files. + // + // if sect.Align < Addr(sym.Align) { + // sect.Align = Addr(sym.Align) + // } + } + + // Assign sections to segments, creating segments as needed. + var seg *Segment + for i, sect := range sections { + if sect == nil { + continue + } + segName := layout[i].Segment + + // Special case: Mach-O does not support "rodata" segment, + // so store read-only data in text segment. + if p.GOOS == "darwin" && segName == "rodata" { + segName = "text" + } + + if seg == nil || seg.Name != segName { + seg = &Segment{ + Name: segName, + } + p.Segments = append(p.Segments, seg) + } + sect.Segment = seg + seg.Sections = append(seg.Sections, sect) + } + + // Assign addresses. + + // TODO(rsc): This choice needs to be informed by both + // the formatter and the target architecture. + // And maybe eventually a command line flag (sigh). + const segAlign = 4096 + + // TODO(rsc): Use a larger amount on most systems, which will let the + // compiler eliminate more nil checks. + if p.UnmappedSize == 0 { + p.UnmappedSize = segAlign + } + + // TODO(rsc): addr := Addr(0) when generating a shared library or PIE. + addr := p.UnmappedSize + + // Account for initial file header. + hdrVirt, hdrFile := p.formatter.headerSize(p) + addr += hdrVirt + + // Assign addresses to segments, sections, symbols. + // Assign sizes to segments, sections. + startVirt := addr + startFile := hdrFile + for _, seg := range p.Segments { + addr = round(addr, segAlign) + seg.VirtAddr = addr + seg.FileOffset = startFile + seg.VirtAddr - startVirt + for _, sect := range seg.Sections { + addr = round(addr, sect.Align) + sect.VirtAddr = addr + for _, sym := range sect.Syms { + // TODO(rsc): Respect alignment once we have that information. + sym.Addr = addr + addr += Addr(sym.Size) + } + sect.Size = addr - sect.VirtAddr + if sect.InFile { + seg.FileSize = addr - seg.VirtAddr + } + } + seg.VirtSize = addr - seg.VirtAddr + } + + // Define symbols for section names. + var progEnd Addr + for i, sect := range sections { + name := layout[i].Section + var start, end Addr + if sect != nil { + start = sect.VirtAddr + end = sect.VirtAddr + sect.Size + } + p.defineConst("runtime."+name, start) + p.defineConst("runtime.e"+name, end) + progEnd = end + } + p.defineConst("runtime.end", progEnd) +} diff --git a/src/cmd/newlink/layout_test.go b/src/cmd/newlink/layout_test.go new file mode 100644 index 0000000000..e5513af35e --- /dev/null +++ b/src/cmd/newlink/layout_test.go @@ -0,0 +1,45 @@ +// Copyright 2014 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 ( + "bytes" + "strings" + "testing" +) + +func TestLayout(t *testing.T) { + p := Prog{GOOS: "darwin", GOARCH: "amd64", StartSym: "text_start"} + p.omitRuntime = true + p.Error = func(s string) { t.Error(s) } + var buf bytes.Buffer + const obj = "testdata/layout.6" + p.link(&buf, obj) + if p.NumError > 0 { + return // already reported + } + if len(p.Dead) > 0 { + t.Errorf("%s: unexpected dead symbols %v", obj, p.Dead) + return + } + + for _, sym := range p.SymOrder { + if p.isAuto(sym.SymID) { + continue + } + if sym.Section == nil { + t.Errorf("%s: symbol %s is missing section", obj, sym) + continue + } + i := strings.Index(sym.Name, "_") + if i < 0 { + t.Errorf("%s: unexpected symbol %s", obj, sym) + continue + } + if sym.Section.Name != sym.Name[:i] { + t.Errorf("%s: symbol %s in section %s, want %s", obj, sym, sym.Section.Name, sym.Name[:i]) + } + } +} diff --git a/src/cmd/newlink/link_test.go b/src/cmd/newlink/link_test.go new file mode 100644 index 0000000000..b5ae15fc71 --- /dev/null +++ b/src/cmd/newlink/link_test.go @@ -0,0 +1,35 @@ +// Copyright 2014 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 ( + "bytes" + "cmd/internal/goobj" + "io/ioutil" + "testing" +) + +func TestLinkHello(t *testing.T) { + p := &Prog{ + GOOS: "darwin", + GOARCH: "amd64", + Error: func(s string) { t.Error(s) }, + StartSym: "_rt0_go", + } + var buf bytes.Buffer + p.link(&buf, "testdata/hello.6") + if p.NumError > 0 { + return + } + if p.Syms[goobj.SymID{"_rt0_go", 0}] == nil || p.Syms[goobj.SymID{"hello", 1}] == nil { + t.Errorf("Syms = %v, want at least [_rt0_go hello<1>]", p.Syms) + } + + // uncomment to leave file behind for execution: + if false { + ioutil.WriteFile("a.out", buf.Bytes(), 0777) + } + checkGolden(t, buf.Bytes(), "testdata/link.hello.darwin.amd64") +} diff --git a/src/cmd/newlink/load.go b/src/cmd/newlink/load.go new file mode 100644 index 0000000000..50602b82a1 --- /dev/null +++ b/src/cmd/newlink/load.go @@ -0,0 +1,108 @@ +// Copyright 2014 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. + +// Loading of code and data fragments from package files into final image. + +package main + +import ( + "cmd/internal/obj" + "os" +) + +// load allocates segment images, populates them with data +// read from package files, and applies relocations to the data. +func (p *Prog) load() { + // TODO(rsc): mmap the output file and store the data directly. + // That will make writing the output file more efficient. + for _, seg := range p.Segments { + seg.Data = make([]byte, seg.FileSize) + } + for _, pkg := range p.Packages { + p.loadPackage(pkg) + } +} + +// loadPackage loads and relocates data for all the +// symbols needed in the given package. +func (p *Prog) loadPackage(pkg *Package) { + if pkg.File == "" { + // This "package" contains internally generated symbols only. + // All such symbols have a sym.Bytes field holding the actual data + // (if any), plus relocations. + for _, sym := range pkg.Syms { + if sym.Bytes == nil { + continue + } + seg := sym.Section.Segment + off := sym.Addr - seg.VirtAddr + data := seg.Data[off : off+Addr(sym.Size)] + copy(data, sym.Bytes) + p.relocateSym(sym, data) + } + return + } + + // Package stored in file. + f, err := os.Open(pkg.File) + if err != nil { + p.errorf("%v", err) + return + } + defer f.Close() + + // TODO(rsc): Mmap file into memory. + + for _, sym := range pkg.Syms { + if sym.Data.Size == 0 { + continue + } + // TODO(rsc): If not using mmap, at least coalesce nearby reads. + if sym.Section == nil { + p.errorf("internal error: missing section for %s", sym.Name) + } + seg := sym.Section.Segment + off := sym.Addr - seg.VirtAddr + if off >= Addr(len(seg.Data)) || off+Addr(sym.Data.Size) > Addr(len(seg.Data)) { + p.errorf("internal error: allocated space for %s too small: %d bytes for %d+%d (%d)", sym, len(seg.Data), off, sym.Data.Size, sym.Size) + } + data := seg.Data[off : off+Addr(sym.Data.Size)] + _, err := f.ReadAt(data, sym.Data.Offset) + if err != nil { + p.errorf("reading %v: %v", sym.SymID, err) + } + p.relocateSym(sym, data) + } +} + +// relocateSym applies relocations to sym's data. +func (p *Prog) relocateSym(sym *Sym, data []byte) { + for i := range sym.Reloc { + r := &sym.Reloc[i] + targ := p.Syms[r.Sym] + if targ == nil { + p.errorf("%v: reference to undefined symbol %v", sym, r.Sym) + continue + } + val := targ.Addr + Addr(r.Add) + switch r.Type { + default: + p.errorf("%v: unknown relocation type %d", sym, r.Type) + case obj.R_ADDR, obj.R_CALLIND: + // ok + case obj.R_PCREL, obj.R_CALL: + val -= sym.Addr + Addr(r.Offset+r.Size) + } + frag := data[r.Offset : r.Offset+r.Size] + switch r.Size { + default: + p.errorf("%v: unknown relocation size %d", sym, r.Size) + case 4: + // TODO(rsc): Check for overflow? + p.byteorder.PutUint32(frag, uint32(val)) + case 8: + p.byteorder.PutUint64(frag, uint64(val)) + } + } +} diff --git a/src/cmd/newlink/macho.go b/src/cmd/newlink/macho.go new file mode 100644 index 0000000000..4e5524be9b --- /dev/null +++ b/src/cmd/newlink/macho.go @@ -0,0 +1,380 @@ +// Copyright 2014 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. + +// Mach-O (Darwin) object file writing. + +package main + +import ( + "debug/macho" + "encoding/binary" + "io" + "strings" +) + +// machoFormat is the implementation of formatter. +type machoFormat struct{} + +// machoHeader and friends are data structures +// corresponding to the Mach-O file header +// to be written to disk. + +const ( + macho64Bit = 1 << 24 + machoSubCPU386 = 3 +) + +// machoArch describes a Mach-O target architecture. +type machoArch struct { + CPU uint32 + SubCPU uint32 +} + +// machoHeader is the Mach-O file header. +type machoHeader struct { + machoArch + FileType uint32 + Loads []*machoLoad + Segments []*machoSegment + p *Prog // for reporting errors +} + +// machoLoad is a Mach-O load command. +type machoLoad struct { + Type uint32 + Data []uint32 +} + +// machoSegment is a Mach-O segment. +type machoSegment struct { + Name string + VirtAddr Addr + VirtSize Addr + FileOffset Addr + FileSize Addr + Prot1 uint32 + Prot2 uint32 + Flags uint32 + Sections []*machoSection +} + +// machoSection is a Mach-O section, inside a segment. +type machoSection struct { + Name string + Segment string + Addr Addr + Size Addr + Offset uint32 + Align uint32 + Reloc uint32 + Nreloc uint32 + Flags uint32 + Res1 uint32 + Res2 uint32 +} + +// layout positions the segments and sections in p +// to make room for the Mach-O file header. +// That is, it edits their VirtAddr fields to adjust for the presence +// of the Mach-O header at the beginning of the address space. +func (machoFormat) headerSize(p *Prog) (virt, file Addr) { + var h machoHeader + h.init(p) + size := Addr(h.size()) + size = round(size, 4096) + p.HeaderSize = size + return size, size +} + +// write writes p to w as a Mach-O executable. +// layout(p) must have already been called, +// and the number, sizes, and addresses of the segments +// and sections must not have been modified since the call. +func (machoFormat) write(w io.Writer, p *Prog) { + var h machoHeader + h.init(p) + off := Addr(0) + enc := h.encode() + w.Write(enc) + off += Addr(len(enc)) + for _, seg := range p.Segments { + if seg.FileOffset < off { + h.p.errorf("mach-o error: invalid file offset") + } + w.Write(make([]byte, int(seg.FileOffset-off))) + if seg.FileSize != Addr(len(seg.Data)) { + h.p.errorf("mach-o error: invalid file size") + } + w.Write(seg.Data) + off = seg.FileOffset + Addr(len(seg.Data)) + } +} + +// Conversion of Prog to macho data structures. + +// machoArches maps from GOARCH to machoArch. +var machoArches = map[string]machoArch{ + "amd64": { + CPU: uint32(macho.CpuAmd64), + SubCPU: uint32(machoSubCPU386), + }, +} + +// init initializes the header h to describe p. +func (h *machoHeader) init(p *Prog) { + h.p = p + h.Segments = nil + h.Loads = nil + var ok bool + h.machoArch, ok = machoArches[p.GOARCH] + if !ok { + p.errorf("mach-o: unknown target GOARCH %q", p.GOARCH) + return + } + h.FileType = uint32(macho.TypeExec) + + mseg := h.addSegment(p, "__PAGEZERO", nil) + mseg.VirtSize = p.UnmappedSize + + for _, seg := range p.Segments { + h.addSegment(p, "__"+strings.ToUpper(seg.Name), seg) + } + + var data []uint32 + switch h.CPU { + default: + p.errorf("mach-o: unknown cpu %#x for GOARCH %q", h.CPU, p.GOARCH) + case uint32(macho.CpuAmd64): + data = make([]uint32, 2+42) + data[0] = 4 // thread type + data[1] = 42 // word count + data[2+32] = uint32(p.Entry) // RIP register, in two parts + data[2+32+1] = uint32(p.Entry >> 32) + } + + h.Loads = append(h.Loads, &machoLoad{ + Type: uint32(macho.LoadCmdUnixThread), + Data: data, + }) +} + +// addSegment adds to h a Mach-O segment like seg with the given name. +func (h *machoHeader) addSegment(p *Prog, name string, seg *Segment) *machoSegment { + mseg := &machoSegment{ + Name: name, + } + h.Segments = append(h.Segments, mseg) + if seg == nil { + return mseg + } + + mseg.VirtAddr = seg.VirtAddr + mseg.VirtSize = seg.VirtSize + mseg.FileOffset = round(seg.FileOffset, 4096) + mseg.FileSize = seg.FileSize + + if name == "__TEXT" { + // Initially RWX, then just RX + mseg.Prot1 = 7 + mseg.Prot2 = 5 + + // Text segment maps Mach-O header, needed by dynamic linker. + mseg.VirtAddr -= p.HeaderSize + mseg.VirtSize += p.HeaderSize + mseg.FileOffset -= p.HeaderSize + mseg.FileSize += p.HeaderSize + } else { + // RW + mseg.Prot1 = 3 + mseg.Prot2 = 3 + } + + for _, sect := range seg.Sections { + h.addSection(mseg, seg, sect) + } + return mseg +} + +// addSection adds to mseg a Mach-O section like sect, inside seg, with the given name. +func (h *machoHeader) addSection(mseg *machoSegment, seg *Segment, sect *Section) { + msect := &machoSection{ + Name: "__" + sect.Name, + Segment: mseg.Name, + // Reloc: sect.RelocOffset, + // NumReloc: sect.RelocLen / 8, + Addr: sect.VirtAddr, + Size: sect.Size, + } + mseg.Sections = append(mseg.Sections, msect) + + for 1< seg.FileSize-off { + h.p.errorf("mach-o error: section crosses file boundary") + } + msect.Offset = uint32(seg.FileOffset + off) + } else { + // Zero filled. + msect.Flags |= 1 + } + + if sect.Name == "text" { + msect.Flags |= 0x400 // contains executable instructions + } +} + +// A machoWriter helps write Mach-O headers. +// It is basically a buffer with some helper routines for writing integers. +type machoWriter struct { + dst []byte + tmp [8]byte + order binary.ByteOrder + is64 bool + p *Prog +} + +// if64 returns x if w is writing a 64-bit object file; otherwise it returns y. +func (w *machoWriter) if64(x, y interface{}) interface{} { + if w.is64 { + return x + } + return y +} + +// encode encodes each of the given arguments into the writer. +// It encodes uint32, []uint32, uint64, and []uint64 by writing each value +// in turn in the correct byte order for the output file. +// It encodes an Addr as a uint64 if writing a 64-bit output file, or else as a uint32. +// It encodes []byte and string by writing the raw bytes (no length prefix). +// It skips nil values in the args list. +func (w *machoWriter) encode(args ...interface{}) { + for _, arg := range args { + switch arg := arg.(type) { + default: + w.p.errorf("mach-o error: cannot encode %T", arg) + case nil: + // skip + case []byte: + w.dst = append(w.dst, arg...) + case string: + w.dst = append(w.dst, arg...) + case uint32: + w.order.PutUint32(w.tmp[:], arg) + w.dst = append(w.dst, w.tmp[:4]...) + case []uint32: + for _, x := range arg { + w.order.PutUint32(w.tmp[:], x) + w.dst = append(w.dst, w.tmp[:4]...) + } + case uint64: + w.order.PutUint64(w.tmp[:], arg) + w.dst = append(w.dst, w.tmp[:8]...) + case Addr: + if w.is64 { + w.order.PutUint64(w.tmp[:], uint64(arg)) + w.dst = append(w.dst, w.tmp[:8]...) + } else { + if Addr(uint32(arg)) != arg { + w.p.errorf("mach-o error: truncating address %#x to uint32", arg) + } + w.order.PutUint32(w.tmp[:], uint32(arg)) + w.dst = append(w.dst, w.tmp[:4]...) + } + } + } +} + +// segmentSize returns the size of the encoding of seg in bytes. +func (w *machoWriter) segmentSize(seg *machoSegment) int { + if w.is64 { + return 18*4 + 20*4*len(seg.Sections) + } + return 14*4 + 22*4*len(seg.Sections) +} + +// zeroPad returns the string s truncated or padded with NULs to n bytes. +func zeroPad(s string, n int) string { + if len(s) >= n { + return s[:n] + } + return s + strings.Repeat("\x00", n-len(s)) +} + +// size returns the encoded size of the header. +func (h *machoHeader) size() int { + // Could write separate code, but encoding is cheap; encode and throw it away. + return len(h.encode()) +} + +// encode returns the Mach-O encoding of the header. +func (h *machoHeader) encode() []byte { + w := &machoWriter{p: h.p} + w.is64 = h.CPU&macho64Bit != 0 + w.order = w.p.byteorder + + loadSize := 0 + for _, seg := range h.Segments { + loadSize += w.segmentSize(seg) + } + for _, l := range h.Loads { + loadSize += 4 * (2 + len(l.Data)) + } + + w.encode( + w.if64(macho.Magic64, macho.Magic32), + uint32(h.CPU), + uint32(h.SubCPU), + uint32(h.FileType), + uint32(len(h.Loads)+len(h.Segments)), + uint32(loadSize), + uint32(1), + w.if64(uint32(0), nil), + ) + + for _, seg := range h.Segments { + w.encode( + w.if64(uint32(macho.LoadCmdSegment64), uint32(macho.LoadCmdSegment)), + uint32(w.segmentSize(seg)), + zeroPad(seg.Name, 16), + seg.VirtAddr, + seg.VirtSize, + seg.FileOffset, + seg.FileSize, + seg.Prot1, + seg.Prot2, + uint32(len(seg.Sections)), + seg.Flags, + ) + for _, sect := range seg.Sections { + w.encode( + zeroPad(sect.Name, 16), + zeroPad(seg.Name, 16), + sect.Addr, + sect.Size, + sect.Offset, + sect.Align, + sect.Reloc, + sect.Nreloc, + sect.Flags, + sect.Res1, + sect.Res2, + w.if64(uint32(0), nil), + ) + } + } + + for _, load := range h.Loads { + w.encode( + load.Type, + uint32(4*(2+len(load.Data))), + load.Data, + ) + } + + return w.dst +} diff --git a/src/cmd/newlink/macho_test.go b/src/cmd/newlink/macho_test.go new file mode 100644 index 0000000000..37c4418b77 --- /dev/null +++ b/src/cmd/newlink/macho_test.go @@ -0,0 +1,407 @@ +// Copyright 2014 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 ( + "bytes" + "debug/macho" + "encoding/binary" + "fmt" + "io/ioutil" + "strings" + "testing" +) + +// Test macho writing by checking that each generated prog can be written +// and then read back using debug/macho to get the same prog. +// Also check against golden testdata file. +var machoWriteTests = []struct { + name string + golden bool + prog *Prog +}{ + // amd64 exit 9 + { + name: "exit9", + golden: true, + prog: &Prog{ + GOARCH: "amd64", + GOOS: "darwin", + UnmappedSize: 0x1000, + Entry: 0x1000, + Segments: []*Segment{ + { + Name: "text", + VirtAddr: 0x1000, + VirtSize: 13, + FileOffset: 0, + FileSize: 13, + Data: []byte{ + 0xb8, 0x01, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX + 0xbf, 0x09, 0x00, 0x00, 0x00, // MOVL $9, DI + 0x0f, 0x05, // SYSCALL + 0xf4, // HLT + }, + Sections: []*Section{ + { + Name: "text", + VirtAddr: 0x1000, + Size: 13, + Align: 64, + }, + }, + }, + }, + }, + }, + + // amd64 write hello world & exit 9 + { + name: "hello", + golden: true, + prog: &Prog{ + GOARCH: "amd64", + GOOS: "darwin", + UnmappedSize: 0x1000, + Entry: 0x1000, + Segments: []*Segment{ + { + Name: "text", + VirtAddr: 0x1000, + VirtSize: 35, + FileOffset: 0, + FileSize: 35, + Data: []byte{ + 0xb8, 0x04, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX + 0xbf, 0x01, 0x00, 0x00, 0x00, // MOVL $1, DI + 0xbe, 0x00, 0x30, 0x00, 0x00, // MOVL $0x3000, SI + 0xba, 0x0c, 0x00, 0x00, 0x00, // MOVL $12, DX + 0x0f, 0x05, // SYSCALL + 0xb8, 0x01, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX + 0xbf, 0x09, 0x00, 0x00, 0x00, // MOVL $9, DI + 0x0f, 0x05, // SYSCALL + 0xf4, // HLT + }, + Sections: []*Section{ + { + Name: "text", + VirtAddr: 0x1000, + Size: 35, + Align: 64, + }, + }, + }, + { + Name: "data", + VirtAddr: 0x2000, + VirtSize: 12, + FileOffset: 0x1000, + FileSize: 12, + Data: []byte("hello world\n"), + Sections: []*Section{ + { + Name: "data", + VirtAddr: 0x2000, + Size: 12, + Align: 64, + }, + }, + }, + }, + }, + }, + + // amd64 write hello world from rodata & exit 0 + { + name: "helloro", + golden: true, + prog: &Prog{ + GOARCH: "amd64", + GOOS: "darwin", + UnmappedSize: 0x1000, + Entry: 0x1000, + Segments: []*Segment{ + { + Name: "text", + VirtAddr: 0x1000, + VirtSize: 0x100c, + FileOffset: 0, + FileSize: 0x100c, + Data: concat( + []byte{ + 0xb8, 0x04, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX + 0xbf, 0x01, 0x00, 0x00, 0x00, // MOVL $1, DI + 0xbe, 0x00, 0x30, 0x00, 0x00, // MOVL $0x3000, SI + 0xba, 0x0c, 0x00, 0x00, 0x00, // MOVL $12, DX + 0x0f, 0x05, // SYSCALL + 0xb8, 0x01, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX + 0xbf, 0x00, 0x00, 0x00, 0x00, // MOVL $0, DI + 0x0f, 0x05, // SYSCALL + 0xf4, // HLT + }, + make([]byte, 0x1000-35), + []byte("hello world\n"), + ), + Sections: []*Section{ + { + Name: "text", + VirtAddr: 0x1000, + Size: 35, + Align: 64, + }, + { + Name: "rodata", + VirtAddr: 0x2000, + Size: 12, + Align: 64, + }, + }, + }, + }, + }, + }, +} + +func concat(xs ...[]byte) []byte { + var out []byte + for _, x := range xs { + out = append(out, x...) + } + return out +} + +func TestMachoWrite(t *testing.T) { + for _, tt := range machoWriteTests { + name := tt.prog.GOARCH + "." + tt.name + prog := cloneProg(tt.prog) + prog.init() + var f machoFormat + vsize, fsize := f.headerSize(prog) + shiftProg(prog, vsize, fsize) + var buf bytes.Buffer + f.write(&buf, prog) + if false { // enable to debug + ioutil.WriteFile("a.out", buf.Bytes(), 0777) + } + read, err := machoRead(machoArches[tt.prog.GOARCH], buf.Bytes()) + if err != nil { + t.Errorf("%s: reading mach-o output:\n\t%v", name, err) + continue + } + diffs := diffProg(read, prog) + if diffs != nil { + t.Errorf("%s: mismatched prog:\n\t%s", name, strings.Join(diffs, "\n\t")) + continue + } + if !tt.golden { + continue + } + checkGolden(t, buf.Bytes(), "testdata/macho."+name) + } +} + +// machoRead reads the mach-o file in data and returns a corresponding prog. +func machoRead(arch machoArch, data []byte) (*Prog, error) { + f, err := macho.NewFile(bytes.NewReader(data)) + if err != nil { + return nil, err + } + + var errors []string + errorf := func(format string, args ...interface{}) { + errors = append(errors, fmt.Sprintf(format, args...)) + } + + magic := uint32(0xFEEDFACE) + if arch.CPU&macho64Bit != 0 { + magic |= 1 + } + if f.Magic != magic { + errorf("header: Magic = %#x, want %#x", f.Magic, magic) + } + if f.Cpu != macho.CpuAmd64 { + errorf("header: CPU = %#x, want %#x", f.Cpu, macho.CpuAmd64) + } + if f.SubCpu != 3 { + errorf("header: SubCPU = %#x, want %#x", f.SubCpu, 3) + } + if f.Type != 2 { + errorf("header: FileType = %d, want %d", f.Type, 2) + } + if f.Flags != 1 { + errorf("header: Flags = %d, want %d", f.Flags, 1) + } + + msects := f.Sections + var limit uint64 + prog := new(Prog) + for _, load := range f.Loads { + switch load := load.(type) { + default: + errorf("unexpected macho load %T %x", load, load.Raw()) + + case macho.LoadBytes: + if len(load) < 8 || len(load)%4 != 0 { + errorf("unexpected load length %d", len(load)) + continue + } + cmd := f.ByteOrder.Uint32(load) + switch macho.LoadCmd(cmd) { + default: + errorf("unexpected macho load cmd %s", macho.LoadCmd(cmd)) + case macho.LoadCmdUnixThread: + data := make([]uint32, len(load[8:])/4) + binary.Read(bytes.NewReader(load[8:]), f.ByteOrder, data) + if len(data) != 44 { + errorf("macho thread len(data) = %d, want 42", len(data)) + continue + } + if data[0] != 4 { + errorf("macho thread type = %d, want 4", data[0]) + } + if data[1] != uint32(len(data))-2 { + errorf("macho thread desc len = %d, want %d", data[1], uint32(len(data))-2) + continue + } + for i, val := range data[2:] { + switch i { + default: + if val != 0 { + errorf("macho thread data[%d] = %#x, want 0", i, val) + } + case 32: + prog.Entry = Addr(val) + case 33: + prog.Entry |= Addr(val) << 32 + } + } + } + + case *macho.Segment: + if load.Addr < limit { + errorf("segments out of order: %q at %#x after %#x", load.Name, load.Addr, limit) + } + limit = load.Addr + load.Memsz + if load.Name == "__PAGEZERO" || load.Addr == 0 && load.Filesz == 0 { + if load.Name != "__PAGEZERO" { + errorf("segment with Addr=0, Filesz=0 is named %q, want %q", load.Name, "__PAGEZERO") + } else if load.Addr != 0 || load.Filesz != 0 { + errorf("segment %q has Addr=%#x, Filesz=%d, want Addr=%#x, Filesz=%d", load.Name, load.Addr, load.Filesz, 0, 0) + } + prog.UnmappedSize = Addr(load.Memsz) + continue + } + + if !strings.HasPrefix(load.Name, "__") { + errorf("segment name %q does not begin with %q", load.Name, "__") + } + if strings.ToUpper(load.Name) != load.Name { + errorf("segment name %q is not all upper case", load.Name) + } + + seg := &Segment{ + Name: strings.ToLower(strings.TrimPrefix(load.Name, "__")), + VirtAddr: Addr(load.Addr), + VirtSize: Addr(load.Memsz), + FileOffset: Addr(load.Offset), + FileSize: Addr(load.Filesz), + } + prog.Segments = append(prog.Segments, seg) + + data, err := load.Data() + if err != nil { + errorf("loading data from %q: %v", load.Name, err) + } + seg.Data = data + + var maxprot, prot uint32 + if load.Name == "__TEXT" { + maxprot, prot = 7, 5 + } else { + maxprot, prot = 3, 3 + } + if load.Maxprot != maxprot || load.Prot != prot { + errorf("segment %q protection is %d, %d, want %d, %d", + load.Name, load.Maxprot, load.Prot, maxprot, prot) + } + + for len(msects) > 0 && msects[0].Addr < load.Addr+load.Memsz { + msect := msects[0] + msects = msects[1:] + + if msect.Offset > 0 && prog.HeaderSize == 0 { + prog.HeaderSize = Addr(msect.Offset) + if seg.FileOffset != 0 { + errorf("initial segment %q does not map header", load.Name) + } + seg.VirtAddr += prog.HeaderSize + seg.VirtSize -= prog.HeaderSize + seg.FileOffset += prog.HeaderSize + seg.FileSize -= prog.HeaderSize + seg.Data = seg.Data[prog.HeaderSize:] + } + + if msect.Addr < load.Addr { + errorf("section %q at address %#x is missing segment", msect.Name, msect.Addr) + continue + } + + if !strings.HasPrefix(msect.Name, "__") { + errorf("section name %q does not begin with %q", msect.Name, "__") + } + if strings.ToLower(msect.Name) != msect.Name { + errorf("section name %q is not all lower case", msect.Name) + } + if msect.Seg != load.Name { + errorf("section %q is lists segment name %q, want %q", + msect.Name, msect.Seg, load.Name) + } + if uint64(msect.Offset) != uint64(load.Offset)+msect.Addr-load.Addr { + errorf("section %q file offset is %#x, want %#x", + msect.Name, msect.Offset, load.Offset+msect.Addr-load.Addr) + } + if msect.Reloff != 0 || msect.Nreloc != 0 { + errorf("section %q has reloff %d,%d, want %d,%d", + msect.Name, msect.Reloff, msect.Nreloc, 0, 0) + } + flags := uint32(0) + if msect.Name == "__text" { + flags = 0x400 + } + if msect.Offset == 0 { + flags = 1 + } + if msect.Flags != flags { + errorf("section %q flags = %#x, want %#x", msect.Name, msect.Flags, flags) + } + sect := &Section{ + Name: strings.ToLower(strings.TrimPrefix(msect.Name, "__")), + VirtAddr: Addr(msect.Addr), + Size: Addr(msect.Size), + Align: 1 << msect.Align, + } + seg.Sections = append(seg.Sections, sect) + } + } + } + + for _, msect := range msects { + errorf("section %q has no segment", msect.Name) + } + + limit = 0 + for _, msect := range f.Sections { + if msect.Addr < limit { + errorf("sections out of order: %q at %#x after %#x", msect.Name, msect.Addr, limit) + } + limit = msect.Addr + msect.Size + } + + err = nil + if errors != nil { + err = fmt.Errorf("%s", strings.Join(errors, "\n\t")) + } + return prog, err +} diff --git a/src/cmd/newlink/main.go b/src/cmd/newlink/main.go new file mode 100644 index 0000000000..b23f3f87b0 --- /dev/null +++ b/src/cmd/newlink/main.go @@ -0,0 +1,9 @@ +// Copyright 2014 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. + +// Placeholder to keep build building. + +package main + +func main() {} diff --git a/src/cmd/newlink/pclntab.go b/src/cmd/newlink/pclntab.go new file mode 100644 index 0000000000..7f8bf52a9b --- /dev/null +++ b/src/cmd/newlink/pclntab.go @@ -0,0 +1,480 @@ +// Copyright 2014 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. + +// Generation of runtime function information (pclntab). + +package main + +import ( + "cmd/internal/goobj" + "cmd/internal/obj" + "encoding/binary" + "os" + "sort" +) + +var zerofunc goobj.Func + +// pclntab collects the runtime function data for each function that will +// be listed in the binary and builds a single table describing all functions. +// This table is used at run time for stack traces and to look up PC-specific +// information during garbage collection. The symbol created is named +// "pclntab" for historical reasons; the scope of the table has grown to +// include more than just PC/line number correspondences. +// The table format is documented at http://golang.org/s/go12symtab. +func (p *Prog) pclntab() { + // Count number of functions going into the binary, + // so that we can size the initial index correctly. + nfunc := 0 + for _, sym := range p.SymOrder { + if sym.Kind != goobj.STEXT { + continue + } + nfunc++ + } + + // Table header. + buf := new(SymBuffer) + buf.Init(p) + buf.SetSize(8 + p.ptrsize) + off := 0 + off = buf.Uint32(off, 0xfffffffb) + off = buf.Uint8(off, 0) + off = buf.Uint8(off, 0) + off = buf.Uint8(off, uint8(p.pcquantum)) + off = buf.Uint8(off, uint8(p.ptrsize)) + off = buf.Uint(off, uint64(nfunc), p.ptrsize) + indexOff := off + off += (nfunc*2 + 1) * p.ptrsize // function index, to be filled in + off += 4 // file table start offset, to be filled in + buf.SetSize(off) + + // One-file cache for reading PCData tables from package files. + // TODO(rsc): Better I/O strategy. + var ( + file *os.File + fname string + ) + + // Files gives the file numbering for source file names recorded + // in the binary. + files := make(map[string]int) + + // Build the table, build the index, and build the file name numbering. + // The loop here must visit functions in the same order that they will + // be stored in the binary, or else binary search over the index will fail. + // The runtime checks that the index is sorted properly at program start time. + var lastSym *Sym + for _, sym := range p.SymOrder { + if sym.Kind != goobj.STEXT { + continue + } + lastSym = sym + + // Treat no recorded function information same as all zeros. + f := sym.Func + if f == nil { + f = &zerofunc + } + + // Open package file if needed, for reading PC data. + if fname != sym.Package.File { + if file != nil { + file.Close() + } + var err error + file, err = os.Open(sym.Package.File) + if err != nil { + p.errorf("%v: %v", sym, err) + return + } + fname = sym.Package.File + } + + // off is the offset of the table entry where we're going to write + // the encoded form of Func. + // indexOff is the current position in the table index; + // we add an entry in the index pointing at off. + off = (buf.Size() + p.ptrsize - 1) &^ (p.ptrsize - 1) + indexOff = buf.Addr(indexOff, sym.SymID, 0) + indexOff = buf.Uint(indexOff, uint64(off), p.ptrsize) + + // The Func encoding starts with a header giving offsets + // to data blobs, and then the data blobs themselves. + // end gives the current write position for the data blobs. + end := off + p.ptrsize + 3*4 + 5*4 + len(f.PCData)*4 + len(f.FuncData)*p.ptrsize + if len(f.FuncData) > 0 { + end += -end & (p.ptrsize - 1) + } + buf.SetSize(end) + + // entry uintptr + // name int32 + // args int32 + // frame int32 + // + // The frame recorded in the object file is + // the frame size used in an assembly listing, which does + // not include the caller PC on the stack. + // The frame size we want to list here is the delta from + // this function's SP to its caller's SP, which does include + // the caller PC. Add p.ptrsize to f.Frame to adjust. + // TODO(rsc): Record the same frame size in the object file. + off = buf.Addr(off, sym.SymID, 0) + off = buf.Uint32(off, uint32(addString(buf, sym.Name))) + off = buf.Uint32(off, uint32(f.Args)) + off = buf.Uint32(off, uint32(f.Frame+p.ptrsize)) + + // pcdata + off = buf.Uint32(off, uint32(addPCTable(p, buf, file, f.PCSP))) + off = buf.Uint32(off, uint32(addPCFileTable(p, buf, file, f.PCFile, sym, files))) + off = buf.Uint32(off, uint32(addPCTable(p, buf, file, f.PCLine))) + off = buf.Uint32(off, uint32(len(f.PCData))) + off = buf.Uint32(off, uint32(len(f.FuncData))) + for _, pcdata := range f.PCData { + off = buf.Uint32(off, uint32(addPCTable(p, buf, file, pcdata))) + } + + // funcdata + if len(f.FuncData) > 0 { + off += -off & (p.ptrsize - 1) // must be pointer-aligned + for _, funcdata := range f.FuncData { + if funcdata.Sym.Name == "" { + off = buf.Uint(off, uint64(funcdata.Offset), p.ptrsize) + } else { + off = buf.Addr(off, funcdata.Sym, funcdata.Offset) + } + } + } + + if off != end { + p.errorf("internal error: invalid math in pclntab: off=%#x end=%#x", off, end) + break + } + } + if file != nil { + file.Close() + } + + // Final entry of index is end PC of last function. + indexOff = buf.Addr(indexOff, lastSym.SymID, int64(lastSym.Size)) + + // Start file table. + // Function index is immediately followed by offset to file table. + off = (buf.Size() + p.ptrsize - 1) &^ (p.ptrsize - 1) + buf.Uint32(indexOff, uint32(off)) + + // File table is an array of uint32s. + // The first entry gives 1+n, the size of the array. + // The following n entries hold offsets to string data. + // File number n uses the string pointed at by entry n. + // File number 0 is invalid. + buf.SetSize(off + (1+len(files))*4) + buf.Uint32(off, uint32(1+len(files))) + var filestr []string + for file := range files { + filestr = append(filestr, file) + } + sort.Strings(filestr) + for _, file := range filestr { + id := files[file] + buf.Uint32(off+4*id, uint32(addString(buf, file))) + } + + pclntab := &Sym{ + Sym: &goobj.Sym{ + SymID: goobj.SymID{Name: "runtime.pclntab"}, + Kind: goobj.SPCLNTAB, + Size: buf.Size(), + Reloc: buf.Reloc(), + }, + Bytes: buf.Bytes(), + } + p.addSym(pclntab) +} + +// addString appends the string s to the buffer b. +// It returns the offset of the beginning of the string in the buffer. +func addString(b *SymBuffer, s string) int { + off := b.Size() + b.SetSize(off + len(s) + 1) + copy(b.data[off:], s) + return off +} + +// addPCTable appends the PC-data table stored in the file f at the location loc +// to the symbol buffer b. It returns the offset of the beginning of the table +// in the buffer. +func addPCTable(p *Prog, b *SymBuffer, f *os.File, loc goobj.Data) int { + if loc.Size == 0 { + return 0 + } + off := b.Size() + b.SetSize(off + int(loc.Size)) + _, err := f.ReadAt(b.data[off:off+int(loc.Size)], loc.Offset) + if err != nil { + p.errorf("%v", err) + } + return off +} + +// addPCFileTable is like addPCTable, but it renumbers the file names referred to by the table +// to use the global numbering maintained in the files map. It adds new files to the +// map as necessary. +func addPCFileTable(p *Prog, b *SymBuffer, f *os.File, loc goobj.Data, sym *Sym, files map[string]int) int { + if loc.Size == 0 { + return 0 + } + off := b.Size() + + src := make([]byte, loc.Size) + _, err := f.ReadAt(src, loc.Offset) + if err != nil { + p.errorf("%v", err) + return 0 + } + + filenum := make([]int, len(sym.Func.File)) + for i, name := range sym.Func.File { + num := files[name] + if num == 0 { + num = len(files) + 1 + files[name] = num + } + filenum[i] = num + } + + var dst []byte + newval := int32(-1) + var it PCIter + for it.Init(p, src); !it.Done; it.Next() { + // value delta + oldval := it.Value + val := oldval + if oldval != -1 { + if oldval < 0 || int(oldval) >= len(filenum) { + p.errorf("%s: corrupt pc-file table", sym) + break + } + val = int32(filenum[oldval]) + } + dv := val - newval + newval = val + uv := uint32(dv<<1) ^ uint32(dv>>31) + dst = appendVarint(dst, uv) + + // pc delta + dst = appendVarint(dst, it.NextPC-it.PC) + } + if it.Corrupt { + p.errorf("%s: corrupt pc-file table", sym) + } + + // terminating value delta + dst = appendVarint(dst, 0) + + b.SetSize(off + len(dst)) + copy(b.data[off:], dst) + return off +} + +// A SymBuffer is a buffer for preparing the data image of a +// linker-generated symbol. +type SymBuffer struct { + data []byte + reloc []goobj.Reloc + order binary.ByteOrder + ptrsize int +} + +// Init initializes the buffer for writing. +func (b *SymBuffer) Init(p *Prog) { + b.data = nil + b.reloc = nil + b.order = p.byteorder + b.ptrsize = p.ptrsize +} + +// Bytes returns the buffer data. +func (b *SymBuffer) Bytes() []byte { + return b.data +} + +// SetSize sets the buffer's data size to n bytes. +func (b *SymBuffer) SetSize(n int) { + for cap(b.data) < n { + b.data = append(b.data[:cap(b.data)], 0) + } + b.data = b.data[:n] +} + +// Size returns the buffer's data size. +func (b *SymBuffer) Size() int { + return len(b.data) +} + +// Reloc returns the buffered relocations. +func (b *SymBuffer) Reloc() []goobj.Reloc { + return b.reloc +} + +// Uint8 sets the uint8 at offset off to v. +// It returns the offset just beyond v. +func (b *SymBuffer) Uint8(off int, v uint8) int { + b.data[off] = v + return off + 1 +} + +// Uint16 sets the uint16 at offset off to v. +// It returns the offset just beyond v. +func (b *SymBuffer) Uint16(off int, v uint16) int { + b.order.PutUint16(b.data[off:], v) + return off + 2 +} + +// Uint32 sets the uint32 at offset off to v. +// It returns the offset just beyond v. +func (b *SymBuffer) Uint32(off int, v uint32) int { + b.order.PutUint32(b.data[off:], v) + return off + 4 +} + +// Uint64 sets the uint64 at offset off to v. +// It returns the offset just beyond v. +func (b *SymBuffer) Uint64(off int, v uint64) int { + b.order.PutUint64(b.data[off:], v) + return off + 8 +} + +// Uint sets the size-byte unsigned integer at offset off to v. +// It returns the offset just beyond v. +func (b *SymBuffer) Uint(off int, v uint64, size int) int { + switch size { + case 1: + return b.Uint8(off, uint8(v)) + case 2: + return b.Uint16(off, uint16(v)) + case 4: + return b.Uint32(off, uint32(v)) + case 8: + return b.Uint64(off, v) + } + panic("invalid use of SymBuffer.SetUint") +} + +// Addr sets the pointer-sized address at offset off to refer +// to symoff bytes past the start of sym. It returns the offset +// just beyond the address. +func (b *SymBuffer) Addr(off int, sym goobj.SymID, symoff int64) int { + b.reloc = append(b.reloc, goobj.Reloc{ + Offset: off, + Size: b.ptrsize, + Sym: sym, + Add: int(symoff), + Type: obj.R_ADDR, + }) + return off + b.ptrsize +} + +// A PCIter implements iteration over PC-data tables. +// +// var it PCIter +// for it.Init(p, data); !it.Done; it.Next() { +// it.Value holds from it.PC up to (but not including) it.NextPC +// } +// if it.Corrupt { +// data was malformed +// } +// +type PCIter struct { + PC uint32 + NextPC uint32 + Value int32 + Done bool + Corrupt bool + p []byte + start bool + pcquantum uint32 +} + +// Init initializes the iteration. +// On return, if it.Done is true, the iteration is over. +// Otherwise it.Value applies in the pc range [it.PC, it.NextPC). +func (it *PCIter) Init(p *Prog, buf []byte) { + it.p = buf + it.PC = 0 + it.NextPC = 0 + it.Value = -1 + it.start = true + it.pcquantum = uint32(p.pcquantum) + it.Done = false + it.Next() +} + +// Next steps forward one entry in the table. +// On return, if it.Done is true, the iteration is over. +// Otherwise it.Value applies in the pc range [it.PC, it.NextPC). +func (it *PCIter) Next() { + it.PC = it.NextPC + if it.Done { + return + } + if len(it.p) == 0 { + it.Done = true + return + } + + // value delta + uv, p, ok := decodeVarint(it.p) + if !ok { + it.Done = true + it.Corrupt = true + return + } + it.p = p + if uv == 0 && !it.start { + it.Done = true + return + } + it.start = false + sv := int32(uv>>1) ^ int32(uv<<31)>>31 + it.Value += sv + + // pc delta + uv, it.p, ok = decodeVarint(it.p) + if !ok { + it.Done = true + it.Corrupt = true + return + } + it.NextPC = it.PC + uv*it.pcquantum +} + +// decodeVarint decodes an unsigned varint from p, +// reporting the value, the remainder of the data, and +// whether the decoding was successful. +func decodeVarint(p []byte) (v uint32, rest []byte, ok bool) { + for shift := uint(0); ; shift += 7 { + if len(p) == 0 { + return + } + c := uint32(p[0]) + p = p[1:] + v |= (c & 0x7F) << shift + if c&0x80 == 0 { + break + } + } + return v, p, true +} + +// appendVarint appends an unsigned varint encoding of v to p +// and returns the resulting slice. +func appendVarint(p []byte, v uint32) []byte { + for ; v >= 0x80; v >>= 7 { + p = append(p, byte(v)|0x80) + } + p = append(p, byte(v)) + return p +} diff --git a/src/cmd/newlink/pclntab_test.go b/src/cmd/newlink/pclntab_test.go new file mode 100644 index 0000000000..ea80806742 --- /dev/null +++ b/src/cmd/newlink/pclntab_test.go @@ -0,0 +1,340 @@ +// Copyright 2014 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 ( + "bytes" + "cmd/internal/goobj" + "fmt" + "math/rand" + "sort" + "strings" + "testing" +) + +// Test of pcln table encoding. +// testdata/genpcln.go generates an assembly file with +// pseudorandom values for the data that pclntab stores. +// This test recomputes the same pseudorandom stream +// and checks that the final linked binary uses those values +// as well. +func TestPclntab(t *testing.T) { + p := &Prog{ + GOOS: "darwin", + GOARCH: "amd64", + Error: func(s string) { t.Error(s) }, + StartSym: "start", + omitRuntime: true, + } + var buf bytes.Buffer + p.link(&buf, "testdata/pclntab.6") + if p.NumError > 0 { + return + } + + // The algorithm for computing values here must match + // the one in testdata/genpcln.go. + for f := 0; f < 3; f++ { + file := "input" + line := 1 + rnd := rand.New(rand.NewSource(int64(f))) + args := rnd.Intn(100) * 8 + frame := 32 + rnd.Intn(32)/8*8 + size := 200 + rnd.Intn(100)*8 + + name := fmt.Sprintf("func%d", f) + r, off, fargs, fframe, ok := findFunc(t, p, name) + if !ok { + continue // error already printed + } + if fargs != args { + t.Errorf("%s: args=%d, want %d", name, fargs, args) + } + if fframe != frame+8 { + t.Errorf("%s: frame=%d, want %d", name, fframe, frame+8) + } + + // Check FUNCDATA 1. + fdata, ok := loadFuncdata(t, r, name, off, 1) + if ok { + fsym := p.Syms[goobj.SymID{Name: fmt.Sprintf("funcdata%d", f)}] + if fsym == nil { + t.Errorf("funcdata%d is missing in binary", f) + } else if fdata != fsym.Addr { + t.Errorf("%s: funcdata 1 = %#x, want %#x", name, fdata, fsym.Addr) + } + } + + // Walk code checking pcdata values. + spadj := 0 + pcdata1 := -1 + pcdata2 := -1 + + checkPCSP(t, r, name, off, 0, 0) + checkPCData(t, r, name, off, 0, 0, -1) + checkPCData(t, r, name, off, 0, 1, -1) + checkPCData(t, r, name, off, 0, 2, -1) + + firstpc := 4 + for i := 0; i < size; i++ { + pc := firstpc + i // skip SP adjustment to allocate frame + if i >= 0x100 && t.Failed() { + break + } + // Possible SP adjustment. + checkPCSP(t, r, name, off, pc, frame+spadj) + if rnd.Intn(100) == 0 { + checkPCFileLine(t, r, name, off, pc, file, line) + checkPCData(t, r, name, off, pc, 1, pcdata1) + checkPCData(t, r, name, off, pc, 2, pcdata2) + i += 1 + pc = firstpc + i + checkPCFileLine(t, r, name, off, pc-1, file, line) + checkPCData(t, r, name, off, pc-1, 1, pcdata1) + checkPCData(t, r, name, off, pc-1, 2, pcdata2) + checkPCSP(t, r, name, off, pc-1, frame+spadj) + + if spadj <= -32 || spadj < 32 && rnd.Intn(2) == 0 { + spadj += 8 + } else { + spadj -= 8 + } + checkPCSP(t, r, name, off, pc, frame+spadj) + } + + // Possible PCFile change. + if rnd.Intn(100) == 0 { + file = fmt.Sprintf("file%d.s", rnd.Intn(10)) + line = rnd.Intn(100) + 1 + } + + // Possible PCLine change. + if rnd.Intn(10) == 0 { + line = rnd.Intn(1000) + 1 + } + + // Possible PCData $1 change. + if rnd.Intn(100) == 0 { + pcdata1 = rnd.Intn(1000) + } + + // Possible PCData $2 change. + if rnd.Intn(100) == 0 { + pcdata2 = rnd.Intn(1000) + } + + if i == 0 { + checkPCFileLine(t, r, name, off, 0, file, line) + checkPCFileLine(t, r, name, off, pc-1, file, line) + } + checkPCFileLine(t, r, name, off, pc, file, line) + checkPCData(t, r, name, off, pc, 1, pcdata1) + checkPCData(t, r, name, off, pc, 2, pcdata2) + } + } +} + +// findFunc finds the function information in the pclntab of p +// for the function with the given name. +// It returns a symbol reader for pclntab, the offset of the function information +// within that symbol, and the args and frame values read out of the information. +func findFunc(t *testing.T, p *Prog, name string) (r *SymReader, off, args, frame int, ok bool) { + tabsym := p.Syms[goobj.SymID{Name: "runtime.pclntab"}] + if tabsym == nil { + t.Errorf("pclntab is missing in binary") + return + } + + r = new(SymReader) + r.Init(p, tabsym) + + // pclntab must with 8-byte header + if r.Uint32(0) != 0xfffffffb || r.Uint8(4) != 0 || r.Uint8(5) != 0 || r.Uint8(6) != uint8(p.pcquantum) || r.Uint8(7) != uint8(p.ptrsize) { + t.Errorf("pclntab has incorrect header %.8x", r.data[:8]) + return + } + + sym := p.Syms[goobj.SymID{Name: name}] + if sym == nil { + t.Errorf("%s is missing in the binary", name) + return + } + + // index is nfunc addr0 off0 addr1 off1 ... addr_nfunc (sentinel) + nfunc := int(r.Addr(8)) + i := sort.Search(nfunc, func(i int) bool { + return r.Addr(8+p.ptrsize*(1+2*i)) >= sym.Addr + }) + if entry := r.Addr(8 + p.ptrsize*(1+2*i)); entry != sym.Addr { + indexTab := make([]Addr, 2*nfunc+1) + for j := range indexTab { + indexTab[j] = r.Addr(8 + p.ptrsize*(1+j)) + } + t.Errorf("pclntab is missing entry for %s (%#x): %#x", name, sym.Addr, indexTab) + return + } + + off = int(r.Addr(8 + p.ptrsize*(1+2*i+1))) + + // func description at off is + // entry addr + // nameoff uint32 + // args uint32 + // frame uint32 + // pcspoff uint32 + // pcfileoff uint32 + // pclineoff uint32 + // npcdata uint32 + // nfuncdata uint32 + // pcdata npcdata*uint32 + // funcdata nfuncdata*addr + // + if entry := r.Addr(off); entry != sym.Addr { + t.Errorf("pclntab inconsistent: entry for %s addr=%#x has entry=%#x", name, sym.Addr, entry) + return + } + nameoff := int(r.Uint32(off + p.ptrsize)) + args = int(r.Uint32(off + p.ptrsize + 1*4)) + frame = int(r.Uint32(off + p.ptrsize + 2*4)) + + fname := r.String(nameoff) + if fname != name { + t.Errorf("pclntab inconsistent: entry for %s addr=%#x has name %q", name, sym.Addr, fname) + } + + ok = true // off, args, frame are usable + return +} + +// loadFuncdata returns the funcdata #fnum value +// loaded from the function information for name. +func loadFuncdata(t *testing.T, r *SymReader, name string, off int, fnum int) (Addr, bool) { + npcdata := int(r.Uint32(off + r.p.ptrsize + 6*4)) + nfuncdata := int(r.Uint32(off + r.p.ptrsize + 7*4)) + if fnum >= nfuncdata { + t.Errorf("pclntab(%s): no funcdata %d (only < %d)", name, fnum, nfuncdata) + return 0, false + } + fdataoff := off + r.p.ptrsize + (8+npcdata)*4 + fnum*r.p.ptrsize + fdataoff += fdataoff & 4 + return r.Addr(fdataoff), true +} + +// checkPCSP checks that the PCSP table in the function information at off +// lists spadj as the sp delta for pc. +func checkPCSP(t *testing.T, r *SymReader, name string, off, pc, spadj int) { + pcoff := r.Uint32(off + r.p.ptrsize + 3*4) + pcval, ok := readPCData(t, r, name, "PCSP", pcoff, pc) + if !ok { + return + } + if pcval != spadj { + t.Errorf("pclntab(%s): at pc=+%#x, pcsp=%d, want %d", name, pc, pcval, spadj) + } +} + +// checkPCSP checks that the PCFile and PCLine tables in the function information at off +// list file, line as the file name and line number for pc. +func checkPCFileLine(t *testing.T, r *SymReader, name string, off, pc int, file string, line int) { + pcfileoff := r.Uint32(off + r.p.ptrsize + 4*4) + pclineoff := r.Uint32(off + r.p.ptrsize + 5*4) + pcfilenum, ok1 := readPCData(t, r, name, "PCFile", pcfileoff, pc) + pcline, ok2 := readPCData(t, r, name, "PCLine", pclineoff, pc) + if !ok1 || !ok2 { + return + } + nfunc := int(r.Addr(8)) + filetaboff := r.Uint32(8 + r.p.ptrsize*2*(nfunc+1)) + nfile := int(r.Uint32(int(filetaboff))) + if pcfilenum <= 0 || pcfilenum >= nfile { + t.Errorf("pclntab(%s): at pc=+%#x, filenum=%d (invalid; nfile=%d)", name, pc, pcfilenum, nfile) + } + pcfile := r.String(int(r.Uint32(int(filetaboff) + pcfilenum*4))) + if !strings.HasSuffix(pcfile, file) { + t.Errorf("pclntab(%s): at pc=+%#x, file=%q, want %q", name, pc, pcfile, file) + } + if pcline != line { + t.Errorf("pclntab(%s): at pc=+%#x, line=%d, want %d", name, pc, pcline, line) + } +} + +// checkPCData checks that the PCData#pnum table in the function information at off +// list val as the value for pc. +func checkPCData(t *testing.T, r *SymReader, name string, off, pc, pnum, val int) { + pcoff := r.Uint32(off + r.p.ptrsize + (8+pnum)*4) + pcval, ok := readPCData(t, r, name, fmt.Sprintf("PCData#%d", pnum), pcoff, pc) + if !ok { + return + } + if pcval != val { + t.Errorf("pclntab(%s): at pc=+%#x, pcdata#%d=%d, want %d", name, pc, pnum, pcval, val) + } +} + +// readPCData reads the PCData table offset off +// to obtain and return the value associated with pc. +func readPCData(t *testing.T, r *SymReader, name, pcdataname string, pcoff uint32, pc int) (int, bool) { + // "If pcsp, pcfile, pcln, or any of the pcdata offsets is zero, + // that table is considered missing, and all PCs take value -1." + if pcoff == 0 { + return -1, true + } + + var it PCIter + for it.Init(r.p, r.data[pcoff:]); !it.Done; it.Next() { + if it.PC <= uint32(pc) && uint32(pc) < it.NextPC { + return int(it.Value), true + } + } + if it.Corrupt { + t.Errorf("pclntab(%s): %s: corrupt pcdata table", name, pcdataname) + } + return 0, false +} + +// A SymReader provides typed access to the data for a symbol. +type SymReader struct { + p *Prog + data []byte +} + +func (r *SymReader) Init(p *Prog, sym *Sym) { + seg := sym.Section.Segment + off := sym.Addr - seg.VirtAddr + data := seg.Data[off : off+Addr(sym.Size)] + r.p = p + r.data = data +} + +func (r *SymReader) Uint8(off int) uint8 { + return r.data[off] +} + +func (r *SymReader) Uint16(off int) uint16 { + return r.p.byteorder.Uint16(r.data[off:]) +} + +func (r *SymReader) Uint32(off int) uint32 { + return r.p.byteorder.Uint32(r.data[off:]) +} + +func (r *SymReader) Uint64(off int) uint64 { + return r.p.byteorder.Uint64(r.data[off:]) +} + +func (r *SymReader) Addr(off int) Addr { + if r.p.ptrsize == 4 { + return Addr(r.Uint32(off)) + } + return Addr(r.Uint64(off)) +} + +func (r *SymReader) String(off int) string { + end := off + for r.data[end] != '\x00' { + end++ + } + return string(r.data[off:end]) +} diff --git a/src/cmd/newlink/prog.go b/src/cmd/newlink/prog.go new file mode 100644 index 0000000000..77fb1ece5b --- /dev/null +++ b/src/cmd/newlink/prog.go @@ -0,0 +1,220 @@ +// Copyright 2014 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/goobj" + "encoding/binary" + "fmt" + "go/build" + "io" + "os" + "runtime" +) + +// A Prog holds state for constructing an executable (program) image. +// +// The usual sequence of operations on a Prog is: +// +// p.init() +// p.scan(file) +// p.dead() +// p.runtime() +// p.layout() +// p.load() +// p.debug() +// p.write(w) +// +// p.init is in this file. The rest of the methods are in files +// named for the method. The convenience method p.link runs +// this sequence. +// +type Prog struct { + // Context + GOOS string // target operating system + GOARCH string // target architecture + Format string // desired file format ("elf", "macho", ...) + Error func(string) // called to report an error (if set) + NumError int // number of errors printed + StartSym string + + // Derived context + arch + formatter formatter + startSym goobj.SymID + pkgdir string + omitRuntime bool // do not load runtime package + + // Input + Packages map[string]*Package // loaded packages, by import path + Syms map[goobj.SymID]*Sym // defined symbols, by symbol ID + Missing map[goobj.SymID]bool // missing symbols + Dead map[goobj.SymID]bool // symbols removed as dead + SymOrder []*Sym // order syms were scanned + MaxVersion int // max SymID.Version, for generating fresh symbol IDs + + // Output + UnmappedSize Addr // size of unmapped region at address 0 + HeaderSize Addr // size of object file header + Entry Addr // virtual address where execution begins + Segments []*Segment // loaded memory segments +} + +// An arch describes architecture-dependent settings. +type arch struct { + byteorder binary.ByteOrder + ptrsize int + pcquantum int +} + +// A formatter takes care of the details of generating a particular +// kind of executable file. +type formatter interface { + // headerSize returns the footprint of the header for p + // in both virtual address space and file bytes. + // The footprint does not include any bytes stored at the + // end of the file. + headerSize(p *Prog) (virt, file Addr) + + // write writes the executable file for p to w. + write(w io.Writer, p *Prog) +} + +// An Addr represents a virtual memory address, a file address, or a size. +// It must be a uint64, not a uintptr, so that a 32-bit linker can still generate a 64-bit binary. +// It must be unsigned in order to link programs placed at very large start addresses. +// Math involving Addrs must be checked carefully not to require negative numbers. +type Addr uint64 + +// A Package is a Go package loaded from a file. +type Package struct { + *goobj.Package // table of contents + File string // file name for reopening + Syms []*Sym // symbols defined by this package +} + +// A Sym is a symbol defined in a loaded package. +type Sym struct { + *goobj.Sym // symbol metadata from package file + Package *Package // package defining symbol + Section *Section // section where symbol is placed in output program + Addr Addr // virtual address of symbol in output program + Bytes []byte // symbol data, for internally defined symbols +} + +// A Segment is a loaded memory segment. +// A Prog is expected to have segments named "text" and optionally "data", +// in that order, before any other segments. +type Segment struct { + Name string // name of segment: "text", "data", ... + VirtAddr Addr // virtual memory address of segment base + VirtSize Addr // size of segment in memory + FileOffset Addr // file offset of segment base + FileSize Addr // size of segment in file; can be less than VirtSize + Sections []*Section // sections inside segment + Data []byte // raw data of segment image +} + +// A Section is part of a loaded memory segment. +type Section struct { + Name string // name of section: "text", "rodata", "noptrbss", and so on + VirtAddr Addr // virtual memory address of section base + Size Addr // size of section in memory + Align Addr // required alignment + InFile bool // section has image data in file (like data, unlike bss) + Syms []*Sym // symbols stored in section + Segment *Segment // segment containing section +} + +func (p *Prog) errorf(format string, args ...interface{}) { + if p.Error != nil { + p.Error(fmt.Sprintf(format, args...)) + } else { + fmt.Fprintf(os.Stderr, format+"\n", args...) + } + p.NumError++ +} + +// link is the one-stop convenience method for running a link. +// It writes to w the object file generated from using mainFile as the main package. +func (p *Prog) link(w io.Writer, mainFile string) { + p.init() + p.scan(mainFile) + if p.NumError > 0 { + return + } + p.dead() + p.runtime() + p.autoData() + p.layout() + p.autoConst() + if p.NumError > 0 { + return + } + p.load() + if p.NumError > 0 { + return + } + p.debug() + if p.NumError > 0 { + return + } + p.write(w) +} + +// init initializes p for use by the other methods. +func (p *Prog) init() { + // Set default context if not overridden. + if p.GOOS == "" { + p.GOOS = build.Default.GOOS + } + if p.GOARCH == "" { + p.GOARCH = build.Default.GOARCH + } + if p.Format == "" { + p.Format = goosFormat[p.GOOS] + if p.Format == "" { + p.errorf("no default file format for GOOS %q", p.GOOS) + return + } + } + if p.StartSym == "" { + p.StartSym = fmt.Sprintf("_rt0_%s_%s", p.GOARCH, p.GOOS) + } + + // Derive internal context. + p.formatter = formatters[p.Format] + if p.formatter == nil { + p.errorf("unknown output file format %q", p.Format) + return + } + p.startSym = goobj.SymID{Name: p.StartSym} + arch, ok := arches[p.GOARCH] + if !ok { + p.errorf("unknown GOOS %q", p.GOOS) + return + } + p.arch = arch + + p.pkgdir = fmt.Sprintf("%s/pkg/%s_%s", runtime.GOROOT(), p.GOOS, p.GOARCH) +} + +// goosFormat records the default format for each known GOOS value. +var goosFormat = map[string]string{ + "darwin": "darwin", +} + +// formatters records the format implementation for each known format value. +var formatters = map[string]formatter{ + "darwin": machoFormat{}, +} + +var arches = map[string]arch{ + "amd64": { + byteorder: binary.LittleEndian, + ptrsize: 8, + pcquantum: 1, + }, +} diff --git a/src/cmd/newlink/prog_test.go b/src/cmd/newlink/prog_test.go new file mode 100644 index 0000000000..0853506957 --- /dev/null +++ b/src/cmd/newlink/prog_test.go @@ -0,0 +1,163 @@ +// Copyright 2014 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 ( + "bytes" + "fmt" + "io/ioutil" + "testing" +) + +// shiftProg adjusts the addresses in p. +// It adds vdelta to all virtual addresses and fdelta to all file offsets. +func shiftProg(p *Prog, vdelta Addr, fdelta Addr) { + p.Entry += vdelta + for _, seg := range p.Segments { + seg.FileOffset += fdelta + seg.VirtAddr += vdelta + for _, sect := range seg.Sections { + sect.VirtAddr += vdelta + for _, sym := range sect.Syms { + sym.Addr += vdelta + } + } + } +} + +// diffProg returns a list of differences between p and q, +// assuming p is being checked and q is the correct answer. +func diffProg(p, q *Prog) []string { + var errors []string + if p.UnmappedSize != q.UnmappedSize { + errors = append(errors, fmt.Sprintf("p.UnmappedSize = %#x, want %#x", p.UnmappedSize, q.UnmappedSize)) + } + if p.HeaderSize != q.HeaderSize { + errors = append(errors, fmt.Sprintf("p.HeaderSize = %#x, want %#x", p.HeaderSize, q.HeaderSize)) + } + if p.Entry != q.Entry { + errors = append(errors, fmt.Sprintf("p.Entry = %#x, want %#x", p.Entry, q.Entry)) + } + for i := 0; i < len(p.Segments) || i < len(q.Segments); i++ { + if i >= len(p.Segments) { + errors = append(errors, fmt.Sprintf("p missing segment %q", q.Segments[i].Name)) + continue + } + if i >= len(q.Segments) { + errors = append(errors, fmt.Sprintf("p has extra segment %q", p.Segments[i].Name)) + continue + } + pseg := p.Segments[i] + qseg := q.Segments[i] + if pseg.Name != qseg.Name { + errors = append(errors, fmt.Sprintf("segment %d Name = %q, want %q", i, pseg.Name, qseg.Name)) + continue // probably out of sync + } + if pseg.VirtAddr != qseg.VirtAddr { + errors = append(errors, fmt.Sprintf("segment %q VirtAddr = %#x, want %#x", pseg.Name, pseg.VirtAddr, qseg.VirtAddr)) + } + if pseg.VirtSize != qseg.VirtSize { + errors = append(errors, fmt.Sprintf("segment %q VirtSize = %#x, want %#x", pseg.Name, pseg.VirtSize, qseg.VirtSize)) + } + if pseg.FileOffset != qseg.FileOffset { + errors = append(errors, fmt.Sprintf("segment %q FileOffset = %#x, want %#x", pseg.Name, pseg.FileOffset, qseg.FileOffset)) + } + if pseg.FileSize != qseg.FileSize { + errors = append(errors, fmt.Sprintf("segment %q FileSize = %#x, want %#x", pseg.Name, pseg.FileSize, qseg.FileSize)) + } + if len(pseg.Data) != len(qseg.Data) { + errors = append(errors, fmt.Sprintf("segment %q len(Data) = %d, want %d", pseg.Name, len(pseg.Data), len(qseg.Data))) + } else if !bytes.Equal(pseg.Data, qseg.Data) { + errors = append(errors, fmt.Sprintf("segment %q Data mismatch:\n\thave %x\n\twant %x", pseg.Name, pseg.Data, qseg.Data)) + } + + for j := 0; j < len(pseg.Sections) || j < len(qseg.Sections); j++ { + if j >= len(pseg.Sections) { + errors = append(errors, fmt.Sprintf("segment %q missing section %q", pseg.Name, qseg.Sections[i].Name)) + continue + } + if j >= len(qseg.Sections) { + errors = append(errors, fmt.Sprintf("segment %q has extra section %q", pseg.Name, pseg.Sections[i].Name)) + continue + } + psect := pseg.Sections[j] + qsect := qseg.Sections[j] + if psect.Name != qsect.Name { + errors = append(errors, fmt.Sprintf("segment %q, section %d Name = %q, want %q", pseg.Name, j, psect.Name, qsect.Name)) + continue // probably out of sync + } + + if psect.VirtAddr != qsect.VirtAddr { + errors = append(errors, fmt.Sprintf("segment %q section %q VirtAddr = %#x, want %#x", pseg.Name, psect.Name, psect.VirtAddr, qsect.VirtAddr)) + } + if psect.Size != qsect.Size { + errors = append(errors, fmt.Sprintf("segment %q section %q Size = %#x, want %#x", pseg.Name, psect.Name, psect.Size, qsect.Size)) + } + if psect.Align != qsect.Align { + errors = append(errors, fmt.Sprintf("segment %q section %q Align = %#x, want %#x", pseg.Name, psect.Name, psect.Align, qsect.Align)) + } + } + } + + return errors +} + +// cloneProg returns a deep copy of p. +func cloneProg(p *Prog) *Prog { + q := new(Prog) + *q = *p + q.Segments = make([]*Segment, len(p.Segments)) + for i, seg := range p.Segments { + q.Segments[i] = cloneSegment(seg) + } + return q +} + +// cloneSegment returns a deep copy of seg. +func cloneSegment(seg *Segment) *Segment { + t := new(Segment) + *t = *seg + t.Sections = make([]*Section, len(seg.Sections)) + for i, sect := range seg.Sections { + t.Sections[i] = cloneSection(sect) + } + t.Data = make([]byte, len(seg.Data)) + copy(t.Data, seg.Data) + return t +} + +// cloneSection returns a deep copy of section. +func cloneSection(sect *Section) *Section { + // At the moment, there's nothing we need to make a deep copy of. + t := new(Section) + *t = *sect + return t +} + +const saveMismatch = true + +// checkGolden checks that data matches the named file. +// If not, it reports the error to the test. +func checkGolden(t *testing.T, data []byte, name string) { + golden := mustParseHexdumpFile(t, name) + if !bytes.Equal(data, golden) { + if saveMismatch { + ioutil.WriteFile(name+".raw", data, 0666) + ioutil.WriteFile(name+".hex", []byte(hexdump(data)), 0666) + } + // TODO(rsc): A better diff would be nice, as needed. + i := 0 + for i < len(data) && i < len(golden) && data[i] == golden[i] { + i++ + } + if i >= len(data) { + t.Errorf("%s: output file shorter than expected: have %d bytes, want %d", name, len(data), len(golden)) + } else if i >= len(golden) { + t.Errorf("%s: output file larger than expected: have %d bytes, want %d", name, len(data), len(golden)) + } else { + t.Errorf("%s: output file differs at byte %d: have %#02x, want %#02x", name, i, data[i], golden[i]) + } + } +} diff --git a/src/cmd/newlink/runtime.go b/src/cmd/newlink/runtime.go new file mode 100644 index 0000000000..acda2d24d6 --- /dev/null +++ b/src/cmd/newlink/runtime.go @@ -0,0 +1,28 @@ +// Copyright 2014 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. + +// Generation of runtime-accessible data structures. +// See also debug.go. + +package main + +import "cmd/internal/goobj" + +func (p *Prog) runtime() { + p.pclntab() + + // TODO: Implement garbage collection data. + p.addSym(&Sym{ + Sym: &goobj.Sym{ + SymID: goobj.SymID{Name: "runtime.gcdata"}, + Kind: goobj.SRODATA, + }, + }) + p.addSym(&Sym{ + Sym: &goobj.Sym{ + SymID: goobj.SymID{Name: "runtime.gcbss"}, + Kind: goobj.SRODATA, + }, + }) +} diff --git a/src/cmd/newlink/scan.go b/src/cmd/newlink/scan.go new file mode 100644 index 0000000000..7feb0d8900 --- /dev/null +++ b/src/cmd/newlink/scan.go @@ -0,0 +1,187 @@ +// Copyright 2014 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. + +// Initial scan of packages making up a program. + +// TODO(rsc): Rename goobj.SymID.Version to StaticID to avoid confusion with the ELF meaning of version. +// TODO(rsc): Fix file format so that SBSS/SNOPTRBSS with data is listed as SDATA/SNOPTRDATA. +// TODO(rsc): Parallelize scan to overlap file i/o where possible. + +package main + +import ( + "cmd/internal/goobj" + "os" + "sort" + "strings" +) + +// scan scans all packages making up the program, starting with package main defined in mainfile. +func (p *Prog) scan(mainfile string) { + p.initScan() + p.scanFile("main", mainfile) + if len(p.Missing) > 0 && !p.omitRuntime { + p.scanImport("runtime") + } + + var missing []string + for sym := range p.Missing { + if !p.isAuto(sym) { + missing = append(missing, sym.String()) + } + } + + if missing != nil { + sort.Strings(missing) + for _, sym := range missing { + p.errorf("undefined: %s", sym) + } + } + + // TODO(rsc): Walk import graph to diagnose cycles. +} + +// initScan initializes the Prog fields needed by scan. +func (p *Prog) initScan() { + p.Packages = make(map[string]*Package) + p.Syms = make(map[goobj.SymID]*Sym) + p.Missing = make(map[goobj.SymID]bool) + p.Missing[p.startSym] = true +} + +// scanFile reads file to learn about the package with the given import path. +func (p *Prog) scanFile(pkgpath string, file string) { + pkg := &Package{ + File: file, + } + p.Packages[pkgpath] = pkg + + f, err := os.Open(file) + if err != nil { + p.errorf("%v", err) + return + } + gp, err := goobj.Parse(f, pkgpath) + f.Close() + if err != nil { + p.errorf("reading %s: %v", file, err) + return + } + + // TODO(rsc): Change cmd/internal/goobj to record package name as gp.Name. + // TODO(rsc): If pkgpath == "main", check that gp.Name == "main". + + pkg.Package = gp + + for _, gs := range gp.Syms { + // TODO(rsc): Fix file format instead of this workaround. + if gs.Data.Size > 0 { + switch gs.Kind { + case goobj.SBSS: + gs.Kind = goobj.SDATA + case goobj.SNOPTRBSS: + gs.Kind = goobj.SNOPTRDATA + } + } + + if gs.Version != 0 { + gs.Version += p.MaxVersion + } + for i := range gs.Reloc { + r := &gs.Reloc[i] + if r.Sym.Version != 0 { + r.Sym.Version += p.MaxVersion + } + if p.Syms[r.Sym] == nil { + p.Missing[r.Sym] = true + } + } + if gs.Func != nil { + for i := range gs.Func.FuncData { + fdata := &gs.Func.FuncData[i] + if fdata.Sym.Name != "" { + if fdata.Sym.Version != 0 { + fdata.Sym.Version += p.MaxVersion + } + if p.Syms[fdata.Sym] == nil { + p.Missing[fdata.Sym] = true + } + } + } + } + if old := p.Syms[gs.SymID]; old != nil { + // Duplicate definition of symbol. Is it okay? + // TODO(rsc): Write test for this code. + switch { + // If both symbols are BSS (no data), take max of sizes + // but otherwise ignore second symbol. + case old.Data.Size == 0 && gs.Data.Size == 0: + if old.Size < gs.Size { + old.Size = gs.Size + } + continue + + // If one is in BSS and one is not, use the one that is not. + case old.Data.Size > 0 && gs.Data.Size == 0: + continue + case gs.Data.Size > 0 && old.Data.Size == 0: + break // install gs as new symbol below + + // If either is marked as DupOK, we can keep either one. + // Keep the one that we saw first. + case old.DupOK || gs.DupOK: + continue + + // Otherwise, there's an actual conflict: + default: + p.errorf("symbol %s defined in both %s and %s %v %v", gs.SymID, old.Package.File, file, old.Data, gs.Data) + continue + } + } + s := &Sym{ + Sym: gs, + Package: pkg, + } + p.addSym(s) + delete(p.Missing, gs.SymID) + + if s.Data.Size > int64(s.Size) { + p.errorf("%s: initialized data larger than symbol (%d > %d)", s, s.Data.Size, s.Size) + } + } + p.MaxVersion += pkg.MaxVersion + + for i, pkgpath := range pkg.Imports { + // TODO(rsc): Fix file format to drop .a from recorded import path. + pkgpath = strings.TrimSuffix(pkgpath, ".a") + pkg.Imports[i] = pkgpath + + p.scanImport(pkgpath) + } +} + +func (p *Prog) addSym(s *Sym) { + pkg := s.Package + if pkg == nil { + pkg = p.Packages[""] + if pkg == nil { + pkg = &Package{} + p.Packages[""] = pkg + } + s.Package = pkg + } + pkg.Syms = append(pkg.Syms, s) + p.Syms[s.SymID] = s + p.SymOrder = append(p.SymOrder, s) +} + +// scanImport finds the object file for the given import path and then scans it. +func (p *Prog) scanImport(pkgpath string) { + if p.Packages[pkgpath] != nil { + return // already loaded + } + + // TODO(rsc): Implement correct search to find file. + p.scanFile(pkgpath, p.pkgdir+"/"+pkgpath+".a") +} diff --git a/src/cmd/newlink/testdata/Makefile b/src/cmd/newlink/testdata/Makefile new file mode 100644 index 0000000000..83e8a05195 --- /dev/null +++ b/src/cmd/newlink/testdata/Makefile @@ -0,0 +1,15 @@ +ALL=\ + autosection.6\ + autoweak.6\ + dead.6\ + hello.6\ + layout.6\ + pclntab.6\ + +all: $(ALL) + +%.6: %.s + GOARCH=amd64 GOOS=darwin go tool asm -I $(shell go env GOROOT)/pkg/include -trimpath=$(shell pwd) $*.s + +pclntab.s: genpcln.go + go run genpcln.go >pclntab.s diff --git a/src/cmd/newlink/testdata/autosection.6 b/src/cmd/newlink/testdata/autosection.6 new file mode 100644 index 0000000000..34aa5d3dbb Binary files /dev/null and b/src/cmd/newlink/testdata/autosection.6 differ diff --git a/src/cmd/newlink/testdata/autosection.s b/src/cmd/newlink/testdata/autosection.s new file mode 100644 index 0000000000..e0cb21723e --- /dev/null +++ b/src/cmd/newlink/testdata/autosection.s @@ -0,0 +1,60 @@ +// Copyright 2014 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 of section-named symbols. + +#include "textflag.h" + +TEXT start(SB),7,$0 + MOVQ $autotab(SB),AX + MOVQ $autoptr(SB),AX + RET + +GLOBL zero(SB), $8 + +GLOBL zeronoptr(SB), NOPTR, $16 + +// text +DATA autotab+0x00(SB)/8, $runtime·text(SB) +DATA autotab+0x08(SB)/8, $start(SB) +DATA autotab+0x10(SB)/8, $runtime·etext(SB) +DATA autotab+0x18(SB)/8, $start+16(SB) + +// data +DATA autotab+0x20(SB)/8, $runtime·data(SB) +DATA autotab+0x28(SB)/8, $autotab(SB) +DATA autotab+0x30(SB)/8, $runtime·edata(SB) +DATA autotab+0x38(SB)/8, $nonzero+4(SB) + +// bss +DATA autotab+0x40(SB)/8, $runtime·bss(SB) +DATA autotab+0x48(SB)/8, $zero(SB) +DATA autotab+0x50(SB)/8, $runtime·ebss(SB) +DATA autotab+0x58(SB)/8, $zero+8(SB) + +// noptrdata +DATA autotab+0x60(SB)/8, $runtime·noptrdata(SB) +DATA autotab+0x68(SB)/8, $nonzeronoptr(SB) +DATA autotab+0x70(SB)/8, $runtime·enoptrdata(SB) +DATA autotab+0x78(SB)/8, $nonzeronoptr+8(SB) + +// noptrbss +DATA autotab+0x80(SB)/8, $runtime·noptrbss(SB) +DATA autotab+0x88(SB)/8, $zeronoptr(SB) +DATA autotab+0x90(SB)/8, $runtime·enoptrbss(SB) +DATA autotab+0x98(SB)/8, $zeronoptr+16(SB) + +// end +DATA autotab+0xa0(SB)/8, $runtime·end(SB) +DATA autotab+0xa8(SB)/8, $zeronoptr+16(SB) + +GLOBL autotab(SB), $0xb0 + +DATA nonzero(SB)/4, $1 +GLOBL nonzero(SB), $4 + +DATA nonzeronoptr(SB)/8, $2 +GLOBL nonzeronoptr(SB), NOPTR, $8 + +GLOBL autoptr(SB), $0 diff --git a/src/cmd/newlink/testdata/autoweak.6 b/src/cmd/newlink/testdata/autoweak.6 new file mode 100644 index 0000000000..a86b841171 Binary files /dev/null and b/src/cmd/newlink/testdata/autoweak.6 differ diff --git a/src/cmd/newlink/testdata/autoweak.s b/src/cmd/newlink/testdata/autoweak.s new file mode 100644 index 0000000000..2d11330db2 --- /dev/null +++ b/src/cmd/newlink/testdata/autoweak.s @@ -0,0 +1,30 @@ +// Copyright 2014 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 of go.weak symbols. + +TEXT start(SB),7,$0 + MOVQ $autotab(SB),AX + MOVQ $autoptr(SB),AX + RET + +// go.weak.sym should resolve to sym, because sym is in the binary. +DATA autotab+0(SB)/8, $go·weak·sym(SB) +DATA autotab+8(SB)/8, $sym(SB) + +// go.weak.missingsym should resolve to 0, because missingsym is not in the binary. +DATA autotab+16(SB)/8, $go·weak·missingsym(SB) +DATA autotab+24(SB)/8, $0 + +// go.weak.deadsym should resolve to 0, because deadsym is discarded during dead code removal +DATA autotab+32(SB)/8, $go·weak·deadsym(SB) +DATA autotab+40(SB)/8, $0 + +GLOBL autotab(SB), $48 + +GLOBL sym(SB), $1 + +GLOBL deadsym(SB), $1 + +GLOBL autoptr(SB), $0 diff --git a/src/cmd/newlink/testdata/dead.6 b/src/cmd/newlink/testdata/dead.6 new file mode 100644 index 0000000000..5e9b09c03e Binary files /dev/null and b/src/cmd/newlink/testdata/dead.6 differ diff --git a/src/cmd/newlink/testdata/dead.s b/src/cmd/newlink/testdata/dead.s new file mode 100644 index 0000000000..86f31360fa --- /dev/null +++ b/src/cmd/newlink/testdata/dead.s @@ -0,0 +1,48 @@ +// Copyright 2014 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 of dead code removal. +// Symbols with names beginning with dead_ should be discarded. +// Others should be kept. + +TEXT start(SB),7,$0 // start symbol + MOVQ $data1<>(SB), AX + CALL text1(SB) + MOVQ $text2(SB), BX + RET + +TEXT text1(SB),7,$0 + FUNCDATA $1, funcdata+4(SB) + RET + +TEXT text2(SB),7,$0 + MOVQ $runtime·edata(SB),BX + RET + +DATA data1<>+0(SB)/8, $data2(SB) +DATA data1<>+8(SB)/8, $data3(SB) +GLOBL data1<>(SB), $16 +GLOBL data2(SB), $1 +GLOBL data3(SB), $1 +GLOBL funcdata(SB), $8 + +TEXT dead_start(SB),7,$0 + MOVQ $dead_data1(SB), AX + CALL dead_text1(SB) + MOVQ $dead_text2(SB), BX + RET + +TEXT dead_text1(SB),7,$0 + FUNCDATA $1, dead_funcdata+4(SB) + RET + +TEXT dead_text2(SB),7,$0 + RET + +DATA dead_data1+0(SB)/8, $dead_data2(SB) +DATA dead_data1+8(SB)/8, $dead_data3(SB) +GLOBL dead_data1(SB), $16 +GLOBL dead_data2(SB), $1 +GLOBL dead_data3(SB), $1 +GLOBL dead_funcdata(SB), $8 diff --git a/src/cmd/newlink/testdata/genpcln.go b/src/cmd/newlink/testdata/genpcln.go new file mode 100644 index 0000000000..c10eaeae91 --- /dev/null +++ b/src/cmd/newlink/testdata/genpcln.go @@ -0,0 +1,112 @@ +// Copyright 2014 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. + +// This program generates a .s file using a pseudorandom +// value stream for the runtime function data. +// The pclntab test checks that the linked copy +// still has the same pseudorandom value stream. + +package main + +import ( + "fmt" + "math/rand" +) + +func main() { + fmt.Printf("// generated by genpcln.go; do not edit\n\n") + for f := 0; f < 3; f++ { + r := rand.New(rand.NewSource(int64(f))) + file := "input" + line := 1 + args := r.Intn(100) * 8 + frame := 32 + r.Intn(32)/8*8 + fmt.Printf("#line %d %q\n", line, file) + fmt.Printf("TEXT func%d(SB),7,$%d-%d\n", f, frame, args) + fmt.Printf("\tFUNCDATA $1, funcdata%d(SB)\n", f) + fmt.Printf("#line %d %q\n", line, file) + size := 200 + r.Intn(100)*8 + spadj := 0 + flushed := 0 + firstpc := 4 + flush := func(i int) { + for i-flushed >= 10 { + fmt.Printf("#line %d %q\n", line, file) + fmt.Printf("/*%#04x*/\tMOVQ $0x123456789, AX\n", firstpc+flushed) + flushed += 10 + } + for i-flushed >= 5 { + fmt.Printf("#line %d %q\n", line, file) + fmt.Printf("/*%#04x*/\tMOVL $0x1234567, AX\n", firstpc+flushed) + flushed += 5 + } + for i-flushed > 0 { + fmt.Printf("#line %d %q\n", line, file) + fmt.Printf("/*%#04x*/\tBYTE $0\n", firstpc+flushed) + flushed++ + } + } + for i := 0; i < size; i++ { + // Possible SP adjustment. + if r.Intn(100) == 0 { + flush(i) + fmt.Printf("#line %d %q\n", line, file) + if spadj <= -32 || spadj < 32 && r.Intn(2) == 0 { + spadj += 8 + fmt.Printf("/*%#04x*/\tPUSHQ AX\n", firstpc+i) + } else { + spadj -= 8 + fmt.Printf("/*%#04x*/\tPOPQ AX\n", firstpc+i) + } + i += 1 + flushed = i + } + + // Possible PCFile change. + if r.Intn(100) == 0 { + flush(i) + file = fmt.Sprintf("file%d.s", r.Intn(10)) + line = r.Intn(100) + 1 + } + + // Possible PCLine change. + if r.Intn(10) == 0 { + flush(i) + line = r.Intn(1000) + 1 + } + + // Possible PCData $1 change. + if r.Intn(100) == 0 { + flush(i) + fmt.Printf("/*%6s*/\tPCDATA $1, $%d\n", "", r.Intn(1000)) + } + + // Possible PCData $2 change. + if r.Intn(100) == 0 { + flush(i) + fmt.Printf("/*%6s*/\tPCDATA $2, $%d\n", "", r.Intn(1000)) + } + } + flush(size) + for spadj < 0 { + fmt.Printf("\tPUSHQ AX\n") + spadj += 8 + } + for spadj > 0 { + fmt.Printf("\tPOPQ AX\n") + spadj -= 8 + } + fmt.Printf("\tRET\n") + + fmt.Printf("\n") + fmt.Printf("GLOBL funcdata%d(SB), $16\n", f) + } + + fmt.Printf("\nTEXT start(SB),7,$0\n") + for f := 0; f < 3; f++ { + fmt.Printf("\tCALL func%d(SB)\n", f) + } + fmt.Printf("\tMOVQ $runtime·pclntab(SB), AX\n") + fmt.Printf("\n\tRET\n") +} diff --git a/src/cmd/newlink/testdata/hello.6 b/src/cmd/newlink/testdata/hello.6 new file mode 100644 index 0000000000..890cbbd016 Binary files /dev/null and b/src/cmd/newlink/testdata/hello.6 differ diff --git a/src/cmd/newlink/testdata/hello.s b/src/cmd/newlink/testdata/hello.s new file mode 100644 index 0000000000..32ed675033 --- /dev/null +++ b/src/cmd/newlink/testdata/hello.s @@ -0,0 +1,15 @@ +TEXT _rt0_go(SB),7,$0 + MOVL $1, DI + MOVL $hello<>(SB), SI + MOVL $12, DX + MOVL $0x2000004, AX + SYSCALL + MOVL $0, DI + MOVL $0x2000001, AX + SYSCALL + RET + +DATA hello<>+0(SB)/4, $"hell" +DATA hello<>+4(SB)/4, $"o wo" +DATA hello<>+8(SB)/4, $"rld\n" +GLOBL hello<>(SB), $12 diff --git a/src/cmd/newlink/testdata/layout.6 b/src/cmd/newlink/testdata/layout.6 new file mode 100644 index 0000000000..748c836255 Binary files /dev/null and b/src/cmd/newlink/testdata/layout.6 differ diff --git a/src/cmd/newlink/testdata/layout.s b/src/cmd/newlink/testdata/layout.s new file mode 100644 index 0000000000..c3e55ef49a --- /dev/null +++ b/src/cmd/newlink/testdata/layout.s @@ -0,0 +1,29 @@ +// Copyright 2014 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 of section assignment in layout.go. +// Each symbol should end up in the section named by the symbol name prefix (up to the underscore). + +#include "textflag.h" + +TEXT text_start(SB),7,$0 + MOVQ $rodata_sym(SB), AX + MOVQ $noptrdata_sym(SB), AX + MOVQ $data_sym(SB), AX + MOVQ $bss_sym(SB), AX + MOVQ $noptrbss_sym(SB), AX + RET + +DATA rodata_sym(SB)/4, $1 +GLOBL rodata_sym(SB), RODATA, $4 + +DATA noptrdata_sym(SB)/4, $1 +GLOBL noptrdata_sym(SB), NOPTR, $4 + +DATA data_sym(SB)/4, $1 +GLOBL data_sym(SB), $4 + +GLOBL bss_sym(SB), $4 + +GLOBL noptrbss_sym(SB), NOPTR, $4 diff --git a/src/cmd/newlink/testdata/link.hello.darwin.amd64 b/src/cmd/newlink/testdata/link.hello.darwin.amd64 new file mode 100644 index 0000000000..4c62eb19b4 --- /dev/null +++ b/src/cmd/newlink/testdata/link.hello.darwin.amd64 @@ -0,0 +1,55 @@ +00000000 cf fa ed fe 07 00 00 01 03 00 00 00 02 00 00 00 |................| +00000010 04 00 00 00 d0 02 00 00 01 00 00 00 00 00 00 00 |................| +00000020 19 00 00 00 48 00 00 00 5f 5f 50 41 47 45 5a 45 |....H...__PAGEZE| +00000030 52 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |RO..............| +00000040 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +* +00000060 00 00 00 00 00 00 00 00 19 00 00 00 38 01 00 00 |............8...| +00000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| +00000080 00 10 00 00 00 00 00 00 c0 10 00 00 00 00 00 00 |................| +00000090 00 00 00 00 00 00 00 00 c0 10 00 00 00 00 00 00 |................| +000000a0 07 00 00 00 05 00 00 00 03 00 00 00 00 00 00 00 |................| +000000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 |__text..........| +000000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| +000000d0 00 20 00 00 00 00 00 00 30 00 00 00 00 00 00 00 |. ......0.......| +000000e0 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +000000f0 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000100 5f 5f 72 6f 64 61 74 61 00 00 00 00 00 00 00 00 |__rodata........| +00000110 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| +00000120 30 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |0 ..............| +00000130 30 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |0...............| +* +00000150 5f 5f 66 75 6e 63 74 61 62 00 00 00 00 00 00 00 |__functab.......| +00000160 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| +00000170 30 20 00 00 00 00 00 00 90 00 00 00 00 00 00 00 |0 ..............| +00000180 30 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |0...............| +* +000001a0 19 00 00 00 98 00 00 00 5f 5f 44 41 54 41 00 00 |........__DATA..| +000001b0 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 |.........0......| +000001c0 0c 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......| +000001d0 0c 00 00 00 00 00 00 00 03 00 00 00 03 00 00 00 |................| +000001e0 01 00 00 00 00 00 00 00 5f 5f 64 61 74 61 00 00 |........__data..| +000001f0 00 00 00 00 00 00 00 00 5f 5f 44 41 54 41 00 00 |........__DATA..| +00000200 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 |.........0......| +00000210 0c 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......| +* +00000230 00 00 00 00 00 00 00 00 05 00 00 00 b8 00 00 00 |................| +00000240 04 00 00 00 2a 00 00 00 00 00 00 00 00 00 00 00 |....*...........| +* +000002c0 00 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......| +* +00001000 bf 01 00 00 00 8d 35 f5 0f 00 00 ba 0c 00 00 00 |......5.........| +00001010 b8 04 00 00 02 0f 05 31 ff b8 01 00 00 02 0f 05 |.......1........| +00001020 c3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00001030 fb ff ff ff 00 00 01 08 01 00 00 00 00 00 00 00 |................| +00001040 00 20 00 00 00 00 00 00 30 00 00 00 00 00 00 00 |. ......0.......| +00001050 30 20 00 00 00 00 00 00 80 00 00 00 00 00 00 00 |0 ..............| +00001060 00 20 00 00 00 00 00 00 58 00 00 00 00 00 00 80 |. ......X.......| +00001070 08 00 00 00 60 00 00 00 63 00 00 00 66 00 00 00 |....`...c...f...| +00001080 00 00 00 00 00 00 00 00 5f 72 74 30 5f 67 6f 00 |........_rt0_go.| +00001090 02 30 00 04 30 00 06 05 02 06 02 05 02 05 02 02 |.0..0...........| +000010a0 02 02 02 05 02 02 02 10 00 00 00 00 00 00 00 00 |................| +000010b0 02 00 00 00 88 00 00 00 68 65 6c 6c 6f 2e 73 00 |........hello.s.| +* +00002000 68 65 6c 6c 6f 20 77 6f 72 6c 64 0a |hello world.| +0000200c diff --git a/src/cmd/newlink/testdata/macho.amd64.exit9 b/src/cmd/newlink/testdata/macho.amd64.exit9 new file mode 100644 index 0000000000..1adc814c33 --- /dev/null +++ b/src/cmd/newlink/testdata/macho.amd64.exit9 @@ -0,0 +1,24 @@ +00000000 cf fa ed fe 07 00 00 01 03 00 00 00 02 00 00 00 |................| +00000010 03 00 00 00 98 01 00 00 01 00 00 00 00 00 00 00 |................| +00000020 19 00 00 00 48 00 00 00 5f 5f 50 41 47 45 5a 45 |....H...__PAGEZE| +00000030 52 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |RO..............| +00000040 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000060 00 00 00 00 00 00 00 00 19 00 00 00 98 00 00 00 |................| +00000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| +00000080 00 10 00 00 00 00 00 00 0d 10 00 00 00 00 00 00 |................| +00000090 00 00 00 00 00 00 00 00 0d 10 00 00 00 00 00 00 |................| +000000a0 07 00 00 00 05 00 00 00 01 00 00 00 00 00 00 00 |................| +000000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 |__text..........| +000000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| +000000d0 00 20 00 00 00 00 00 00 0d 00 00 00 00 00 00 00 |. ..............| +000000e0 00 10 00 00 06 00 00 00 00 00 00 00 00 00 00 00 |................| +000000f0 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000100 05 00 00 00 b8 00 00 00 04 00 00 00 2a 00 00 00 |............*...| +00000110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +* +00000190 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |. ..............| +000001a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +* +00001000 b8 01 00 00 02 bf 09 00 00 00 0f 05 f4 |.............| +0000100d diff --git a/src/cmd/newlink/testdata/macho.amd64.hello b/src/cmd/newlink/testdata/macho.amd64.hello new file mode 100644 index 0000000000..45e70d0ac5 --- /dev/null +++ b/src/cmd/newlink/testdata/macho.amd64.hello @@ -0,0 +1,39 @@ +00000000 cf fa ed fe 07 00 00 01 03 00 00 00 02 00 00 00 |................| +00000010 04 00 00 00 30 02 00 00 01 00 00 00 00 00 00 00 |....0...........| +00000020 19 00 00 00 48 00 00 00 5f 5f 50 41 47 45 5a 45 |....H...__PAGEZE| +00000030 52 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |RO..............| +00000040 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000060 00 00 00 00 00 00 00 00 19 00 00 00 98 00 00 00 |................| +00000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| +00000080 00 10 00 00 00 00 00 00 23 10 00 00 00 00 00 00 |........#.......| +00000090 00 00 00 00 00 00 00 00 23 10 00 00 00 00 00 00 |........#.......| +000000a0 07 00 00 00 05 00 00 00 01 00 00 00 00 00 00 00 |................| +000000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 |__text..........| +000000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| +000000d0 00 20 00 00 00 00 00 00 23 00 00 00 00 00 00 00 |. ......#.......| +000000e0 00 10 00 00 06 00 00 00 00 00 00 00 00 00 00 00 |................| +000000f0 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000100 19 00 00 00 98 00 00 00 5f 5f 44 41 54 41 00 00 |........__DATA..| +00000110 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 |.........0......| +00000120 0c 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......| +00000130 0c 00 00 00 00 00 00 00 03 00 00 00 03 00 00 00 |................| +00000140 01 00 00 00 00 00 00 00 5f 5f 64 61 74 61 00 00 |........__data..| +00000150 00 00 00 00 00 00 00 00 5f 5f 44 41 54 41 00 00 |........__DATA..| +00000160 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 |.........0......| +00000170 0c 00 00 00 00 00 00 00 00 20 00 00 06 00 00 00 |......... ......| +00000180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000190 00 00 00 00 00 00 00 00 05 00 00 00 b8 00 00 00 |................| +000001a0 04 00 00 00 2a 00 00 00 00 00 00 00 00 00 00 00 |....*...........| +000001b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +* +00000220 00 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......| +00000230 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +* +00001000 b8 04 00 00 02 bf 01 00 00 00 be 00 30 00 00 ba |............0...| +00001010 0c 00 00 00 0f 05 b8 01 00 00 02 bf 09 00 00 00 |................| +00001020 0f 05 f4 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00001030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +* +00002000 68 65 6c 6c 6f 20 77 6f 72 6c 64 0a |hello world.| +0000200c diff --git a/src/cmd/newlink/testdata/macho.amd64.helloro b/src/cmd/newlink/testdata/macho.amd64.helloro new file mode 100644 index 0000000000..4b70fbd0fa --- /dev/null +++ b/src/cmd/newlink/testdata/macho.amd64.helloro @@ -0,0 +1,34 @@ +00000000 cf fa ed fe 07 00 00 01 03 00 00 00 02 00 00 00 |................| +00000010 03 00 00 00 e8 01 00 00 01 00 00 00 00 00 00 00 |................| +00000020 19 00 00 00 48 00 00 00 5f 5f 50 41 47 45 5a 45 |....H...__PAGEZE| +00000030 52 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |RO..............| +00000040 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000060 00 00 00 00 00 00 00 00 19 00 00 00 e8 00 00 00 |................| +00000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| +00000080 00 10 00 00 00 00 00 00 0c 20 00 00 00 00 00 00 |......... ......| +00000090 00 00 00 00 00 00 00 00 0c 20 00 00 00 00 00 00 |......... ......| +000000a0 07 00 00 00 05 00 00 00 02 00 00 00 00 00 00 00 |................| +000000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 |__text..........| +000000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| +000000d0 00 20 00 00 00 00 00 00 23 00 00 00 00 00 00 00 |. ......#.......| +000000e0 00 10 00 00 06 00 00 00 00 00 00 00 00 00 00 00 |................| +000000f0 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000100 5f 5f 72 6f 64 61 74 61 00 00 00 00 00 00 00 00 |__rodata........| +00000110 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| +00000120 00 30 00 00 00 00 00 00 0c 00 00 00 00 00 00 00 |.0..............| +00000130 00 20 00 00 06 00 00 00 00 00 00 00 00 00 00 00 |. ..............| +00000140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000150 05 00 00 00 b8 00 00 00 04 00 00 00 2a 00 00 00 |............*...| +00000160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +* +000001e0 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |. ..............| +000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +* +00001000 b8 04 00 00 02 bf 01 00 00 00 be 00 30 00 00 ba |............0...| +00001010 0c 00 00 00 0f 05 b8 01 00 00 02 bf 00 00 00 00 |................| +00001020 0f 05 f4 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00001030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +* +00002000 68 65 6c 6c 6f 20 77 6f 72 6c 64 0a |hello world.| +0000200c diff --git a/src/cmd/newlink/testdata/pclntab.6 b/src/cmd/newlink/testdata/pclntab.6 new file mode 100644 index 0000000000..8156698265 Binary files /dev/null and b/src/cmd/newlink/testdata/pclntab.6 differ diff --git a/src/cmd/newlink/testdata/pclntab.s b/src/cmd/newlink/testdata/pclntab.s new file mode 100644 index 0000000000..12dac70b0c --- /dev/null +++ b/src/cmd/newlink/testdata/pclntab.s @@ -0,0 +1,1751 @@ +// generated by genpcln.go; do not edit + +#line 1 "input" +TEXT func0(SB),7,$48-592 + FUNCDATA $1, funcdata0(SB) +#line 1 "input" +#line 1 "input" +/*0x0004*/ MOVL $0x1234567, AX +#line 1 "input" +/*0x0009*/ BYTE $0 +/* */ PCDATA $1, $110 +#line 1 "input" +/*0x000a*/ BYTE $0 +#line 1 "input" +/*0x000b*/ BYTE $0 +/* */ PCDATA $1, $422 +#line 1 "input" +/*0x000c*/ BYTE $0 +#line 1 "input" +/*0x000d*/ BYTE $0 +#line 1 "input" +/*0x000e*/ POPQ AX +#line 1 "input" +/*0x000f*/ MOVL $0x1234567, AX +#line 1 "input" +/*0x0014*/ BYTE $0 +#line 1 "input" +/*0x0015*/ BYTE $0 +#line 1 "input" +/*0x0016*/ BYTE $0 +#line 542 "input" +/*0x0017*/ BYTE $0 +#line 960 "input" +/*0x0018*/ MOVL $0x1234567, AX +#line 960 "input" +/*0x001d*/ BYTE $0 +#line 960 "input" +/*0x001e*/ PUSHQ AX +#line 960 "input" +/*0x001f*/ BYTE $0 +#line 960 "input" +/*0x0020*/ BYTE $0 +#line 594 "input" +/*0x0021*/ BYTE $0 +#line 671 "input" +/*0x0022*/ MOVL $0x1234567, AX +#line 671 "input" +/*0x0027*/ BYTE $0 +#line 671 "input" +/*0x0028*/ BYTE $0 +#line 230 "input" +/*0x0029*/ BYTE $0 +#line 230 "input" +/*0x002a*/ BYTE $0 +#line 413 "input" +/*0x002b*/ BYTE $0 +#line 413 "input" +/*0x002c*/ BYTE $0 +#line 413 "input" +/*0x002d*/ BYTE $0 +#line 729 "input" +/*0x002e*/ BYTE $0 +#line 729 "input" +/*0x002f*/ BYTE $0 +#line 729 "input" +/*0x0030*/ BYTE $0 +#line 948 "input" +/*0x0031*/ BYTE $0 +#line 11 "input" +/*0x0032*/ MOVQ $0x123456789, AX +#line 11 "input" +/*0x003c*/ MOVL $0x1234567, AX +#line 11 "input" +/*0x0041*/ BYTE $0 +#line 11 "input" +/*0x0042*/ BYTE $0 +#line 11 "input" +/*0x0043*/ POPQ AX +/* */ PCDATA $2, $342 +#line 11 "input" +/*0x0044*/ MOVQ $0x123456789, AX +#line 11 "input" +/*0x004e*/ MOVQ $0x123456789, AX +#line 11 "input" +/*0x0058*/ MOVL $0x1234567, AX +#line 11 "input" +/*0x005d*/ BYTE $0 +#line 11 "input" +/*0x005e*/ BYTE $0 +#line 70 "input" +/*0x005f*/ BYTE $0 +#line 70 "input" +/*0x0060*/ BYTE $0 +#line 70 "input" +/*0x0061*/ BYTE $0 +#line 18 "input" +/*0x0062*/ MOVQ $0x123456789, AX +#line 18 "input" +/*0x006c*/ MOVL $0x1234567, AX +#line 18 "input" +/*0x0071*/ BYTE $0 +#line 814 "input" +/*0x0072*/ BYTE $0 +#line 814 "input" +/*0x0073*/ PUSHQ AX +#line 763 "input" +/*0x0074*/ BYTE $0 +#line 763 "input" +/*0x0075*/ BYTE $0 +#line 763 "input" +/*0x0076*/ BYTE $0 +#line 530 "input" +/*0x0077*/ BYTE $0 +#line 530 "input" +/*0x0078*/ BYTE $0 +#line 530 "input" +/*0x0079*/ BYTE $0 +#line 530 "input" +/*0x007a*/ BYTE $0 +#line 985 "input" +/*0x007b*/ BYTE $0 +#line 985 "input" +/*0x007c*/ BYTE $0 +#line 985 "input" +/*0x007d*/ BYTE $0 +#line 985 "input" +/*0x007e*/ PUSHQ AX +#line 985 "input" +/*0x007f*/ MOVL $0x1234567, AX +#line 958 "input" +/*0x0084*/ BYTE $0 +#line 368 "input" +/*0x0085*/ MOVQ $0x123456789, AX +#line 368 "input" +/*0x008f*/ MOVL $0x1234567, AX +#line 368 "input" +/*0x0094*/ BYTE $0 +#line 368 "input" +/*0x0095*/ BYTE $0 +#line 368 "input" +/*0x0096*/ BYTE $0 +#line 368 "input" +/*0x0097*/ BYTE $0 +#line 75 "file0.s" +/*0x0098*/ BYTE $0 +#line 75 "file0.s" +/*0x0099*/ BYTE $0 +#line 75 "file0.s" +/*0x009a*/ BYTE $0 +#line 75 "file0.s" +/*0x009b*/ BYTE $0 +#line 588 "file0.s" +/*0x009c*/ MOVQ $0x123456789, AX +#line 187 "file0.s" +/*0x00a6*/ MOVQ $0x123456789, AX +#line 187 "file0.s" +/*0x00b0*/ BYTE $0 +#line 202 "file0.s" +/*0x00b1*/ MOVL $0x1234567, AX +#line 202 "file0.s" +/*0x00b6*/ BYTE $0 +#line 887 "file0.s" +/*0x00b7*/ MOVL $0x1234567, AX +#line 887 "file0.s" +/*0x00bc*/ BYTE $0 +#line 887 "file0.s" +/*0x00bd*/ BYTE $0 +#line 480 "file0.s" +/*0x00be*/ MOVL $0x1234567, AX +#line 480 "file0.s" +/*0x00c3*/ BYTE $0 +#line 40 "file8.s" +/*0x00c4*/ BYTE $0 +#line 17 "file0.s" +/*0x00c5*/ MOVQ $0x123456789, AX +#line 17 "file0.s" +/*0x00cf*/ BYTE $0 +#line 17 "file0.s" +/*0x00d0*/ BYTE $0 +#line 17 "file0.s" +/*0x00d1*/ BYTE $0 +#line 17 "file0.s" +/*0x00d2*/ BYTE $0 +#line 436 "file0.s" +/*0x00d3*/ MOVL $0x1234567, AX +#line 436 "file0.s" +/*0x00d8*/ BYTE $0 +#line 436 "file0.s" +/*0x00d9*/ BYTE $0 +#line 346 "file0.s" +/*0x00da*/ BYTE $0 +#line 346 "file0.s" +/*0x00db*/ BYTE $0 +#line 346 "file0.s" +/*0x00dc*/ BYTE $0 +#line 812 "file0.s" +/*0x00dd*/ BYTE $0 +#line 812 "file0.s" +/*0x00de*/ BYTE $0 +#line 812 "file0.s" +/*0x00df*/ BYTE $0 +#line 812 "file0.s" +/*0x00e0*/ BYTE $0 +#line 94 "file1.s" +/*0x00e1*/ BYTE $0 +#line 94 "file1.s" +/*0x00e2*/ BYTE $0 +#line 165 "file1.s" +/*0x00e3*/ MOVL $0x1234567, AX +#line 165 "file1.s" +/*0x00e8*/ BYTE $0 +#line 456 "file1.s" +/*0x00e9*/ BYTE $0 +#line 810 "file1.s" +/*0x00ea*/ BYTE $0 +#line 722 "file1.s" +/*0x00eb*/ BYTE $0 +#line 722 "file1.s" +/*0x00ec*/ BYTE $0 +#line 722 "file1.s" +/*0x00ed*/ BYTE $0 +#line 722 "file1.s" +/*0x00ee*/ BYTE $0 +#line 722 "file1.s" +/*0x00ef*/ PUSHQ AX +#line 722 "file1.s" +/*0x00f0*/ BYTE $0 +#line 722 "file1.s" +/*0x00f1*/ BYTE $0 +#line 722 "file1.s" +/*0x00f2*/ BYTE $0 +#line 722 "file1.s" +/*0x00f3*/ BYTE $0 +/* */ PCDATA $2, $246 +#line 722 "file1.s" +/*0x00f4*/ MOVQ $0x123456789, AX +#line 722 "file1.s" +/*0x00fe*/ MOVQ $0x123456789, AX +#line 722 "file1.s" +/*0x0108*/ MOVL $0x1234567, AX +#line 722 "file1.s" +/*0x010d*/ BYTE $0 +#line 722 "file1.s" +/*0x010e*/ BYTE $0 +#line 497 "file1.s" +/*0x010f*/ MOVQ $0x123456789, AX +#line 497 "file1.s" +/*0x0119*/ MOVQ $0x123456789, AX +#line 497 "file1.s" +/*0x0123*/ MOVQ $0x123456789, AX +#line 497 "file1.s" +/*0x012d*/ MOVL $0x1234567, AX +#line 497 "file1.s" +/*0x0132*/ BYTE $0 +#line 686 "file1.s" +/*0x0133*/ BYTE $0 +#line 686 "file1.s" +/*0x0134*/ BYTE $0 +#line 248 "file1.s" +/*0x0135*/ BYTE $0 +#line 248 "file1.s" +/*0x0136*/ BYTE $0 +#line 248 "file1.s" +/*0x0137*/ BYTE $0 +#line 248 "file1.s" +/*0x0138*/ BYTE $0 +#line 307 "file1.s" +/*0x0139*/ BYTE $0 +#line 220 "file1.s" +/*0x013a*/ MOVL $0x1234567, AX +#line 220 "file1.s" +/*0x013f*/ BYTE $0 +#line 220 "file1.s" +/*0x0140*/ BYTE $0 +#line 467 "file1.s" +/*0x0141*/ MOVQ $0x123456789, AX +#line 467 "file1.s" +/*0x014b*/ BYTE $0 +#line 467 "file1.s" +/*0x014c*/ BYTE $0 +#line 467 "file1.s" +/*0x014d*/ BYTE $0 +#line 467 "file1.s" +/*0x014e*/ BYTE $0 +#line 786 "file1.s" +/*0x014f*/ MOVL $0x1234567, AX +#line 251 "file1.s" +/*0x0154*/ BYTE $0 +/* */ PCDATA $2, $64 +#line 251 "file1.s" +/*0x0155*/ BYTE $0 +#line 251 "file1.s" +/*0x0156*/ BYTE $0 +#line 251 "file1.s" +/*0x0157*/ BYTE $0 +#line 618 "file1.s" +/*0x0158*/ MOVQ $0x123456789, AX +/* */ PCDATA $1, $686 +#line 618 "file1.s" +/*0x0162*/ BYTE $0 +#line 618 "file1.s" +/*0x0163*/ BYTE $0 +#line 618 "file1.s" +/*0x0164*/ BYTE $0 +#line 618 "file1.s" +/*0x0165*/ PUSHQ AX +/* */ PCDATA $2, $915 +#line 618 "file1.s" +/*0x0166*/ BYTE $0 +#line 618 "file1.s" +/*0x0167*/ BYTE $0 +#line 618 "file1.s" +/*0x0168*/ BYTE $0 +#line 618 "file1.s" +/*0x0169*/ BYTE $0 +#line 230 "file1.s" +/*0x016a*/ BYTE $0 +#line 823 "file1.s" +/*0x016b*/ BYTE $0 +#line 145 "file1.s" +/*0x016c*/ MOVQ $0x123456789, AX +#line 145 "file1.s" +/*0x0176*/ BYTE $0 +#line 145 "file1.s" +/*0x0177*/ BYTE $0 +#line 675 "file1.s" +/*0x0178*/ BYTE $0 +#line 62 "file9.s" +/*0x0179*/ BYTE $0 +/* */ PCDATA $2, $768 +#line 62 "file9.s" +/*0x017a*/ BYTE $0 +#line 62 "file9.s" +/*0x017b*/ BYTE $0 +#line 29 "file9.s" +/*0x017c*/ BYTE $0 +#line 29 "file9.s" +/*0x017d*/ BYTE $0 +#line 29 "file9.s" +/*0x017e*/ BYTE $0 +#line 29 "file9.s" +/*0x017f*/ BYTE $0 +#line 65 "file4.s" +/*0x0180*/ BYTE $0 +#line 77 "file3.s" +/*0x0181*/ MOVL $0x1234567, AX +#line 77 "file3.s" +/*0x0186*/ BYTE $0 +#line 77 "file3.s" +/*0x0187*/ BYTE $0 +#line 77 "file3.s" +/*0x0188*/ BYTE $0 +#line 307 "file3.s" +/*0x0189*/ MOVQ $0x123456789, AX +#line 307 "file3.s" +/*0x0193*/ BYTE $0 +#line 654 "file3.s" +/*0x0194*/ BYTE $0 +#line 654 "file3.s" +/*0x0195*/ BYTE $0 +#line 115 "file3.s" +/*0x0196*/ MOVL $0x1234567, AX +#line 115 "file3.s" +/*0x019b*/ BYTE $0 +#line 115 "file3.s" +/*0x019c*/ BYTE $0 +#line 115 "file3.s" +/*0x019d*/ BYTE $0 +#line 115 "file3.s" +/*0x019e*/ BYTE $0 +#line 154 "file3.s" +/*0x019f*/ MOVQ $0x123456789, AX +#line 166 "file3.s" +/*0x01a9*/ BYTE $0 +#line 166 "file3.s" +/*0x01aa*/ BYTE $0 +#line 166 "file3.s" +/*0x01ab*/ BYTE $0 +/* */ PCDATA $1, $523 +#line 166 "file3.s" +/*0x01ac*/ MOVL $0x1234567, AX +#line 166 "file3.s" +/*0x01b1*/ BYTE $0 +#line 779 "file3.s" +/*0x01b2*/ BYTE $0 +#line 779 "file3.s" +/*0x01b3*/ BYTE $0 +#line 515 "file3.s" +/*0x01b4*/ BYTE $0 +#line 515 "file3.s" +/*0x01b5*/ BYTE $0 +#line 369 "file3.s" +/*0x01b6*/ MOVL $0x1234567, AX +#line 369 "file3.s" +/*0x01bb*/ BYTE $0 +#line 369 "file3.s" +/*0x01bc*/ BYTE $0 +#line 369 "file3.s" +/*0x01bd*/ BYTE $0 +#line 680 "file3.s" +/*0x01be*/ BYTE $0 +#line 680 "file3.s" +/*0x01bf*/ BYTE $0 +#line 680 "file3.s" +/*0x01c0*/ BYTE $0 +#line 680 "file3.s" +/*0x01c1*/ BYTE $0 +#line 131 "file3.s" +/*0x01c2*/ MOVQ $0x123456789, AX +#line 131 "file3.s" +/*0x01cc*/ BYTE $0 +#line 131 "file3.s" +/*0x01cd*/ BYTE $0 +#line 131 "file3.s" +/*0x01ce*/ BYTE $0 +#line 131 "file3.s" +/*0x01cf*/ BYTE $0 +#line 848 "file3.s" +/*0x01d0*/ BYTE $0 +#line 848 "file3.s" +/*0x01d1*/ BYTE $0 +#line 848 "file3.s" +/*0x01d2*/ POPQ AX +#line 848 "file3.s" +/*0x01d3*/ BYTE $0 +#line 848 "file3.s" +/*0x01d4*/ BYTE $0 +#line 848 "file3.s" +/*0x01d5*/ BYTE $0 +/* */ PCDATA $1, $86 +#line 848 "file3.s" +/*0x01d6*/ MOVL $0x1234567, AX +#line 438 "file3.s" +/*0x01db*/ MOVQ $0x123456789, AX +#line 438 "file3.s" +/*0x01e5*/ MOVL $0x1234567, AX +#line 5 "file3.s" +/*0x01ea*/ BYTE $0 +#line 5 "file3.s" +/*0x01eb*/ BYTE $0 +#line 531 "file3.s" +/*0x01ec*/ MOVQ $0x123456789, AX +#line 531 "file3.s" +/*0x01f6*/ MOVQ $0x123456789, AX +#line 531 "file3.s" +/*0x0200*/ MOVQ $0x123456789, AX +#line 531 "file3.s" +/*0x020a*/ MOVL $0x1234567, AX +#line 863 "file3.s" +/*0x020f*/ BYTE $0 +#line 733 "file3.s" +/*0x0210*/ MOVQ $0x123456789, AX +#line 166 "file3.s" +/*0x021a*/ MOVQ $0x123456789, AX +#line 166 "file3.s" +/*0x0224*/ BYTE $0 +#line 166 "file3.s" +/*0x0225*/ BYTE $0 +#line 166 "file3.s" +/*0x0226*/ BYTE $0 +#line 166 "file3.s" +/*0x0227*/ BYTE $0 +#line 54 "file3.s" +/*0x0228*/ MOVQ $0x123456789, AX +#line 54 "file3.s" +/*0x0232*/ BYTE $0 +#line 54 "file3.s" +/*0x0233*/ BYTE $0 +#line 54 "file3.s" +/*0x0234*/ BYTE $0 +#line 20 "file4.s" +/*0x0235*/ BYTE $0 +#line 20 "file4.s" +/*0x0236*/ BYTE $0 +#line 546 "file4.s" +/*0x0237*/ BYTE $0 +#line 546 "file4.s" +/*0x0238*/ BYTE $0 +#line 74 "file4.s" +/*0x0239*/ BYTE $0 +#line 31 "file4.s" +/*0x023a*/ BYTE $0 +#line 31 "file4.s" +/*0x023b*/ BYTE $0 +#line 31 "file4.s" +/*0x023c*/ BYTE $0 +#line 31 "file4.s" +/*0x023d*/ BYTE $0 +#line 834 "file4.s" +/*0x023e*/ BYTE $0 +#line 834 "file4.s" +/*0x023f*/ BYTE $0 +#line 519 "file4.s" +/*0x0240*/ MOVL $0x1234567, AX +#line 342 "file4.s" +/*0x0245*/ BYTE $0 +#line 342 "file4.s" +/*0x0246*/ BYTE $0 +#line 342 "file4.s" +/*0x0247*/ BYTE $0 +#line 458 "file4.s" +/*0x0248*/ BYTE $0 +#line 458 "file4.s" +/*0x0249*/ BYTE $0 +#line 458 "file4.s" +/*0x024a*/ BYTE $0 +#line 458 "file4.s" +/*0x024b*/ BYTE $0 +#line 13 "file9.s" +/*0x024c*/ BYTE $0 +#line 13 "file9.s" +/*0x024d*/ BYTE $0 +#line 13 "file9.s" +/*0x024e*/ BYTE $0 +#line 365 "file9.s" +/*0x024f*/ BYTE $0 +#line 749 "file9.s" +/*0x0250*/ MOVL $0x1234567, AX +#line 749 "file9.s" +/*0x0255*/ BYTE $0 +#line 749 "file9.s" +/*0x0256*/ BYTE $0 +#line 41 "file0.s" +/*0x0257*/ MOVL $0x1234567, AX +#line 41 "file0.s" +/*0x025c*/ BYTE $0 +#line 41 "file0.s" +/*0x025d*/ BYTE $0 +#line 41 "file0.s" +/*0x025e*/ BYTE $0 +#line 869 "file0.s" +/*0x025f*/ BYTE $0 +#line 881 "file0.s" +/*0x0260*/ MOVQ $0x123456789, AX +#line 881 "file0.s" +/*0x026a*/ MOVQ $0x123456789, AX + POPQ AX + POPQ AX + RET + +GLOBL funcdata0(SB), $16 +#line 1 "input" +TEXT func1(SB),7,$40-648 + FUNCDATA $1, funcdata1(SB) +#line 1 "input" +#line 1 "input" +/*0x0004*/ BYTE $0 +#line 12 "file4.s" +/*0x0005*/ MOVL $0x1234567, AX +#line 12 "file4.s" +/*0x000a*/ BYTE $0 +#line 12 "file4.s" +/*0x000b*/ BYTE $0 +#line 12 "file4.s" +/*0x000c*/ BYTE $0 +/* */ PCDATA $2, $705 +#line 12 "file4.s" +/*0x000d*/ MOVQ $0x123456789, AX +#line 12 "file4.s" +/*0x0017*/ BYTE $0 +#line 633 "file4.s" +/*0x0018*/ MOVQ $0x123456789, AX +#line 633 "file4.s" +/*0x0022*/ MOVL $0x1234567, AX +#line 633 "file4.s" +/*0x0027*/ POPQ AX +#line 633 "file4.s" +/*0x0028*/ BYTE $0 +#line 633 "file4.s" +/*0x0029*/ BYTE $0 +#line 633 "file4.s" +/*0x002a*/ BYTE $0 +#line 633 "file4.s" +/*0x002b*/ PUSHQ AX +#line 633 "file4.s" +/*0x002c*/ MOVL $0x1234567, AX +#line 997 "file4.s" +/*0x0031*/ BYTE $0 +#line 997 "file4.s" +/*0x0032*/ BYTE $0 +#line 997 "file4.s" +/*0x0033*/ BYTE $0 +#line 997 "file4.s" +/*0x0034*/ BYTE $0 +#line 997 "file4.s" +/*0x0035*/ POPQ AX +#line 997 "file4.s" +/*0x0036*/ BYTE $0 +#line 997 "file4.s" +/*0x0037*/ BYTE $0 +#line 1 "file4.s" +/*0x0038*/ MOVQ $0x123456789, AX +#line 1 "file4.s" +/*0x0042*/ MOVQ $0x123456789, AX +#line 1 "file4.s" +/*0x004c*/ MOVQ $0x123456789, AX +#line 1 "file4.s" +/*0x0056*/ MOVQ $0x123456789, AX +#line 1 "file4.s" +/*0x0060*/ BYTE $0 +#line 922 "file4.s" +/*0x0061*/ BYTE $0 +#line 375 "file4.s" +/*0x0062*/ MOVL $0x1234567, AX +/* */ PCDATA $1, $51 +#line 31 "file4.s" +/*0x0067*/ MOVQ $0x123456789, AX +#line 31 "file4.s" +/*0x0071*/ BYTE $0 +#line 620 "file4.s" +/*0x0072*/ MOVL $0x1234567, AX +#line 620 "file4.s" +/*0x0077*/ BYTE $0 +#line 695 "file4.s" +/*0x0078*/ MOVL $0x1234567, AX +#line 799 "file4.s" +/*0x007d*/ MOVL $0x1234567, AX +#line 41 "file4.s" +/*0x0082*/ BYTE $0 +#line 795 "file4.s" +/*0x0083*/ MOVL $0x1234567, AX +#line 908 "file4.s" +/*0x0088*/ BYTE $0 +#line 905 "file4.s" +/*0x0089*/ BYTE $0 +#line 905 "file4.s" +/*0x008a*/ BYTE $0 +#line 905 "file4.s" +/*0x008b*/ BYTE $0 +#line 905 "file4.s" +/*0x008c*/ BYTE $0 +#line 861 "file4.s" +/*0x008d*/ MOVL $0x1234567, AX +#line 861 "file4.s" +/*0x0092*/ BYTE $0 +#line 861 "file4.s" +/*0x0093*/ BYTE $0 +#line 861 "file4.s" +/*0x0094*/ BYTE $0 +#line 861 "file4.s" +/*0x0095*/ BYTE $0 +/* */ PCDATA $1, $192 +#line 861 "file4.s" +/*0x0096*/ MOVQ $0x123456789, AX +/* */ PCDATA $1, $60 +#line 861 "file4.s" +/*0x00a0*/ MOVL $0x1234567, AX +#line 861 "file4.s" +/*0x00a5*/ BYTE $0 +#line 861 "file4.s" +/*0x00a6*/ BYTE $0 +#line 56 "file6.s" +/*0x00a7*/ MOVQ $0x123456789, AX +#line 56 "file6.s" +/*0x00b1*/ BYTE $0 +#line 56 "file6.s" +/*0x00b2*/ BYTE $0 +#line 56 "file6.s" +/*0x00b3*/ BYTE $0 +#line 204 "file6.s" +/*0x00b4*/ BYTE $0 +#line 204 "file6.s" +/*0x00b5*/ BYTE $0 +#line 204 "file6.s" +/*0x00b6*/ BYTE $0 +#line 204 "file6.s" +/*0x00b7*/ BYTE $0 +#line 979 "file6.s" +/*0x00b8*/ MOVL $0x1234567, AX +#line 979 "file6.s" +/*0x00bd*/ BYTE $0 +#line 979 "file6.s" +/*0x00be*/ BYTE $0 +#line 979 "file6.s" +/*0x00bf*/ BYTE $0 +#line 58 "file6.s" +/*0x00c0*/ MOVL $0x1234567, AX +#line 238 "file6.s" +/*0x00c5*/ MOVL $0x1234567, AX +#line 238 "file6.s" +/*0x00ca*/ BYTE $0 +#line 238 "file6.s" +/*0x00cb*/ BYTE $0 +#line 699 "file2.s" +/*0x00cc*/ MOVQ $0x123456789, AX +#line 699 "file2.s" +/*0x00d6*/ BYTE $0 +#line 699 "file2.s" +/*0x00d7*/ BYTE $0 +#line 514 "file2.s" +/*0x00d8*/ MOVL $0x1234567, AX +#line 514 "file2.s" +/*0x00dd*/ BYTE $0 +#line 514 "file2.s" +/*0x00de*/ BYTE $0 +#line 108 "file2.s" +/*0x00df*/ MOVQ $0x123456789, AX +#line 108 "file2.s" +/*0x00e9*/ MOVQ $0x123456789, AX +#line 108 "file2.s" +/*0x00f3*/ MOVL $0x1234567, AX +#line 108 "file2.s" +/*0x00f8*/ BYTE $0 +#line 108 "file2.s" +/*0x00f9*/ BYTE $0 +#line 918 "file2.s" +/*0x00fa*/ BYTE $0 +#line 918 "file2.s" +/*0x00fb*/ BYTE $0 +#line 785 "file2.s" +/*0x00fc*/ BYTE $0 +#line 3 "file5.s" +/*0x00fd*/ BYTE $0 +#line 918 "file5.s" +/*0x00fe*/ BYTE $0 +#line 918 "file5.s" +/*0x00ff*/ BYTE $0 +#line 670 "file5.s" +/*0x0100*/ MOVL $0x1234567, AX +#line 670 "file5.s" +/*0x0105*/ BYTE $0 +#line 720 "file5.s" +/*0x0106*/ BYTE $0 +#line 384 "file5.s" +/*0x0107*/ MOVQ $0x123456789, AX +#line 384 "file5.s" +/*0x0111*/ MOVQ $0x123456789, AX +#line 384 "file5.s" +/*0x011b*/ MOVQ $0x123456789, AX +#line 384 "file5.s" +/*0x0125*/ BYTE $0 +#line 384 "file5.s" +/*0x0126*/ BYTE $0 +#line 263 "file5.s" +/*0x0127*/ BYTE $0 +#line 750 "file5.s" +/*0x0128*/ MOVL $0x1234567, AX +#line 750 "file5.s" +/*0x012d*/ BYTE $0 +#line 679 "file5.s" +/*0x012e*/ MOVL $0x1234567, AX +#line 679 "file5.s" +/*0x0133*/ BYTE $0 +#line 679 "file5.s" +/*0x0134*/ BYTE $0 +#line 679 "file5.s" +/*0x0135*/ BYTE $0 +#line 679 "file5.s" +/*0x0136*/ POPQ AX +#line 171 "file5.s" +/*0x0137*/ MOVL $0x1234567, AX +#line 171 "file5.s" +/*0x013c*/ BYTE $0 +#line 34 "file2.s" +/*0x013d*/ BYTE $0 +#line 34 "file2.s" +/*0x013e*/ BYTE $0 +#line 34 "file2.s" +/*0x013f*/ BYTE $0 +#line 732 "file2.s" +/*0x0140*/ BYTE $0 +#line 732 "file2.s" +/*0x0141*/ PUSHQ AX +#line 485 "file2.s" +/*0x0142*/ BYTE $0 +#line 485 "file2.s" +/*0x0143*/ BYTE $0 +#line 485 "file2.s" +/*0x0144*/ BYTE $0 +#line 222 "file2.s" +/*0x0145*/ BYTE $0 +#line 222 "file2.s" +/*0x0146*/ BYTE $0 +/* */ PCDATA $1, $462 +#line 222 "file2.s" +/*0x0147*/ MOVQ $0x123456789, AX +#line 222 "file2.s" +/*0x0151*/ MOVL $0x1234567, AX +#line 222 "file2.s" +/*0x0156*/ BYTE $0 +#line 677 "file2.s" +/*0x0157*/ BYTE $0 +#line 117 "file2.s" +/*0x0158*/ MOVL $0x1234567, AX +#line 117 "file2.s" +/*0x015d*/ BYTE $0 +#line 117 "file2.s" +/*0x015e*/ BYTE $0 +#line 361 "file2.s" +/*0x015f*/ MOVL $0x1234567, AX +#line 590 "file2.s" +/*0x0164*/ BYTE $0 +#line 590 "file2.s" +/*0x0165*/ BYTE $0 +#line 58 "file2.s" +/*0x0166*/ MOVL $0x1234567, AX +#line 58 "file2.s" +/*0x016b*/ BYTE $0 +#line 58 "file2.s" +/*0x016c*/ BYTE $0 +#line 58 "file2.s" +/*0x016d*/ BYTE $0 +#line 58 "file2.s" +/*0x016e*/ BYTE $0 +#line 983 "file2.s" +/*0x016f*/ BYTE $0 +#line 983 "file2.s" +/*0x0170*/ BYTE $0 +#line 983 "file2.s" +/*0x0171*/ BYTE $0 +#line 983 "file2.s" +/*0x0172*/ BYTE $0 +#line 727 "file2.s" +/*0x0173*/ MOVL $0x1234567, AX +#line 450 "file2.s" +/*0x0178*/ BYTE $0 +#line 450 "file2.s" +/*0x0179*/ BYTE $0 +#line 450 "file2.s" +/*0x017a*/ BYTE $0 +#line 450 "file2.s" +/*0x017b*/ BYTE $0 +#line 334 "file2.s" +/*0x017c*/ BYTE $0 +#line 334 "file2.s" +/*0x017d*/ BYTE $0 +#line 334 "file2.s" +/*0x017e*/ BYTE $0 +#line 334 "file2.s" +/*0x017f*/ BYTE $0 +#line 465 "file2.s" +/*0x0180*/ MOVL $0x1234567, AX +/* */ PCDATA $1, $518 +#line 465 "file2.s" +/*0x0185*/ MOVL $0x1234567, AX +#line 465 "file2.s" +/*0x018a*/ BYTE $0 +#line 465 "file2.s" +/*0x018b*/ BYTE $0 +#line 465 "file2.s" +/*0x018c*/ BYTE $0 +#line 465 "file2.s" +/*0x018d*/ BYTE $0 +#line 682 "file2.s" +/*0x018e*/ MOVL $0x1234567, AX +#line 682 "file2.s" +/*0x0193*/ BYTE $0 +#line 682 "file2.s" +/*0x0194*/ BYTE $0 +#line 846 "file2.s" +/*0x0195*/ MOVQ $0x123456789, AX +#line 846 "file2.s" +/*0x019f*/ BYTE $0 +#line 846 "file2.s" +/*0x01a0*/ BYTE $0 +#line 846 "file2.s" +/*0x01a1*/ POPQ AX +#line 846 "file2.s" +/*0x01a2*/ BYTE $0 +#line 846 "file2.s" +/*0x01a3*/ BYTE $0 +#line 49 "file2.s" +/*0x01a4*/ BYTE $0 +#line 49 "file2.s" +/*0x01a5*/ BYTE $0 +#line 49 "file2.s" +/*0x01a6*/ BYTE $0 +#line 726 "file2.s" +/*0x01a7*/ MOVQ $0x123456789, AX +#line 726 "file2.s" +/*0x01b1*/ MOVL $0x1234567, AX +#line 726 "file2.s" +/*0x01b6*/ BYTE $0 +#line 726 "file2.s" +/*0x01b7*/ BYTE $0 +#line 726 "file2.s" +/*0x01b8*/ BYTE $0 +#line 726 "file2.s" +/*0x01b9*/ PUSHQ AX +#line 726 "file2.s" +/*0x01ba*/ BYTE $0 +#line 726 "file2.s" +/*0x01bb*/ BYTE $0 +#line 726 "file2.s" +/*0x01bc*/ BYTE $0 +#line 726 "file2.s" +/*0x01bd*/ BYTE $0 +#line 13 "file1.s" +/*0x01be*/ BYTE $0 +#line 13 "file1.s" +/*0x01bf*/ BYTE $0 +#line 13 "file1.s" +/*0x01c0*/ BYTE $0 +#line 827 "file1.s" +/*0x01c1*/ MOVL $0x1234567, AX +#line 827 "file1.s" +/*0x01c6*/ BYTE $0 +#line 827 "file1.s" +/*0x01c7*/ BYTE $0 +#line 827 "file1.s" +/*0x01c8*/ BYTE $0 +#line 827 "file1.s" +/*0x01c9*/ BYTE $0 +#line 783 "file1.s" +/*0x01ca*/ BYTE $0 +#line 783 "file1.s" +/*0x01cb*/ BYTE $0 +#line 783 "file1.s" +/*0x01cc*/ BYTE $0 +#line 783 "file1.s" +/*0x01cd*/ BYTE $0 +#line 367 "file1.s" +/*0x01ce*/ MOVQ $0x123456789, AX +#line 367 "file1.s" +/*0x01d8*/ MOVL $0x1234567, AX +#line 367 "file1.s" +/*0x01dd*/ BYTE $0 +#line 367 "file1.s" +/*0x01de*/ BYTE $0 +#line 367 "file1.s" +/*0x01df*/ BYTE $0 +#line 367 "file1.s" +/*0x01e0*/ BYTE $0 +#line 581 "file1.s" +/*0x01e1*/ BYTE $0 +#line 581 "file1.s" +/*0x01e2*/ BYTE $0 +#line 581 "file1.s" +/*0x01e3*/ BYTE $0 +#line 638 "file1.s" +/*0x01e4*/ MOVQ $0x123456789, AX +#line 638 "file1.s" +/*0x01ee*/ MOVL $0x1234567, AX +#line 638 "file1.s" +/*0x01f3*/ BYTE $0 +#line 638 "file1.s" +/*0x01f4*/ BYTE $0 +#line 638 "file1.s" +/*0x01f5*/ BYTE $0 +#line 359 "file1.s" +/*0x01f6*/ BYTE $0 +#line 449 "file1.s" +/*0x01f7*/ BYTE $0 +#line 449 "file1.s" +/*0x01f8*/ BYTE $0 +#line 449 "file1.s" +/*0x01f9*/ BYTE $0 +#line 298 "file1.s" +/*0x01fa*/ BYTE $0 +#line 298 "file1.s" +/*0x01fb*/ BYTE $0 +#line 257 "file1.s" +/*0x01fc*/ BYTE $0 +#line 257 "file1.s" +/*0x01fd*/ BYTE $0 +#line 257 "file1.s" +/*0x01fe*/ BYTE $0 +#line 512 "file1.s" +/*0x01ff*/ MOVL $0x1234567, AX +#line 512 "file1.s" +/*0x0204*/ BYTE $0 +#line 512 "file1.s" +/*0x0205*/ BYTE $0 +#line 617 "file1.s" +/*0x0206*/ MOVL $0x1234567, AX +#line 478 "file1.s" +/*0x020b*/ BYTE $0 +/* */ PCDATA $2, $351 +#line 478 "file1.s" +/*0x020c*/ BYTE $0 +#line 958 "file1.s" +/*0x020d*/ BYTE $0 +#line 958 "file1.s" +/*0x020e*/ BYTE $0 +#line 958 "file1.s" +/*0x020f*/ BYTE $0 +#line 958 "file1.s" +/*0x0210*/ BYTE $0 +#line 371 "file1.s" +/*0x0211*/ MOVQ $0x123456789, AX +#line 371 "file1.s" +/*0x021b*/ BYTE $0 +#line 371 "file1.s" +/*0x021c*/ BYTE $0 +#line 56 "file3.s" +/*0x021d*/ MOVL $0x1234567, AX +#line 56 "file3.s" +/*0x0222*/ BYTE $0 +#line 56 "file3.s" +/*0x0223*/ BYTE $0 +#line 56 "file9.s" +/*0x0224*/ MOVQ $0x123456789, AX +#line 56 "file9.s" +/*0x022e*/ BYTE $0 +#line 56 "file9.s" +/*0x022f*/ BYTE $0 +#line 56 "file9.s" +/*0x0230*/ BYTE $0 +#line 56 "file9.s" +/*0x0231*/ BYTE $0 +#line 684 "file9.s" +/*0x0232*/ MOVQ $0x123456789, AX +#line 684 "file9.s" +/*0x023c*/ BYTE $0 +#line 684 "file9.s" +/*0x023d*/ POPQ AX +#line 407 "file9.s" +/*0x023e*/ MOVL $0x1234567, AX +#line 407 "file9.s" +/*0x0243*/ BYTE $0 + PUSHQ AX + PUSHQ AX + RET + +GLOBL funcdata1(SB), $16 +#line 1 "input" +TEXT func2(SB),7,$40-688 + FUNCDATA $1, funcdata2(SB) +#line 1 "input" +#line 1 "input" +/*0x0004*/ BYTE $0 +#line 860 "input" +/*0x0005*/ BYTE $0 +#line 860 "input" +/*0x0006*/ BYTE $0 +#line 860 "input" +/*0x0007*/ BYTE $0 +#line 860 "input" +/*0x0008*/ BYTE $0 +#line 85 "input" +/*0x0009*/ BYTE $0 +#line 85 "input" +/*0x000a*/ BYTE $0 +#line 355 "input" +/*0x000b*/ MOVQ $0x123456789, AX +#line 355 "input" +/*0x0015*/ MOVL $0x1234567, AX +#line 355 "input" +/*0x001a*/ BYTE $0 +#line 355 "input" +/*0x001b*/ BYTE $0 +#line 355 "input" +/*0x001c*/ BYTE $0 +#line 840 "input" +/*0x001d*/ MOVL $0x1234567, AX +#line 840 "input" +/*0x0022*/ BYTE $0 +#line 294 "input" +/*0x0023*/ MOVQ $0x123456789, AX +#line 294 "input" +/*0x002d*/ MOVQ $0x123456789, AX +#line 294 "input" +/*0x0037*/ MOVQ $0x123456789, AX +#line 294 "input" +/*0x0041*/ BYTE $0 +#line 294 "input" +/*0x0042*/ BYTE $0 +#line 294 "input" +/*0x0043*/ BYTE $0 +#line 294 "input" +/*0x0044*/ BYTE $0 +/* */ PCDATA $1, $385 +#line 294 "input" +/*0x0045*/ BYTE $0 +#line 294 "input" +/*0x0046*/ BYTE $0 +#line 294 "input" +/*0x0047*/ BYTE $0 +#line 81 "file9.s" +/*0x0048*/ MOVL $0x1234567, AX +#line 81 "file9.s" +/*0x004d*/ BYTE $0 +#line 81 "file9.s" +/*0x004e*/ BYTE $0 +#line 81 "file9.s" +/*0x004f*/ POPQ AX +#line 81 "file9.s" +/*0x0050*/ MOVL $0x1234567, AX +#line 81 "file9.s" +/*0x0055*/ BYTE $0 +/* */ PCDATA $1, $701 +#line 81 "file9.s" +/*0x0056*/ MOVL $0x1234567, AX +#line 81 "file9.s" +/*0x005b*/ BYTE $0 +#line 81 "file9.s" +/*0x005c*/ BYTE $0 +#line 81 "file9.s" +/*0x005d*/ BYTE $0 +#line 81 "file9.s" +/*0x005e*/ BYTE $0 +#line 290 "file9.s" +/*0x005f*/ BYTE $0 +#line 290 "file9.s" +/*0x0060*/ BYTE $0 +#line 290 "file9.s" +/*0x0061*/ BYTE $0 +#line 197 "file9.s" +/*0x0062*/ MOVL $0x1234567, AX +#line 197 "file9.s" +/*0x0067*/ BYTE $0 +#line 608 "file9.s" +/*0x0068*/ MOVQ $0x123456789, AX +#line 608 "file9.s" +/*0x0072*/ MOVQ $0x123456789, AX +#line 608 "file9.s" +/*0x007c*/ BYTE $0 +/* */ PCDATA $1, $562 +#line 608 "file9.s" +/*0x007d*/ BYTE $0 +#line 608 "file9.s" +/*0x007e*/ BYTE $0 +#line 189 "file9.s" +/*0x007f*/ MOVL $0x1234567, AX +#line 189 "file9.s" +/*0x0084*/ BYTE $0 +#line 189 "file9.s" +/*0x0085*/ BYTE $0 +#line 189 "file9.s" +/*0x0086*/ BYTE $0 +#line 189 "file9.s" +/*0x0087*/ BYTE $0 +#line 472 "file9.s" +/*0x0088*/ MOVL $0x1234567, AX +#line 472 "file9.s" +/*0x008d*/ BYTE $0 +#line 472 "file9.s" +/*0x008e*/ BYTE $0 +#line 472 "file9.s" +/*0x008f*/ PUSHQ AX +#line 472 "file9.s" +/*0x0090*/ MOVQ $0x123456789, AX +#line 472 "file9.s" +/*0x009a*/ MOVL $0x1234567, AX +#line 472 "file9.s" +/*0x009f*/ BYTE $0 +#line 472 "file9.s" +/*0x00a0*/ BYTE $0 +#line 472 "file9.s" +/*0x00a1*/ BYTE $0 +#line 472 "file9.s" +/*0x00a2*/ BYTE $0 +#line 148 "file9.s" +/*0x00a3*/ MOVQ $0x123456789, AX +#line 148 "file9.s" +/*0x00ad*/ BYTE $0 +#line 148 "file9.s" +/*0x00ae*/ BYTE $0 +#line 148 "file9.s" +/*0x00af*/ BYTE $0 +#line 148 "file9.s" +/*0x00b0*/ BYTE $0 +#line 562 "file9.s" +/*0x00b1*/ MOVL $0x1234567, AX +#line 562 "file9.s" +/*0x00b6*/ BYTE $0 +#line 562 "file9.s" +/*0x00b7*/ PUSHQ AX +#line 562 "file9.s" +/*0x00b8*/ BYTE $0 +#line 532 "file9.s" +/*0x00b9*/ MOVQ $0x123456789, AX +#line 532 "file9.s" +/*0x00c3*/ MOVQ $0x123456789, AX +#line 282 "file9.s" +/*0x00cd*/ BYTE $0 +#line 282 "file9.s" +/*0x00ce*/ BYTE $0 +#line 282 "file9.s" +/*0x00cf*/ BYTE $0 +/* */ PCDATA $2, $861 +#line 282 "file9.s" +/*0x00d0*/ BYTE $0 +#line 282 "file9.s" +/*0x00d1*/ BYTE $0 +/* */ PCDATA $2, $310 +#line 282 "file9.s" +/*0x00d2*/ BYTE $0 +#line 416 "file9.s" +/*0x00d3*/ MOVQ $0x123456789, AX +#line 416 "file9.s" +/*0x00dd*/ MOVL $0x1234567, AX +#line 780 "file9.s" +/*0x00e2*/ BYTE $0 +#line 780 "file9.s" +/*0x00e3*/ BYTE $0 +#line 765 "file9.s" +/*0x00e4*/ MOVL $0x1234567, AX +#line 523 "file9.s" +/*0x00e9*/ BYTE $0 +#line 523 "file9.s" +/*0x00ea*/ BYTE $0 +#line 523 "file9.s" +/*0x00eb*/ BYTE $0 +#line 733 "file9.s" +/*0x00ec*/ MOVQ $0x123456789, AX +#line 378 "file9.s" +/*0x00f6*/ BYTE $0 +#line 378 "file9.s" +/*0x00f7*/ BYTE $0 +#line 378 "file9.s" +/*0x00f8*/ BYTE $0 +#line 378 "file9.s" +/*0x00f9*/ BYTE $0 +#line 540 "file9.s" +/*0x00fa*/ BYTE $0 +#line 540 "file9.s" +/*0x00fb*/ BYTE $0 +#line 57 "file9.s" +/*0x00fc*/ BYTE $0 +#line 57 "file9.s" +/*0x00fd*/ BYTE $0 +#line 57 "file9.s" +/*0x00fe*/ BYTE $0 +#line 417 "file9.s" +/*0x00ff*/ BYTE $0 +/* */ PCDATA $1, $932 +#line 417 "file9.s" +/*0x0100*/ BYTE $0 +#line 417 "file9.s" +/*0x0101*/ BYTE $0 +#line 417 "file9.s" +/*0x0102*/ BYTE $0 +#line 417 "file9.s" +/*0x0103*/ BYTE $0 +#line 713 "file9.s" +/*0x0104*/ MOVL $0x1234567, AX +#line 610 "file0.s" +/*0x0109*/ MOVQ $0x123456789, AX +#line 610 "file0.s" +/*0x0113*/ MOVL $0x1234567, AX +#line 610 "file0.s" +/*0x0118*/ BYTE $0 +#line 787 "file0.s" +/*0x0119*/ MOVQ $0x123456789, AX +#line 829 "file0.s" +/*0x0123*/ BYTE $0 +#line 829 "file0.s" +/*0x0124*/ BYTE $0 +#line 829 "file0.s" +/*0x0125*/ BYTE $0 +#line 849 "file0.s" +/*0x0126*/ BYTE $0 +#line 849 "file0.s" +/*0x0127*/ BYTE $0 +#line 849 "file0.s" +/*0x0128*/ BYTE $0 +/* */ PCDATA $2, $356 +#line 849 "file0.s" +/*0x0129*/ BYTE $0 +#line 849 "file0.s" +/*0x012a*/ BYTE $0 +#line 88 "file0.s" +/*0x012b*/ MOVL $0x1234567, AX +#line 88 "file0.s" +/*0x0130*/ BYTE $0 +#line 88 "file0.s" +/*0x0131*/ BYTE $0 +#line 88 "file0.s" +/*0x0132*/ BYTE $0 +#line 684 "file0.s" +/*0x0133*/ BYTE $0 +#line 684 "file0.s" +/*0x0134*/ BYTE $0 +#line 684 "file0.s" +/*0x0135*/ BYTE $0 +#line 684 "file0.s" +/*0x0136*/ BYTE $0 +#line 238 "file0.s" +/*0x0137*/ BYTE $0 +#line 238 "file0.s" +/*0x0138*/ BYTE $0 +#line 238 "file0.s" +/*0x0139*/ PUSHQ AX +#line 238 "file0.s" +/*0x013a*/ BYTE $0 +#line 238 "file0.s" +/*0x013b*/ BYTE $0 +#line 603 "file0.s" +/*0x013c*/ BYTE $0 +#line 981 "file0.s" +/*0x013d*/ BYTE $0 +#line 981 "file0.s" +/*0x013e*/ POPQ AX +#line 616 "file0.s" +/*0x013f*/ BYTE $0 +#line 616 "file0.s" +/*0x0140*/ BYTE $0 +#line 616 "file0.s" +/*0x0141*/ BYTE $0 +#line 616 "file0.s" +/*0x0142*/ BYTE $0 +#line 716 "file0.s" +/*0x0143*/ MOVL $0x1234567, AX +#line 716 "file0.s" +/*0x0148*/ BYTE $0 +#line 716 "file0.s" +/*0x0149*/ BYTE $0 +#line 34 "file0.s" +/*0x014a*/ BYTE $0 +#line 34 "file0.s" +/*0x014b*/ BYTE $0 +#line 34 "file0.s" +/*0x014c*/ BYTE $0 +#line 90 "file0.s" +/*0x014d*/ MOVL $0x1234567, AX +#line 316 "file0.s" +/*0x0152*/ MOVQ $0x123456789, AX +#line 230 "file0.s" +/*0x015c*/ MOVQ $0x123456789, AX +#line 799 "file0.s" +/*0x0166*/ MOVQ $0x123456789, AX +#line 799 "file0.s" +/*0x0170*/ BYTE $0 +#line 799 "file0.s" +/*0x0171*/ BYTE $0 +/* */ PCDATA $1, $247 +#line 799 "file0.s" +/*0x0172*/ BYTE $0 +#line 799 "file0.s" +/*0x0173*/ BYTE $0 +#line 799 "file0.s" +/*0x0174*/ BYTE $0 +#line 655 "file0.s" +/*0x0175*/ MOVL $0x1234567, AX +#line 655 "file0.s" +/*0x017a*/ BYTE $0 +#line 551 "file0.s" +/*0x017b*/ BYTE $0 +#line 551 "file0.s" +/*0x017c*/ BYTE $0 +#line 271 "file0.s" +/*0x017d*/ MOVQ $0x123456789, AX +#line 271 "file0.s" +/*0x0187*/ MOVQ $0x123456789, AX +#line 271 "file0.s" +/*0x0191*/ MOVL $0x1234567, AX +#line 271 "file0.s" +/*0x0196*/ BYTE $0 +#line 271 "file0.s" +/*0x0197*/ BYTE $0 +#line 271 "file0.s" +/*0x0198*/ BYTE $0 +#line 233 "file0.s" +/*0x0199*/ MOVL $0x1234567, AX +#line 233 "file0.s" +/*0x019e*/ BYTE $0 +#line 233 "file0.s" +/*0x019f*/ BYTE $0 +#line 233 "file0.s" +/*0x01a0*/ BYTE $0 +#line 233 "file0.s" +/*0x01a1*/ BYTE $0 +#line 738 "file0.s" +/*0x01a2*/ MOVL $0x1234567, AX +#line 738 "file0.s" +/*0x01a7*/ BYTE $0 +#line 800 "file0.s" +/*0x01a8*/ BYTE $0 +#line 800 "file0.s" +/*0x01a9*/ BYTE $0 +#line 646 "file0.s" +/*0x01aa*/ MOVQ $0x123456789, AX +#line 646 "file0.s" +/*0x01b4*/ BYTE $0 +#line 646 "file0.s" +/*0x01b5*/ BYTE $0 +#line 646 "file0.s" +/*0x01b6*/ BYTE $0 +#line 709 "file0.s" +/*0x01b7*/ BYTE $0 +#line 709 "file0.s" +/*0x01b8*/ BYTE $0 +#line 709 "file0.s" +/*0x01b9*/ BYTE $0 +#line 709 "file0.s" +/*0x01ba*/ BYTE $0 +#line 296 "file0.s" +/*0x01bb*/ BYTE $0 +#line 296 "file0.s" +/*0x01bc*/ BYTE $0 +#line 296 "file0.s" +/*0x01bd*/ BYTE $0 +#line 71 "file0.s" +/*0x01be*/ BYTE $0 +#line 71 "file0.s" +/*0x01bf*/ BYTE $0 +#line 71 "file0.s" +/*0x01c0*/ BYTE $0 +#line 7 "file2.s" +/*0x01c1*/ BYTE $0 +#line 747 "file2.s" +/*0x01c2*/ BYTE $0 +#line 177 "file2.s" +/*0x01c3*/ MOVQ $0x123456789, AX +#line 177 "file2.s" +/*0x01cd*/ MOVQ $0x123456789, AX +#line 177 "file2.s" +/*0x01d7*/ MOVL $0x1234567, AX +#line 177 "file2.s" +/*0x01dc*/ BYTE $0 +#line 177 "file2.s" +/*0x01dd*/ BYTE $0 +#line 177 "file2.s" +/*0x01de*/ BYTE $0 +#line 72 "file2.s" +/*0x01df*/ BYTE $0 +#line 215 "file2.s" +/*0x01e0*/ MOVL $0x1234567, AX +#line 215 "file2.s" +/*0x01e5*/ BYTE $0 +#line 215 "file2.s" +/*0x01e6*/ BYTE $0 +#line 215 "file2.s" +/*0x01e7*/ BYTE $0 +#line 946 "file2.s" +/*0x01e8*/ BYTE $0 +#line 946 "file2.s" +/*0x01e9*/ BYTE $0 +#line 946 "file2.s" +/*0x01ea*/ BYTE $0 +#line 946 "file2.s" +/*0x01eb*/ BYTE $0 +#line 263 "file2.s" +/*0x01ec*/ MOVL $0x1234567, AX +#line 263 "file2.s" +/*0x01f1*/ BYTE $0 +#line 897 "file2.s" +/*0x01f2*/ MOVQ $0x123456789, AX +#line 897 "file2.s" +/*0x01fc*/ MOVQ $0x123456789, AX +#line 897 "file2.s" +/*0x0206*/ BYTE $0 +#line 897 "file2.s" +/*0x0207*/ BYTE $0 +#line 897 "file2.s" +/*0x0208*/ BYTE $0 +#line 229 "file2.s" +/*0x0209*/ BYTE $0 +#line 229 "file2.s" +/*0x020a*/ BYTE $0 +#line 229 "file2.s" +/*0x020b*/ BYTE $0 +#line 229 "file2.s" +/*0x020c*/ BYTE $0 +/* */ PCDATA $1, $7 +#line 229 "file2.s" +/*0x020d*/ MOVL $0x1234567, AX +#line 229 "file2.s" +/*0x0212*/ BYTE $0 +#line 353 "file2.s" +/*0x0213*/ BYTE $0 +#line 353 "file2.s" +/*0x0214*/ BYTE $0 +#line 353 "file2.s" +/*0x0215*/ BYTE $0 +#line 353 "file2.s" +/*0x0216*/ BYTE $0 +#line 852 "file2.s" +/*0x0217*/ BYTE $0 +#line 852 "file2.s" +/*0x0218*/ BYTE $0 +#line 852 "file2.s" +/*0x0219*/ BYTE $0 +#line 852 "file2.s" +/*0x021a*/ BYTE $0 +#line 852 "file2.s" +/*0x021b*/ PUSHQ AX +#line 852 "file2.s" +/*0x021c*/ BYTE $0 +#line 852 "file2.s" +/*0x021d*/ BYTE $0 +#line 852 "file2.s" +/*0x021e*/ BYTE $0 +#line 904 "file2.s" +/*0x021f*/ MOVQ $0x123456789, AX +#line 904 "file2.s" +/*0x0229*/ BYTE $0 +#line 904 "file2.s" +/*0x022a*/ BYTE $0 +#line 882 "file2.s" +/*0x022b*/ BYTE $0 +#line 905 "file2.s" +/*0x022c*/ BYTE $0 +#line 410 "file2.s" +/*0x022d*/ MOVQ $0x123456789, AX +#line 410 "file2.s" +/*0x0237*/ BYTE $0 +#line 410 "file2.s" +/*0x0238*/ BYTE $0 +#line 410 "file2.s" +/*0x0239*/ POPQ AX +#line 410 "file2.s" +/*0x023a*/ BYTE $0 +#line 410 "file2.s" +/*0x023b*/ BYTE $0 +#line 410 "file2.s" +/*0x023c*/ BYTE $0 +/* */ PCDATA $2, $173 +#line 410 "file2.s" +/*0x023d*/ MOVL $0x1234567, AX +#line 410 "file2.s" +/*0x0242*/ BYTE $0 +/* */ PCDATA $1, $396 +#line 410 "file2.s" +/*0x0243*/ BYTE $0 +#line 410 "file2.s" +/*0x0244*/ BYTE $0 +#line 666 "file2.s" +/*0x0245*/ BYTE $0 +#line 129 "file2.s" +/*0x0246*/ MOVQ $0x123456789, AX +#line 129 "file2.s" +/*0x0250*/ BYTE $0 +#line 391 "file2.s" +/*0x0251*/ BYTE $0 +#line 696 "file2.s" +/*0x0252*/ MOVL $0x1234567, AX +#line 940 "file2.s" +/*0x0257*/ BYTE $0 +#line 940 "file2.s" +/*0x0258*/ BYTE $0 +#line 606 "file2.s" +/*0x0259*/ MOVL $0x1234567, AX +#line 606 "file2.s" +/*0x025e*/ BYTE $0 +#line 648 "file2.s" +/*0x025f*/ MOVQ $0x123456789, AX +#line 648 "file2.s" +/*0x0269*/ BYTE $0 +#line 648 "file2.s" +/*0x026a*/ BYTE $0 +/* */ PCDATA $2, $84 +#line 648 "file2.s" +/*0x026b*/ MOVQ $0x123456789, AX +#line 648 "file2.s" +/*0x0275*/ MOVQ $0x123456789, AX +#line 648 "file2.s" +/*0x027f*/ MOVQ $0x123456789, AX +#line 648 "file2.s" +/*0x0289*/ MOVQ $0x123456789, AX +#line 648 "file2.s" +/*0x0293*/ MOVQ $0x123456789, AX +#line 648 "file2.s" +/*0x029d*/ MOVL $0x1234567, AX +#line 648 "file2.s" +/*0x02a2*/ PUSHQ AX +#line 648 "file2.s" +/*0x02a3*/ MOVL $0x1234567, AX +#line 648 "file2.s" +/*0x02a8*/ BYTE $0 +#line 648 "file2.s" +/*0x02a9*/ BYTE $0 +#line 648 "file2.s" +/*0x02aa*/ BYTE $0 +#line 648 "file2.s" +/*0x02ab*/ BYTE $0 +#line 449 "file2.s" +/*0x02ac*/ MOVQ $0x123456789, AX +#line 449 "file2.s" +/*0x02b6*/ MOVL $0x1234567, AX +#line 284 "file2.s" +/*0x02bb*/ BYTE $0 +#line 284 "file2.s" +/*0x02bc*/ BYTE $0 +#line 284 "file2.s" +/*0x02bd*/ BYTE $0 +#line 284 "file2.s" +/*0x02be*/ BYTE $0 +#line 26 "file2.s" +/*0x02bf*/ MOVQ $0x123456789, AX +#line 26 "file2.s" +/*0x02c9*/ MOVL $0x1234567, AX +#line 26 "file2.s" +/*0x02ce*/ BYTE $0 +#line 26 "file2.s" +/*0x02cf*/ BYTE $0 +#line 26 "file2.s" +/*0x02d0*/ BYTE $0 +#line 605 "file2.s" +/*0x02d1*/ MOVL $0x1234567, AX +#line 605 "file2.s" +/*0x02d6*/ BYTE $0 +#line 605 "file2.s" +/*0x02d7*/ BYTE $0 +#line 605 "file2.s" +/*0x02d8*/ BYTE $0 +#line 593 "file2.s" +/*0x02d9*/ MOVL $0x1234567, AX +#line 541 "file2.s" +/*0x02de*/ MOVL $0x1234567, AX +#line 541 "file2.s" +/*0x02e3*/ BYTE $0 +#line 541 "file2.s" +/*0x02e4*/ BYTE $0 +#line 181 "file2.s" +/*0x02e5*/ MOVQ $0x123456789, AX +#line 181 "file2.s" +/*0x02ef*/ BYTE $0 +#line 907 "file2.s" +/*0x02f0*/ MOVQ $0x123456789, AX +#line 704 "file2.s" +/*0x02fa*/ MOVQ $0x123456789, AX +#line 704 "file2.s" +/*0x0304*/ MOVQ $0x123456789, AX +#line 704 "file2.s" +/*0x030e*/ MOVL $0x1234567, AX +#line 704 "file2.s" +/*0x0313*/ BYTE $0 +#line 704 "file2.s" +/*0x0314*/ BYTE $0 +#line 704 "file2.s" +/*0x0315*/ BYTE $0 +#line 704 "file2.s" +/*0x0316*/ BYTE $0 +#line 859 "file2.s" +/*0x0317*/ MOVL $0x1234567, AX +#line 407 "file2.s" +/*0x031c*/ BYTE $0 +#line 407 "file2.s" +/*0x031d*/ BYTE $0 +/* */ PCDATA $2, $569 +#line 407 "file2.s" +/*0x031e*/ MOVL $0x1234567, AX +#line 407 "file2.s" +/*0x0323*/ BYTE $0 +#line 407 "file2.s" +/*0x0324*/ BYTE $0 +#line 407 "file2.s" +/*0x0325*/ BYTE $0 +/* */ PCDATA $1, $937 +#line 407 "file2.s" +/*0x0326*/ MOVL $0x1234567, AX +#line 407 "file2.s" +/*0x032b*/ BYTE $0 +#line 774 "file2.s" +/*0x032c*/ MOVQ $0x123456789, AX +#line 774 "file2.s" +/*0x0336*/ BYTE $0 +#line 547 "file2.s" +/*0x0337*/ BYTE $0 +#line 547 "file2.s" +/*0x0338*/ BYTE $0 +#line 547 "file2.s" +/*0x0339*/ BYTE $0 +#line 547 "file2.s" +/*0x033a*/ PUSHQ AX +#line 547 "file2.s" +/*0x033b*/ MOVL $0x1234567, AX +#line 427 "file2.s" +/*0x0340*/ MOVL $0x1234567, AX +/* */ PCDATA $1, $462 +#line 427 "file2.s" +/*0x0345*/ MOVQ $0x123456789, AX +#line 427 "file2.s" +/*0x034f*/ MOVL $0x1234567, AX +#line 427 "file2.s" +/*0x0354*/ BYTE $0 +#line 427 "file2.s" +/*0x0355*/ BYTE $0 +#line 427 "file2.s" +/*0x0356*/ BYTE $0 +#line 427 "file2.s" +/*0x0357*/ BYTE $0 +/* */ PCDATA $2, $303 +#line 427 "file2.s" +/*0x0358*/ MOVQ $0x123456789, AX +#line 427 "file2.s" +/*0x0362*/ BYTE $0 +#line 427 "file2.s" +/*0x0363*/ BYTE $0 +#line 708 "file2.s" +/*0x0364*/ BYTE $0 +#line 708 "file2.s" +/*0x0365*/ BYTE $0 +#line 708 "file2.s" +/*0x0366*/ BYTE $0 +#line 708 "file2.s" +/*0x0367*/ BYTE $0 +#line 218 "file2.s" +/*0x0368*/ MOVL $0x1234567, AX +#line 44 "file2.s" +/*0x036d*/ BYTE $0 +#line 915 "file2.s" +/*0x036e*/ BYTE $0 +#line 915 "file2.s" +/*0x036f*/ BYTE $0 +#line 915 "file2.s" +/*0x0370*/ BYTE $0 +#line 915 "file2.s" +/*0x0371*/ BYTE $0 +#line 122 "file2.s" +/*0x0372*/ MOVQ $0x123456789, AX +#line 122 "file2.s" +/*0x037c*/ MOVL $0x1234567, AX +#line 122 "file2.s" +/*0x0381*/ BYTE $0 +#line 122 "file2.s" +/*0x0382*/ BYTE $0 +#line 266 "file2.s" +/*0x0383*/ BYTE $0 +#line 266 "file2.s" +/*0x0384*/ BYTE $0 +#line 368 "file2.s" +/*0x0385*/ BYTE $0 +#line 368 "file2.s" +/*0x0386*/ BYTE $0 +#line 368 "file2.s" +/*0x0387*/ BYTE $0 +#line 368 "file2.s" +/*0x0388*/ BYTE $0 +#line 775 "file2.s" +/*0x0389*/ BYTE $0 +#line 10 "file8.s" +/*0x038a*/ BYTE $0 +#line 10 "file8.s" +/*0x038b*/ BYTE $0 +#line 422 "file8.s" +/*0x038c*/ MOVL $0x1234567, AX +#line 422 "file8.s" +/*0x0391*/ BYTE $0 +#line 613 "file8.s" +/*0x0392*/ BYTE $0 +#line 613 "file8.s" +/*0x0393*/ BYTE $0 +#line 613 "file8.s" +/*0x0394*/ BYTE $0 +#line 697 "file8.s" +/*0x0395*/ MOVQ $0x123456789, AX +#line 697 "file8.s" +/*0x039f*/ MOVQ $0x123456789, AX +#line 697 "file8.s" +/*0x03a9*/ BYTE $0 +#line 697 "file8.s" +/*0x03aa*/ BYTE $0 +#line 697 "file8.s" +/*0x03ab*/ BYTE $0 + POPQ AX + POPQ AX + POPQ AX + RET + +GLOBL funcdata2(SB), $16 + +TEXT start(SB),7,$0 + CALL func0(SB) + CALL func1(SB) + CALL func2(SB) + MOVQ $runtime·pclntab(SB), AX + + RET diff --git a/src/cmd/newlink/util.go b/src/cmd/newlink/util.go new file mode 100644 index 0000000000..b8a6b2cb30 --- /dev/null +++ b/src/cmd/newlink/util.go @@ -0,0 +1,11 @@ +// Copyright 2014 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 + +// round returns size rounded up to the next multiple of align; +// align must be a power of two. +func round(size, align Addr) Addr { + return (size + align - 1) &^ (align - 1) +} diff --git a/src/cmd/newlink/write.go b/src/cmd/newlink/write.go new file mode 100644 index 0000000000..7e11b2fe71 --- /dev/null +++ b/src/cmd/newlink/write.go @@ -0,0 +1,14 @@ +// Copyright 2014 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. + +// Writing of executable and (for hostlink mode) object files. + +package main + +import "io" + +func (p *Prog) write(w io.Writer) { + p.Entry = p.Syms[p.startSym].Addr + p.formatter.write(w, p) +} -- cgit v1.3 From 17eba6e6b72b9dbf24d73a84be22edd65c229631 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 21 May 2015 13:28:10 -0400 Subject: 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/5g/cgen.go | 229 - src/cmd/5g/cgen64.go | 859 -- src/cmd/5g/galign.go | 94 - src/cmd/5g/ggen.go | 529 - src/cmd/5g/gsubr.go | 1209 --- src/cmd/5g/peep.go | 1748 ---- src/cmd/5g/prog.go | 165 - src/cmd/5g/reg.go | 136 - src/cmd/5l/asm.go | 651 -- src/cmd/5l/l.go | 79 - src/cmd/5l/obj.go | 178 - src/cmd/6g/cgen.go | 151 - src/cmd/6g/galign.go | 129 - src/cmd/6g/ggen.go | 743 -- src/cmd/6g/gsubr.go | 1380 --- src/cmd/6g/peep.go | 1038 -- src/cmd/6g/prog.go | 308 - src/cmd/6g/reg.go | 154 - src/cmd/6l/asm.go | 817 -- src/cmd/6l/l.go | 47 - src/cmd/6l/obj.go | 222 - src/cmd/6l/z.go | 1 - src/cmd/7g/cgen.go | 157 - src/cmd/7g/galign.go | 93 - src/cmd/7g/ggen.go | 532 - src/cmd/7g/gsubr.go | 983 -- src/cmd/7g/peep.go | 809 -- src/cmd/7g/prog.go | 174 - src/cmd/7g/reg.go | 171 - src/cmd/7l/asm.go | 435 - src/cmd/7l/l.go | 78 - src/cmd/7l/obj.go | 169 - src/cmd/8g/cgen.go | 159 - src/cmd/8g/cgen64.go | 598 -- src/cmd/8g/galign.go | 110 - src/cmd/8g/ggen.go | 940 -- src/cmd/8g/gsubr.go | 1798 ---- src/cmd/8g/peep.go | 814 -- src/cmd/8g/prog.go | 292 - src/cmd/8g/reg.go | 112 - src/cmd/8l/asm.go | 643 -- src/cmd/8l/l.go | 47 - src/cmd/8l/obj.go | 187 - src/cmd/9g/cgen.go | 149 - src/cmd/9g/galign.go | 100 - src/cmd/9g/ggen.go | 558 - src/cmd/9g/gsubr.go | 1031 -- src/cmd/9g/opt.go | 12 - src/cmd/9g/peep.go | 1051 -- src/cmd/9g/prog.go | 314 - src/cmd/9g/reg.go | 162 - src/cmd/9l/asm.go | 737 -- src/cmd/9l/l.go | 78 - src/cmd/9l/obj.go | 162 - src/cmd/compile/internal/amd64/cgen.go | 151 + src/cmd/compile/internal/amd64/galign.go | 129 + src/cmd/compile/internal/amd64/ggen.go | 743 ++ src/cmd/compile/internal/amd64/gsubr.go | 1380 +++ src/cmd/compile/internal/amd64/peep.go | 1038 ++ src/cmd/compile/internal/amd64/prog.go | 308 + src/cmd/compile/internal/amd64/reg.go | 154 + src/cmd/compile/internal/arm/cgen.go | 229 + src/cmd/compile/internal/arm/cgen64.go | 859 ++ src/cmd/compile/internal/arm/galign.go | 94 + src/cmd/compile/internal/arm/ggen.go | 529 + src/cmd/compile/internal/arm/gsubr.go | 1209 +++ src/cmd/compile/internal/arm/peep.go | 1748 ++++ src/cmd/compile/internal/arm/prog.go | 165 + src/cmd/compile/internal/arm/reg.go | 136 + src/cmd/compile/internal/arm64/cgen.go | 157 + src/cmd/compile/internal/arm64/galign.go | 93 + src/cmd/compile/internal/arm64/ggen.go | 532 + src/cmd/compile/internal/arm64/gsubr.go | 983 ++ src/cmd/compile/internal/arm64/peep.go | 809 ++ src/cmd/compile/internal/arm64/prog.go | 174 + src/cmd/compile/internal/arm64/reg.go | 171 + src/cmd/compile/internal/big/accuracy_string.go | 17 + src/cmd/compile/internal/big/arith.go | 291 + src/cmd/compile/internal/big/arith_decl.go | 53 + src/cmd/compile/internal/big/arith_test.go | 456 + src/cmd/compile/internal/big/bits_test.go | 224 + src/cmd/compile/internal/big/calibrate_test.go | 88 + src/cmd/compile/internal/big/decimal.go | 258 + src/cmd/compile/internal/big/decimal_test.go | 106 + src/cmd/compile/internal/big/example_test.go | 51 + src/cmd/compile/internal/big/float.go | 1681 +++ src/cmd/compile/internal/big/float_test.go | 1664 +++ src/cmd/compile/internal/big/floatconv.go | 373 + src/cmd/compile/internal/big/floatconv_test.go | 397 + src/cmd/compile/internal/big/floatexample_test.go | 111 + src/cmd/compile/internal/big/ftoa.go | 190 + src/cmd/compile/internal/big/gcd_test.go | 47 + src/cmd/compile/internal/big/hilbert_test.go | 160 + src/cmd/compile/internal/big/int.go | 848 ++ src/cmd/compile/internal/big/int_test.go | 1387 +++ src/cmd/compile/internal/big/intconv.go | 228 + src/cmd/compile/internal/big/intconv_test.go | 342 + src/cmd/compile/internal/big/nat.go | 1155 ++ src/cmd/compile/internal/big/nat_test.go | 518 + src/cmd/compile/internal/big/natconv.go | 495 + src/cmd/compile/internal/big/natconv_test.go | 425 + src/cmd/compile/internal/big/rat.go | 570 + src/cmd/compile/internal/big/rat_test.go | 736 ++ src/cmd/compile/internal/big/ratconv.go | 251 + src/cmd/compile/internal/big/ratconv_test.go | 451 + .../compile/internal/big/roundingmode_string.go | 16 + src/cmd/compile/internal/big/vendor.bash | 25 + src/cmd/compile/internal/gc/align.go | 706 ++ src/cmd/compile/internal/gc/builtin.go | 168 + src/cmd/compile/internal/gc/builtin/runtime.go | 194 + src/cmd/compile/internal/gc/builtin/unsafe.go | 18 + src/cmd/compile/internal/gc/bv.go | 195 + src/cmd/compile/internal/gc/cgen.go | 3564 +++++++ src/cmd/compile/internal/gc/closure.go | 689 ++ src/cmd/compile/internal/gc/const.go | 1717 +++ src/cmd/compile/internal/gc/cplx.go | 480 + src/cmd/compile/internal/gc/dcl.go | 1494 +++ src/cmd/compile/internal/gc/esc.go | 1758 ++++ src/cmd/compile/internal/gc/export.go | 560 + src/cmd/compile/internal/gc/fmt.go | 1736 ++++ src/cmd/compile/internal/gc/gen.go | 1279 +++ src/cmd/compile/internal/gc/go.go | 841 ++ src/cmd/compile/internal/gc/go.y | 2312 ++++ src/cmd/compile/internal/gc/gsubr.go | 827 ++ src/cmd/compile/internal/gc/init.go | 189 + src/cmd/compile/internal/gc/inl.go | 1008 ++ src/cmd/compile/internal/gc/lex.go | 2601 +++++ src/cmd/compile/internal/gc/mkbuiltin.go | 104 + src/cmd/compile/internal/gc/mparith2.go | 300 + src/cmd/compile/internal/gc/mparith3.go | 235 + src/cmd/compile/internal/gc/obj.go | 434 + src/cmd/compile/internal/gc/opnames.go | 162 + src/cmd/compile/internal/gc/order.go | 1170 +++ src/cmd/compile/internal/gc/pgen.go | 555 + src/cmd/compile/internal/gc/plive.go | 1830 ++++ src/cmd/compile/internal/gc/popt.go | 1086 ++ src/cmd/compile/internal/gc/racewalk.go | 639 ++ src/cmd/compile/internal/gc/range.go | 406 + src/cmd/compile/internal/gc/reflect.go | 1572 +++ src/cmd/compile/internal/gc/reg.go | 1559 +++ src/cmd/compile/internal/gc/select.go | 371 + src/cmd/compile/internal/gc/sinit.go | 1528 +++ src/cmd/compile/internal/gc/subr.go | 3571 +++++++ src/cmd/compile/internal/gc/swt.go | 840 ++ src/cmd/compile/internal/gc/syntax.go | 469 + src/cmd/compile/internal/gc/typecheck.go | 4069 ++++++++ src/cmd/compile/internal/gc/unsafe.go | 167 + src/cmd/compile/internal/gc/util.go | 103 + src/cmd/compile/internal/gc/walk.go | 4090 ++++++++ src/cmd/compile/internal/gc/y.go | 3512 +++++++ src/cmd/compile/internal/gc/y.output | 10411 +++++++++++++++++++ src/cmd/compile/internal/ppc64/cgen.go | 149 + src/cmd/compile/internal/ppc64/galign.go | 100 + src/cmd/compile/internal/ppc64/ggen.go | 558 + src/cmd/compile/internal/ppc64/gsubr.go | 1031 ++ src/cmd/compile/internal/ppc64/opt.go | 12 + src/cmd/compile/internal/ppc64/peep.go | 1051 ++ src/cmd/compile/internal/ppc64/prog.go | 314 + src/cmd/compile/internal/ppc64/reg.go | 162 + src/cmd/compile/internal/x86/cgen.go | 159 + src/cmd/compile/internal/x86/cgen64.go | 598 ++ src/cmd/compile/internal/x86/galign.go | 110 + src/cmd/compile/internal/x86/ggen.go | 940 ++ src/cmd/compile/internal/x86/gsubr.go | 1798 ++++ src/cmd/compile/internal/x86/peep.go | 814 ++ src/cmd/compile/internal/x86/prog.go | 292 + src/cmd/compile/internal/x86/reg.go | 112 + src/cmd/compile/main.go | 34 + src/cmd/internal/gc/align.go | 706 -- src/cmd/internal/gc/big/accuracy_string.go | 17 - src/cmd/internal/gc/big/arith.go | 291 - src/cmd/internal/gc/big/arith_decl.go | 53 - src/cmd/internal/gc/big/arith_test.go | 456 - src/cmd/internal/gc/big/bits_test.go | 224 - src/cmd/internal/gc/big/calibrate_test.go | 88 - src/cmd/internal/gc/big/decimal.go | 258 - src/cmd/internal/gc/big/decimal_test.go | 106 - src/cmd/internal/gc/big/example_test.go | 51 - src/cmd/internal/gc/big/float.go | 1681 --- src/cmd/internal/gc/big/float_test.go | 1664 --- src/cmd/internal/gc/big/floatconv.go | 373 - src/cmd/internal/gc/big/floatconv_test.go | 397 - src/cmd/internal/gc/big/floatexample_test.go | 111 - src/cmd/internal/gc/big/ftoa.go | 190 - src/cmd/internal/gc/big/gcd_test.go | 47 - src/cmd/internal/gc/big/hilbert_test.go | 160 - src/cmd/internal/gc/big/int.go | 848 -- src/cmd/internal/gc/big/int_test.go | 1387 --- src/cmd/internal/gc/big/intconv.go | 228 - src/cmd/internal/gc/big/intconv_test.go | 342 - src/cmd/internal/gc/big/nat.go | 1155 -- src/cmd/internal/gc/big/nat_test.go | 518 - src/cmd/internal/gc/big/natconv.go | 495 - src/cmd/internal/gc/big/natconv_test.go | 425 - src/cmd/internal/gc/big/rat.go | 570 - src/cmd/internal/gc/big/rat_test.go | 736 -- src/cmd/internal/gc/big/ratconv.go | 251 - src/cmd/internal/gc/big/ratconv_test.go | 451 - src/cmd/internal/gc/big/roundingmode_string.go | 16 - src/cmd/internal/gc/big/vendor.bash | 25 - src/cmd/internal/gc/builtin.go | 168 - src/cmd/internal/gc/builtin/runtime.go | 194 - src/cmd/internal/gc/builtin/unsafe.go | 18 - src/cmd/internal/gc/bv.go | 195 - src/cmd/internal/gc/cgen.go | 3564 ------- src/cmd/internal/gc/closure.go | 689 -- src/cmd/internal/gc/const.go | 1717 --- src/cmd/internal/gc/cplx.go | 480 - src/cmd/internal/gc/dcl.go | 1494 --- src/cmd/internal/gc/esc.go | 1758 ---- src/cmd/internal/gc/export.go | 560 - src/cmd/internal/gc/fmt.go | 1736 ---- src/cmd/internal/gc/gen.go | 1279 --- src/cmd/internal/gc/go.go | 841 -- src/cmd/internal/gc/go.y | 2312 ---- src/cmd/internal/gc/gsubr.go | 827 -- src/cmd/internal/gc/init.go | 189 - src/cmd/internal/gc/inl.go | 1008 -- src/cmd/internal/gc/lex.go | 2601 ----- src/cmd/internal/gc/mkbuiltin.go | 104 - src/cmd/internal/gc/mparith2.go | 300 - src/cmd/internal/gc/mparith3.go | 235 - src/cmd/internal/gc/obj.go | 434 - src/cmd/internal/gc/opnames.go | 162 - src/cmd/internal/gc/order.go | 1170 --- src/cmd/internal/gc/pgen.go | 555 - src/cmd/internal/gc/plive.go | 1830 ---- src/cmd/internal/gc/popt.go | 1086 -- src/cmd/internal/gc/racewalk.go | 639 -- src/cmd/internal/gc/range.go | 406 - src/cmd/internal/gc/reflect.go | 1572 --- src/cmd/internal/gc/reg.go | 1559 --- src/cmd/internal/gc/select.go | 371 - src/cmd/internal/gc/sinit.go | 1528 --- src/cmd/internal/gc/subr.go | 3571 ------- src/cmd/internal/gc/swt.go | 840 -- src/cmd/internal/gc/syntax.go | 469 - src/cmd/internal/gc/typecheck.go | 4069 -------- src/cmd/internal/gc/unsafe.go | 167 - src/cmd/internal/gc/util.go | 103 - src/cmd/internal/gc/walk.go | 4090 -------- src/cmd/internal/gc/y.go | 3512 ------- src/cmd/internal/gc/y.output | 10411 ------------------- src/cmd/internal/ld/ar.go | 52 - src/cmd/internal/ld/arch.go | 70 - src/cmd/internal/ld/data.go | 1721 --- src/cmd/internal/ld/decodesym.go | 192 - src/cmd/internal/ld/dwarf.go | 2621 ----- src/cmd/internal/ld/dwarf_defs.go | 516 - src/cmd/internal/ld/elf.go | 2493 ----- src/cmd/internal/ld/go.go | 870 -- src/cmd/internal/ld/ld.go | 130 - src/cmd/internal/ld/ldelf.go | 1019 -- src/cmd/internal/ld/ldmacho.go | 893 -- src/cmd/internal/ld/ldpe.go | 534 - src/cmd/internal/ld/lib.go | 1946 ---- src/cmd/internal/ld/link.go | 226 - src/cmd/internal/ld/macho.go | 855 -- src/cmd/internal/ld/macho_combine_dwarf.go | 369 - src/cmd/internal/ld/objfile.go | 354 - src/cmd/internal/ld/pcln.go | 462 - src/cmd/internal/ld/pe.go | 1253 --- src/cmd/internal/ld/pobj.go | 253 - src/cmd/internal/ld/sym.go | 227 - src/cmd/internal/ld/symtab.go | 534 - src/cmd/internal/ld/textflag.go | 133 - src/cmd/internal/ld/util.go | 172 - src/cmd/link/internal/amd64/asm.go | 817 ++ src/cmd/link/internal/amd64/l.go | 47 + src/cmd/link/internal/amd64/obj.go | 222 + src/cmd/link/internal/amd64/z.go | 1 + src/cmd/link/internal/arm/asm.go | 651 ++ src/cmd/link/internal/arm/l.go | 79 + src/cmd/link/internal/arm/obj.go | 178 + src/cmd/link/internal/arm64/asm.go | 435 + src/cmd/link/internal/arm64/l.go | 78 + src/cmd/link/internal/arm64/obj.go | 169 + src/cmd/link/internal/ld/ar.go | 52 + src/cmd/link/internal/ld/arch.go | 70 + src/cmd/link/internal/ld/data.go | 1721 +++ src/cmd/link/internal/ld/decodesym.go | 192 + src/cmd/link/internal/ld/dwarf.go | 2621 +++++ src/cmd/link/internal/ld/dwarf_defs.go | 516 + src/cmd/link/internal/ld/elf.go | 2493 +++++ src/cmd/link/internal/ld/go.go | 870 ++ src/cmd/link/internal/ld/ld.go | 130 + src/cmd/link/internal/ld/ldelf.go | 1019 ++ src/cmd/link/internal/ld/ldmacho.go | 893 ++ src/cmd/link/internal/ld/ldpe.go | 534 + src/cmd/link/internal/ld/lib.go | 1946 ++++ src/cmd/link/internal/ld/link.go | 226 + src/cmd/link/internal/ld/macho.go | 855 ++ src/cmd/link/internal/ld/macho_combine_dwarf.go | 369 + src/cmd/link/internal/ld/objfile.go | 354 + src/cmd/link/internal/ld/pcln.go | 462 + src/cmd/link/internal/ld/pe.go | 1253 +++ src/cmd/link/internal/ld/pobj.go | 253 + src/cmd/link/internal/ld/sym.go | 227 + src/cmd/link/internal/ld/symtab.go | 534 + src/cmd/link/internal/ld/textflag.go | 133 + src/cmd/link/internal/ld/util.go | 172 + src/cmd/link/internal/ppc64/asm.go | 737 ++ src/cmd/link/internal/ppc64/l.go | 78 + src/cmd/link/internal/ppc64/obj.go | 162 + src/cmd/link/internal/x86/asm.go | 643 ++ src/cmd/link/internal/x86/l.go | 47 + src/cmd/link/internal/x86/obj.go | 187 + src/cmd/link/main.go | 34 + 308 files changed, 117618 insertions(+), 117550 deletions(-) delete mode 100644 src/cmd/5g/cgen.go delete mode 100644 src/cmd/5g/cgen64.go delete mode 100644 src/cmd/5g/galign.go delete mode 100644 src/cmd/5g/ggen.go delete mode 100644 src/cmd/5g/gsubr.go delete mode 100644 src/cmd/5g/peep.go delete mode 100644 src/cmd/5g/prog.go delete mode 100644 src/cmd/5g/reg.go delete mode 100644 src/cmd/5l/asm.go delete mode 100644 src/cmd/5l/l.go delete mode 100644 src/cmd/5l/obj.go delete mode 100644 src/cmd/6g/cgen.go delete mode 100644 src/cmd/6g/galign.go delete mode 100644 src/cmd/6g/ggen.go delete mode 100644 src/cmd/6g/gsubr.go delete mode 100644 src/cmd/6g/peep.go delete mode 100644 src/cmd/6g/prog.go delete mode 100644 src/cmd/6g/reg.go delete mode 100644 src/cmd/6l/asm.go delete mode 100644 src/cmd/6l/l.go delete mode 100644 src/cmd/6l/obj.go delete mode 100644 src/cmd/6l/z.go delete mode 100644 src/cmd/7g/cgen.go delete mode 100644 src/cmd/7g/galign.go delete mode 100644 src/cmd/7g/ggen.go delete mode 100644 src/cmd/7g/gsubr.go delete mode 100644 src/cmd/7g/peep.go delete mode 100644 src/cmd/7g/prog.go delete mode 100644 src/cmd/7g/reg.go delete mode 100644 src/cmd/7l/asm.go delete mode 100644 src/cmd/7l/l.go delete mode 100644 src/cmd/7l/obj.go delete mode 100644 src/cmd/8g/cgen.go delete mode 100644 src/cmd/8g/cgen64.go delete mode 100644 src/cmd/8g/galign.go delete mode 100644 src/cmd/8g/ggen.go delete mode 100644 src/cmd/8g/gsubr.go delete mode 100644 src/cmd/8g/peep.go delete mode 100644 src/cmd/8g/prog.go delete mode 100644 src/cmd/8g/reg.go delete mode 100644 src/cmd/8l/asm.go delete mode 100644 src/cmd/8l/l.go delete mode 100644 src/cmd/8l/obj.go delete mode 100644 src/cmd/9g/cgen.go delete mode 100644 src/cmd/9g/galign.go delete mode 100644 src/cmd/9g/ggen.go delete mode 100644 src/cmd/9g/gsubr.go delete mode 100644 src/cmd/9g/opt.go delete mode 100644 src/cmd/9g/peep.go delete mode 100644 src/cmd/9g/prog.go delete mode 100644 src/cmd/9g/reg.go delete mode 100644 src/cmd/9l/asm.go delete mode 100644 src/cmd/9l/l.go delete mode 100644 src/cmd/9l/obj.go create mode 100644 src/cmd/compile/internal/amd64/cgen.go create mode 100644 src/cmd/compile/internal/amd64/galign.go create mode 100644 src/cmd/compile/internal/amd64/ggen.go create mode 100644 src/cmd/compile/internal/amd64/gsubr.go create mode 100644 src/cmd/compile/internal/amd64/peep.go create mode 100644 src/cmd/compile/internal/amd64/prog.go create mode 100644 src/cmd/compile/internal/amd64/reg.go create mode 100644 src/cmd/compile/internal/arm/cgen.go create mode 100644 src/cmd/compile/internal/arm/cgen64.go create mode 100644 src/cmd/compile/internal/arm/galign.go create mode 100644 src/cmd/compile/internal/arm/ggen.go create mode 100644 src/cmd/compile/internal/arm/gsubr.go create mode 100644 src/cmd/compile/internal/arm/peep.go create mode 100644 src/cmd/compile/internal/arm/prog.go create mode 100644 src/cmd/compile/internal/arm/reg.go create mode 100644 src/cmd/compile/internal/arm64/cgen.go create mode 100644 src/cmd/compile/internal/arm64/galign.go create mode 100644 src/cmd/compile/internal/arm64/ggen.go create mode 100644 src/cmd/compile/internal/arm64/gsubr.go create mode 100644 src/cmd/compile/internal/arm64/peep.go create mode 100644 src/cmd/compile/internal/arm64/prog.go create mode 100644 src/cmd/compile/internal/arm64/reg.go create mode 100644 src/cmd/compile/internal/big/accuracy_string.go create mode 100644 src/cmd/compile/internal/big/arith.go create mode 100644 src/cmd/compile/internal/big/arith_decl.go create mode 100644 src/cmd/compile/internal/big/arith_test.go create mode 100644 src/cmd/compile/internal/big/bits_test.go create mode 100644 src/cmd/compile/internal/big/calibrate_test.go create mode 100644 src/cmd/compile/internal/big/decimal.go create mode 100644 src/cmd/compile/internal/big/decimal_test.go create mode 100644 src/cmd/compile/internal/big/example_test.go create mode 100644 src/cmd/compile/internal/big/float.go create mode 100644 src/cmd/compile/internal/big/float_test.go create mode 100644 src/cmd/compile/internal/big/floatconv.go create mode 100644 src/cmd/compile/internal/big/floatconv_test.go create mode 100644 src/cmd/compile/internal/big/floatexample_test.go create mode 100644 src/cmd/compile/internal/big/ftoa.go create mode 100644 src/cmd/compile/internal/big/gcd_test.go create mode 100644 src/cmd/compile/internal/big/hilbert_test.go create mode 100644 src/cmd/compile/internal/big/int.go create mode 100644 src/cmd/compile/internal/big/int_test.go create mode 100644 src/cmd/compile/internal/big/intconv.go create mode 100644 src/cmd/compile/internal/big/intconv_test.go create mode 100644 src/cmd/compile/internal/big/nat.go create mode 100644 src/cmd/compile/internal/big/nat_test.go create mode 100644 src/cmd/compile/internal/big/natconv.go create mode 100644 src/cmd/compile/internal/big/natconv_test.go create mode 100644 src/cmd/compile/internal/big/rat.go create mode 100644 src/cmd/compile/internal/big/rat_test.go create mode 100644 src/cmd/compile/internal/big/ratconv.go create mode 100644 src/cmd/compile/internal/big/ratconv_test.go create mode 100644 src/cmd/compile/internal/big/roundingmode_string.go create mode 100755 src/cmd/compile/internal/big/vendor.bash create mode 100644 src/cmd/compile/internal/gc/align.go create mode 100644 src/cmd/compile/internal/gc/builtin.go create mode 100644 src/cmd/compile/internal/gc/builtin/runtime.go create mode 100644 src/cmd/compile/internal/gc/builtin/unsafe.go create mode 100644 src/cmd/compile/internal/gc/bv.go create mode 100644 src/cmd/compile/internal/gc/cgen.go create mode 100644 src/cmd/compile/internal/gc/closure.go create mode 100644 src/cmd/compile/internal/gc/const.go create mode 100644 src/cmd/compile/internal/gc/cplx.go create mode 100644 src/cmd/compile/internal/gc/dcl.go create mode 100644 src/cmd/compile/internal/gc/esc.go create mode 100644 src/cmd/compile/internal/gc/export.go create mode 100644 src/cmd/compile/internal/gc/fmt.go create mode 100644 src/cmd/compile/internal/gc/gen.go create mode 100644 src/cmd/compile/internal/gc/go.go create mode 100644 src/cmd/compile/internal/gc/go.y create mode 100644 src/cmd/compile/internal/gc/gsubr.go create mode 100644 src/cmd/compile/internal/gc/init.go create mode 100644 src/cmd/compile/internal/gc/inl.go create mode 100644 src/cmd/compile/internal/gc/lex.go create mode 100644 src/cmd/compile/internal/gc/mkbuiltin.go create mode 100644 src/cmd/compile/internal/gc/mparith2.go create mode 100644 src/cmd/compile/internal/gc/mparith3.go create mode 100644 src/cmd/compile/internal/gc/obj.go create mode 100644 src/cmd/compile/internal/gc/opnames.go create mode 100644 src/cmd/compile/internal/gc/order.go create mode 100644 src/cmd/compile/internal/gc/pgen.go create mode 100644 src/cmd/compile/internal/gc/plive.go create mode 100644 src/cmd/compile/internal/gc/popt.go create mode 100644 src/cmd/compile/internal/gc/racewalk.go create mode 100644 src/cmd/compile/internal/gc/range.go create mode 100644 src/cmd/compile/internal/gc/reflect.go create mode 100644 src/cmd/compile/internal/gc/reg.go create mode 100644 src/cmd/compile/internal/gc/select.go create mode 100644 src/cmd/compile/internal/gc/sinit.go create mode 100644 src/cmd/compile/internal/gc/subr.go create mode 100644 src/cmd/compile/internal/gc/swt.go create mode 100644 src/cmd/compile/internal/gc/syntax.go create mode 100644 src/cmd/compile/internal/gc/typecheck.go create mode 100644 src/cmd/compile/internal/gc/unsafe.go create mode 100644 src/cmd/compile/internal/gc/util.go create mode 100644 src/cmd/compile/internal/gc/walk.go create mode 100644 src/cmd/compile/internal/gc/y.go create mode 100644 src/cmd/compile/internal/gc/y.output create mode 100644 src/cmd/compile/internal/ppc64/cgen.go create mode 100644 src/cmd/compile/internal/ppc64/galign.go create mode 100644 src/cmd/compile/internal/ppc64/ggen.go create mode 100644 src/cmd/compile/internal/ppc64/gsubr.go create mode 100644 src/cmd/compile/internal/ppc64/opt.go create mode 100644 src/cmd/compile/internal/ppc64/peep.go create mode 100644 src/cmd/compile/internal/ppc64/prog.go create mode 100644 src/cmd/compile/internal/ppc64/reg.go create mode 100644 src/cmd/compile/internal/x86/cgen.go create mode 100644 src/cmd/compile/internal/x86/cgen64.go create mode 100644 src/cmd/compile/internal/x86/galign.go create mode 100644 src/cmd/compile/internal/x86/ggen.go create mode 100644 src/cmd/compile/internal/x86/gsubr.go create mode 100644 src/cmd/compile/internal/x86/peep.go create mode 100644 src/cmd/compile/internal/x86/prog.go create mode 100644 src/cmd/compile/internal/x86/reg.go create mode 100644 src/cmd/compile/main.go delete mode 100644 src/cmd/internal/gc/align.go delete mode 100644 src/cmd/internal/gc/big/accuracy_string.go delete mode 100644 src/cmd/internal/gc/big/arith.go delete mode 100644 src/cmd/internal/gc/big/arith_decl.go delete mode 100644 src/cmd/internal/gc/big/arith_test.go delete mode 100644 src/cmd/internal/gc/big/bits_test.go delete mode 100644 src/cmd/internal/gc/big/calibrate_test.go delete mode 100644 src/cmd/internal/gc/big/decimal.go delete mode 100644 src/cmd/internal/gc/big/decimal_test.go delete mode 100644 src/cmd/internal/gc/big/example_test.go delete mode 100644 src/cmd/internal/gc/big/float.go delete mode 100644 src/cmd/internal/gc/big/float_test.go delete mode 100644 src/cmd/internal/gc/big/floatconv.go delete mode 100644 src/cmd/internal/gc/big/floatconv_test.go delete mode 100644 src/cmd/internal/gc/big/floatexample_test.go delete mode 100644 src/cmd/internal/gc/big/ftoa.go delete mode 100644 src/cmd/internal/gc/big/gcd_test.go delete mode 100644 src/cmd/internal/gc/big/hilbert_test.go delete mode 100644 src/cmd/internal/gc/big/int.go delete mode 100644 src/cmd/internal/gc/big/int_test.go delete mode 100644 src/cmd/internal/gc/big/intconv.go delete mode 100644 src/cmd/internal/gc/big/intconv_test.go delete mode 100644 src/cmd/internal/gc/big/nat.go delete mode 100644 src/cmd/internal/gc/big/nat_test.go delete mode 100644 src/cmd/internal/gc/big/natconv.go delete mode 100644 src/cmd/internal/gc/big/natconv_test.go delete mode 100644 src/cmd/internal/gc/big/rat.go delete mode 100644 src/cmd/internal/gc/big/rat_test.go delete mode 100644 src/cmd/internal/gc/big/ratconv.go delete mode 100644 src/cmd/internal/gc/big/ratconv_test.go delete mode 100644 src/cmd/internal/gc/big/roundingmode_string.go delete mode 100755 src/cmd/internal/gc/big/vendor.bash delete mode 100644 src/cmd/internal/gc/builtin.go delete mode 100644 src/cmd/internal/gc/builtin/runtime.go delete mode 100644 src/cmd/internal/gc/builtin/unsafe.go delete mode 100644 src/cmd/internal/gc/bv.go delete mode 100644 src/cmd/internal/gc/cgen.go delete mode 100644 src/cmd/internal/gc/closure.go delete mode 100644 src/cmd/internal/gc/const.go delete mode 100644 src/cmd/internal/gc/cplx.go delete mode 100644 src/cmd/internal/gc/dcl.go delete mode 100644 src/cmd/internal/gc/esc.go delete mode 100644 src/cmd/internal/gc/export.go delete mode 100644 src/cmd/internal/gc/fmt.go delete mode 100644 src/cmd/internal/gc/gen.go delete mode 100644 src/cmd/internal/gc/go.go delete mode 100644 src/cmd/internal/gc/go.y delete mode 100644 src/cmd/internal/gc/gsubr.go delete mode 100644 src/cmd/internal/gc/init.go delete mode 100644 src/cmd/internal/gc/inl.go delete mode 100644 src/cmd/internal/gc/lex.go delete mode 100644 src/cmd/internal/gc/mkbuiltin.go delete mode 100644 src/cmd/internal/gc/mparith2.go delete mode 100644 src/cmd/internal/gc/mparith3.go delete mode 100644 src/cmd/internal/gc/obj.go delete mode 100644 src/cmd/internal/gc/opnames.go delete mode 100644 src/cmd/internal/gc/order.go delete mode 100644 src/cmd/internal/gc/pgen.go delete mode 100644 src/cmd/internal/gc/plive.go delete mode 100644 src/cmd/internal/gc/popt.go delete mode 100644 src/cmd/internal/gc/racewalk.go delete mode 100644 src/cmd/internal/gc/range.go delete mode 100644 src/cmd/internal/gc/reflect.go delete mode 100644 src/cmd/internal/gc/reg.go delete mode 100644 src/cmd/internal/gc/select.go delete mode 100644 src/cmd/internal/gc/sinit.go delete mode 100644 src/cmd/internal/gc/subr.go delete mode 100644 src/cmd/internal/gc/swt.go delete mode 100644 src/cmd/internal/gc/syntax.go delete mode 100644 src/cmd/internal/gc/typecheck.go delete mode 100644 src/cmd/internal/gc/unsafe.go delete mode 100644 src/cmd/internal/gc/util.go delete mode 100644 src/cmd/internal/gc/walk.go delete mode 100644 src/cmd/internal/gc/y.go delete mode 100644 src/cmd/internal/gc/y.output delete mode 100644 src/cmd/internal/ld/ar.go delete mode 100644 src/cmd/internal/ld/arch.go delete mode 100644 src/cmd/internal/ld/data.go delete mode 100644 src/cmd/internal/ld/decodesym.go delete mode 100644 src/cmd/internal/ld/dwarf.go delete mode 100644 src/cmd/internal/ld/dwarf_defs.go delete mode 100644 src/cmd/internal/ld/elf.go delete mode 100644 src/cmd/internal/ld/go.go delete mode 100644 src/cmd/internal/ld/ld.go delete mode 100644 src/cmd/internal/ld/ldelf.go delete mode 100644 src/cmd/internal/ld/ldmacho.go delete mode 100644 src/cmd/internal/ld/ldpe.go delete mode 100644 src/cmd/internal/ld/lib.go delete mode 100644 src/cmd/internal/ld/link.go delete mode 100644 src/cmd/internal/ld/macho.go delete mode 100644 src/cmd/internal/ld/macho_combine_dwarf.go delete mode 100644 src/cmd/internal/ld/objfile.go delete mode 100644 src/cmd/internal/ld/pcln.go delete mode 100644 src/cmd/internal/ld/pe.go delete mode 100644 src/cmd/internal/ld/pobj.go delete mode 100644 src/cmd/internal/ld/sym.go delete mode 100644 src/cmd/internal/ld/symtab.go delete mode 100644 src/cmd/internal/ld/textflag.go delete mode 100644 src/cmd/internal/ld/util.go create mode 100644 src/cmd/link/internal/amd64/asm.go create mode 100644 src/cmd/link/internal/amd64/l.go create mode 100644 src/cmd/link/internal/amd64/obj.go create mode 100644 src/cmd/link/internal/amd64/z.go create mode 100644 src/cmd/link/internal/arm/asm.go create mode 100644 src/cmd/link/internal/arm/l.go create mode 100644 src/cmd/link/internal/arm/obj.go create mode 100644 src/cmd/link/internal/arm64/asm.go create mode 100644 src/cmd/link/internal/arm64/l.go create mode 100644 src/cmd/link/internal/arm64/obj.go create mode 100644 src/cmd/link/internal/ld/ar.go create mode 100644 src/cmd/link/internal/ld/arch.go create mode 100644 src/cmd/link/internal/ld/data.go create mode 100644 src/cmd/link/internal/ld/decodesym.go create mode 100644 src/cmd/link/internal/ld/dwarf.go create mode 100644 src/cmd/link/internal/ld/dwarf_defs.go create mode 100644 src/cmd/link/internal/ld/elf.go create mode 100644 src/cmd/link/internal/ld/go.go create mode 100644 src/cmd/link/internal/ld/ld.go create mode 100644 src/cmd/link/internal/ld/ldelf.go create mode 100644 src/cmd/link/internal/ld/ldmacho.go create mode 100644 src/cmd/link/internal/ld/ldpe.go create mode 100644 src/cmd/link/internal/ld/lib.go create mode 100644 src/cmd/link/internal/ld/link.go create mode 100644 src/cmd/link/internal/ld/macho.go create mode 100644 src/cmd/link/internal/ld/macho_combine_dwarf.go create mode 100644 src/cmd/link/internal/ld/objfile.go create mode 100644 src/cmd/link/internal/ld/pcln.go create mode 100644 src/cmd/link/internal/ld/pe.go create mode 100644 src/cmd/link/internal/ld/pobj.go create mode 100644 src/cmd/link/internal/ld/sym.go create mode 100644 src/cmd/link/internal/ld/symtab.go create mode 100644 src/cmd/link/internal/ld/textflag.go create mode 100644 src/cmd/link/internal/ld/util.go create mode 100644 src/cmd/link/internal/ppc64/asm.go create mode 100644 src/cmd/link/internal/ppc64/l.go create mode 100644 src/cmd/link/internal/ppc64/obj.go create mode 100644 src/cmd/link/internal/x86/asm.go create mode 100644 src/cmd/link/internal/x86/l.go create mode 100644 src/cmd/link/internal/x86/obj.go create mode 100644 src/cmd/link/main.go (limited to 'src/cmd') diff --git a/src/cmd/5g/cgen.go b/src/cmd/5g/cgen.go deleted file mode 100644 index c0d7651584..0000000000 --- a/src/cmd/5g/cgen.go +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright 2009 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/gc" - "cmd/internal/obj" - "cmd/internal/obj/arm" -) - -/* - * generate array index into res. - * n might be any size; res is 32-bit. - * returns Prog* to patch to panic call. - */ -func cgenindex(n *gc.Node, res *gc.Node, bounded bool) *obj.Prog { - if !gc.Is64(n.Type) { - gc.Cgen(n, res) - return nil - } - - var tmp gc.Node - gc.Tempname(&tmp, gc.Types[gc.TINT64]) - gc.Cgen(n, &tmp) - var lo gc.Node - var hi gc.Node - split64(&tmp, &lo, &hi) - gmove(&lo, res) - if bounded { - splitclean() - return nil - } - - var n1 gc.Node - gc.Regalloc(&n1, gc.Types[gc.TINT32], nil) - var n2 gc.Node - gc.Regalloc(&n2, gc.Types[gc.TINT32], nil) - var zero gc.Node - gc.Nodconst(&zero, gc.Types[gc.TINT32], 0) - gmove(&hi, &n1) - gmove(&zero, &n2) - gins(arm.ACMP, &n1, &n2) - gc.Regfree(&n2) - gc.Regfree(&n1) - splitclean() - return gc.Gbranch(arm.ABNE, nil, -1) -} - -func igenindex(n *gc.Node, res *gc.Node, bounded bool) *obj.Prog { - gc.Tempname(res, n.Type) - return cgenindex(n, res, bounded) -} - -func blockcopy(n, res *gc.Node, osrc, odst, w int64) { - // determine alignment. - // want to avoid unaligned access, so have to use - // smaller operations for less aligned types. - // for example moving [4]byte must use 4 MOVB not 1 MOVW. - align := int(n.Type.Align) - - var op int - switch align { - default: - gc.Fatal("sgen: invalid alignment %d for %v", align, n.Type) - - case 1: - op = arm.AMOVB - - case 2: - op = arm.AMOVH - - case 4: - op = arm.AMOVW - } - - if w%int64(align) != 0 { - gc.Fatal("sgen: unaligned size %d (align=%d) for %v", w, align, n.Type) - } - c := int32(w / int64(align)) - - if osrc%int64(align) != 0 || odst%int64(align) != 0 { - gc.Fatal("sgen: unaligned offset src %d or dst %d (align %d)", osrc, odst, align) - } - - // if we are copying forward on the stack and - // the src and dst overlap, then reverse direction - dir := align - if osrc < odst && int64(odst) < int64(osrc)+w { - dir = -dir - } - - if op == arm.AMOVW && !gc.Nacl && dir > 0 && c >= 4 && c <= 128 { - var r0 gc.Node - r0.Op = gc.OREGISTER - r0.Reg = arm.REG_R0 - var r1 gc.Node - r1.Op = gc.OREGISTER - r1.Reg = arm.REG_R0 + 1 - var r2 gc.Node - r2.Op = gc.OREGISTER - r2.Reg = arm.REG_R0 + 2 - - var src gc.Node - gc.Regalloc(&src, gc.Types[gc.Tptr], &r1) - var dst gc.Node - gc.Regalloc(&dst, gc.Types[gc.Tptr], &r2) - if n.Ullman >= res.Ullman { - // eval n first - gc.Agen(n, &src) - - if res.Op == gc.ONAME { - gc.Gvardef(res) - } - gc.Agen(res, &dst) - } else { - // eval res first - if res.Op == gc.ONAME { - gc.Gvardef(res) - } - gc.Agen(res, &dst) - gc.Agen(n, &src) - } - - var tmp gc.Node - gc.Regalloc(&tmp, gc.Types[gc.Tptr], &r0) - f := gc.Sysfunc("duffcopy") - p := gins(obj.ADUFFCOPY, nil, f) - gc.Afunclit(&p.To, f) - - // 8 and 128 = magic constants: see ../../runtime/asm_arm.s - p.To.Offset = 8 * (128 - int64(c)) - - gc.Regfree(&tmp) - gc.Regfree(&src) - gc.Regfree(&dst) - return - } - - var dst gc.Node - var src gc.Node - if n.Ullman >= res.Ullman { - gc.Agenr(n, &dst, res) // temporarily use dst - gc.Regalloc(&src, gc.Types[gc.Tptr], nil) - gins(arm.AMOVW, &dst, &src) - if res.Op == gc.ONAME { - gc.Gvardef(res) - } - gc.Agen(res, &dst) - } else { - if res.Op == gc.ONAME { - gc.Gvardef(res) - } - gc.Agenr(res, &dst, res) - gc.Agenr(n, &src, nil) - } - - var tmp gc.Node - gc.Regalloc(&tmp, gc.Types[gc.TUINT32], nil) - - // set up end marker - var nend gc.Node - - if c >= 4 { - gc.Regalloc(&nend, gc.Types[gc.TUINT32], nil) - - p := gins(arm.AMOVW, &src, &nend) - p.From.Type = obj.TYPE_ADDR - if dir < 0 { - p.From.Offset = int64(dir) - } else { - p.From.Offset = w - } - } - - // move src and dest to the end of block if necessary - if dir < 0 { - p := gins(arm.AMOVW, &src, &src) - p.From.Type = obj.TYPE_ADDR - p.From.Offset = w + int64(dir) - - p = gins(arm.AMOVW, &dst, &dst) - p.From.Type = obj.TYPE_ADDR - p.From.Offset = w + int64(dir) - } - - // move - if c >= 4 { - p := gins(op, &src, &tmp) - p.From.Type = obj.TYPE_MEM - p.From.Offset = int64(dir) - p.Scond |= arm.C_PBIT - ploop := p - - p = gins(op, &tmp, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = int64(dir) - p.Scond |= arm.C_PBIT - - p = gins(arm.ACMP, &src, nil) - raddr(&nend, p) - - gc.Patch(gc.Gbranch(arm.ABNE, nil, 0), ploop) - gc.Regfree(&nend) - } else { - var p *obj.Prog - for { - tmp14 := c - c-- - if tmp14 <= 0 { - break - } - p = gins(op, &src, &tmp) - p.From.Type = obj.TYPE_MEM - p.From.Offset = int64(dir) - p.Scond |= arm.C_PBIT - - p = gins(op, &tmp, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = int64(dir) - p.Scond |= arm.C_PBIT - } - } - - gc.Regfree(&dst) - gc.Regfree(&src) - gc.Regfree(&tmp) -} diff --git a/src/cmd/5g/cgen64.go b/src/cmd/5g/cgen64.go deleted file mode 100644 index c55e000adc..0000000000 --- a/src/cmd/5g/cgen64.go +++ /dev/null @@ -1,859 +0,0 @@ -// Copyright 2009 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/gc" - "cmd/internal/obj" - "cmd/internal/obj/arm" -) - -/* - * attempt to generate 64-bit - * res = n - * return 1 on success, 0 if op not handled. - */ -func cgen64(n *gc.Node, res *gc.Node) { - if res.Op != gc.OINDREG && res.Op != gc.ONAME { - gc.Dump("n", n) - gc.Dump("res", res) - gc.Fatal("cgen64 %v of %v", gc.Oconv(int(n.Op), 0), gc.Oconv(int(res.Op), 0)) - } - - l := n.Left - var t1 gc.Node - if !l.Addable { - gc.Tempname(&t1, l.Type) - gc.Cgen(l, &t1) - l = &t1 - } - - var hi1 gc.Node - var lo1 gc.Node - split64(l, &lo1, &hi1) - switch n.Op { - default: - gc.Fatal("cgen64 %v", gc.Oconv(int(n.Op), 0)) - - case gc.OMINUS: - var lo2 gc.Node - var hi2 gc.Node - split64(res, &lo2, &hi2) - - gc.Regalloc(&t1, lo1.Type, nil) - var al gc.Node - gc.Regalloc(&al, lo1.Type, nil) - var ah gc.Node - gc.Regalloc(&ah, hi1.Type, nil) - - gins(arm.AMOVW, &lo1, &al) - gins(arm.AMOVW, &hi1, &ah) - - gmove(ncon(0), &t1) - p1 := gins(arm.ASUB, &al, &t1) - p1.Scond |= arm.C_SBIT - gins(arm.AMOVW, &t1, &lo2) - - gmove(ncon(0), &t1) - gins(arm.ASBC, &ah, &t1) - gins(arm.AMOVW, &t1, &hi2) - - gc.Regfree(&t1) - gc.Regfree(&al) - gc.Regfree(&ah) - splitclean() - splitclean() - return - - case gc.OCOM: - gc.Regalloc(&t1, lo1.Type, nil) - gmove(ncon(^uint32(0)), &t1) - - var lo2 gc.Node - var hi2 gc.Node - split64(res, &lo2, &hi2) - var n1 gc.Node - gc.Regalloc(&n1, lo1.Type, nil) - - gins(arm.AMOVW, &lo1, &n1) - gins(arm.AEOR, &t1, &n1) - gins(arm.AMOVW, &n1, &lo2) - - gins(arm.AMOVW, &hi1, &n1) - gins(arm.AEOR, &t1, &n1) - gins(arm.AMOVW, &n1, &hi2) - - gc.Regfree(&t1) - gc.Regfree(&n1) - splitclean() - splitclean() - return - - // binary operators. - // common setup below. - case gc.OADD, - gc.OSUB, - gc.OMUL, - gc.OLSH, - gc.ORSH, - gc.OAND, - gc.OOR, - gc.OXOR, - gc.OLROT: - break - } - - // setup for binary operators - r := n.Right - - if r != nil && !r.Addable { - var t2 gc.Node - gc.Tempname(&t2, r.Type) - gc.Cgen(r, &t2) - r = &t2 - } - - var hi2 gc.Node - var lo2 gc.Node - if gc.Is64(r.Type) { - split64(r, &lo2, &hi2) - } - - var al gc.Node - gc.Regalloc(&al, lo1.Type, nil) - var ah gc.Node - gc.Regalloc(&ah, hi1.Type, nil) - - // Do op. Leave result in ah:al. - switch n.Op { - default: - gc.Fatal("cgen64: not implemented: %v\n", n) - - // TODO: Constants - case gc.OADD: - var bl gc.Node - gc.Regalloc(&bl, gc.Types[gc.TPTR32], nil) - - var bh gc.Node - gc.Regalloc(&bh, gc.Types[gc.TPTR32], nil) - gins(arm.AMOVW, &hi1, &ah) - gins(arm.AMOVW, &lo1, &al) - gins(arm.AMOVW, &hi2, &bh) - gins(arm.AMOVW, &lo2, &bl) - p1 := gins(arm.AADD, &bl, &al) - p1.Scond |= arm.C_SBIT - gins(arm.AADC, &bh, &ah) - gc.Regfree(&bl) - gc.Regfree(&bh) - - // TODO: Constants. - case gc.OSUB: - var bl gc.Node - gc.Regalloc(&bl, gc.Types[gc.TPTR32], nil) - - var bh gc.Node - gc.Regalloc(&bh, gc.Types[gc.TPTR32], nil) - gins(arm.AMOVW, &lo1, &al) - gins(arm.AMOVW, &hi1, &ah) - gins(arm.AMOVW, &lo2, &bl) - gins(arm.AMOVW, &hi2, &bh) - p1 := gins(arm.ASUB, &bl, &al) - p1.Scond |= arm.C_SBIT - gins(arm.ASBC, &bh, &ah) - gc.Regfree(&bl) - gc.Regfree(&bh) - - // TODO(kaib): this can be done with 4 regs and does not need 6 - case gc.OMUL: - var bl gc.Node - gc.Regalloc(&bl, gc.Types[gc.TPTR32], nil) - - var bh gc.Node - gc.Regalloc(&bh, gc.Types[gc.TPTR32], nil) - var cl gc.Node - gc.Regalloc(&cl, gc.Types[gc.TPTR32], nil) - var ch gc.Node - gc.Regalloc(&ch, gc.Types[gc.TPTR32], nil) - - // load args into bh:bl and bh:bl. - gins(arm.AMOVW, &hi1, &bh) - - gins(arm.AMOVW, &lo1, &bl) - gins(arm.AMOVW, &hi2, &ch) - gins(arm.AMOVW, &lo2, &cl) - - // bl * cl -> ah al - p1 := gins(arm.AMULLU, nil, nil) - - p1.From.Type = obj.TYPE_REG - p1.From.Reg = bl.Reg - p1.Reg = cl.Reg - p1.To.Type = obj.TYPE_REGREG - p1.To.Reg = ah.Reg - p1.To.Offset = int64(al.Reg) - - //print("%P\n", p1); - - // bl * ch + ah -> ah - p1 = gins(arm.AMULA, nil, nil) - - p1.From.Type = obj.TYPE_REG - p1.From.Reg = bl.Reg - p1.Reg = ch.Reg - p1.To.Type = obj.TYPE_REGREG2 - p1.To.Reg = ah.Reg - p1.To.Offset = int64(ah.Reg) - - //print("%P\n", p1); - - // bh * cl + ah -> ah - p1 = gins(arm.AMULA, nil, nil) - - p1.From.Type = obj.TYPE_REG - p1.From.Reg = bh.Reg - p1.Reg = cl.Reg - p1.To.Type = obj.TYPE_REGREG2 - p1.To.Reg = ah.Reg - p1.To.Offset = int64(ah.Reg) - - //print("%P\n", p1); - - gc.Regfree(&bh) - - gc.Regfree(&bl) - gc.Regfree(&ch) - gc.Regfree(&cl) - - // We only rotate by a constant c in [0,64). - // if c >= 32: - // lo, hi = hi, lo - // c -= 32 - // if c == 0: - // no-op - // else: - // t = hi - // shld hi:lo, c - // shld lo:t, c - case gc.OLROT: - v := uint64(r.Int()) - - var bl gc.Node - gc.Regalloc(&bl, lo1.Type, nil) - var bh gc.Node - gc.Regalloc(&bh, hi1.Type, nil) - if v >= 32 { - // reverse during load to do the first 32 bits of rotate - v -= 32 - - gins(arm.AMOVW, &hi1, &bl) - gins(arm.AMOVW, &lo1, &bh) - } else { - gins(arm.AMOVW, &hi1, &bh) - gins(arm.AMOVW, &lo1, &bl) - } - - if v == 0 { - gins(arm.AMOVW, &bh, &ah) - gins(arm.AMOVW, &bl, &al) - } else { - // rotate by 1 <= v <= 31 - // MOVW bl<>(32-v), ah - // OR bh>>(32-v), al - gshift(arm.AMOVW, &bl, arm.SHIFT_LL, int32(v), &al) - - gshift(arm.AMOVW, &bh, arm.SHIFT_LL, int32(v), &ah) - gshift(arm.AORR, &bl, arm.SHIFT_LR, int32(32-v), &ah) - gshift(arm.AORR, &bh, arm.SHIFT_LR, int32(32-v), &al) - } - - gc.Regfree(&bl) - gc.Regfree(&bh) - - case gc.OLSH: - var bl gc.Node - gc.Regalloc(&bl, lo1.Type, nil) - var bh gc.Node - gc.Regalloc(&bh, hi1.Type, nil) - gins(arm.AMOVW, &hi1, &bh) - gins(arm.AMOVW, &lo1, &bl) - - var p6 *obj.Prog - var s gc.Node - var n1 gc.Node - var creg gc.Node - var p1 *obj.Prog - var p2 *obj.Prog - var p3 *obj.Prog - var p4 *obj.Prog - var p5 *obj.Prog - if r.Op == gc.OLITERAL { - v := uint64(r.Int()) - if v >= 64 { - // TODO(kaib): replace with gins(AMOVW, nodintconst(0), &al) - // here and below (verify it optimizes to EOR) - gins(arm.AEOR, &al, &al) - - gins(arm.AEOR, &ah, &ah) - } else if v > 32 { - gins(arm.AEOR, &al, &al) - - // MOVW bl<<(v-32), ah - gshift(arm.AMOVW, &bl, arm.SHIFT_LL, int32(v-32), &ah) - } else if v == 32 { - gins(arm.AEOR, &al, &al) - gins(arm.AMOVW, &bl, &ah) - } else if v > 0 { - // MOVW bl<>(32-v), ah - gshift(arm.AORR, &bl, arm.SHIFT_LR, int32(32-v), &ah) - } else { - gins(arm.AMOVW, &bl, &al) - gins(arm.AMOVW, &bh, &ah) - } - - goto olsh_break - } - - gc.Regalloc(&s, gc.Types[gc.TUINT32], nil) - gc.Regalloc(&creg, gc.Types[gc.TUINT32], nil) - if gc.Is64(r.Type) { - // shift is >= 1<<32 - var cl gc.Node - var ch gc.Node - split64(r, &cl, &ch) - - gmove(&ch, &s) - gins(arm.ATST, &s, nil) - p6 = gc.Gbranch(arm.ABNE, nil, 0) - gmove(&cl, &s) - splitclean() - } else { - gmove(r, &s) - p6 = nil - } - - gins(arm.ATST, &s, nil) - - // shift == 0 - p1 = gins(arm.AMOVW, &bl, &al) - - p1.Scond = arm.C_SCOND_EQ - p1 = gins(arm.AMOVW, &bh, &ah) - p1.Scond = arm.C_SCOND_EQ - p2 = gc.Gbranch(arm.ABEQ, nil, 0) - - // shift is < 32 - gc.Nodconst(&n1, gc.Types[gc.TUINT32], 32) - - gmove(&n1, &creg) - gins(arm.ACMP, &s, &creg) - - // MOVW.LO bl<>creg, ah - p1 = gregshift(arm.AORR, &bl, arm.SHIFT_LR, &creg, &ah) - - p1.Scond = arm.C_SCOND_LO - - // BLO end - p3 = gc.Gbranch(arm.ABLO, nil, 0) - - // shift == 32 - p1 = gins(arm.AEOR, &al, &al) - - p1.Scond = arm.C_SCOND_EQ - p1 = gins(arm.AMOVW, &bl, &ah) - p1.Scond = arm.C_SCOND_EQ - p4 = gc.Gbranch(arm.ABEQ, nil, 0) - - // shift is < 64 - gc.Nodconst(&n1, gc.Types[gc.TUINT32], 64) - - gmove(&n1, &creg) - gins(arm.ACMP, &s, &creg) - - // EOR.LO al, al - p1 = gins(arm.AEOR, &al, &al) - - p1.Scond = arm.C_SCOND_LO - - // MOVW.LO creg>>1, creg - p1 = gshift(arm.AMOVW, &creg, arm.SHIFT_LR, 1, &creg) - - p1.Scond = arm.C_SCOND_LO - - // SUB.LO creg, s - p1 = gins(arm.ASUB, &creg, &s) - - p1.Scond = arm.C_SCOND_LO - - // MOVW bl<= 64 - if p6 != nil { - gc.Patch(p6, gc.Pc) - } - gins(arm.AEOR, &al, &al) - gins(arm.AEOR, &ah, &ah) - - gc.Patch(p2, gc.Pc) - gc.Patch(p3, gc.Pc) - gc.Patch(p4, gc.Pc) - gc.Patch(p5, gc.Pc) - gc.Regfree(&s) - gc.Regfree(&creg) - - olsh_break: - gc.Regfree(&bl) - gc.Regfree(&bh) - - case gc.ORSH: - var bl gc.Node - gc.Regalloc(&bl, lo1.Type, nil) - var bh gc.Node - gc.Regalloc(&bh, hi1.Type, nil) - gins(arm.AMOVW, &hi1, &bh) - gins(arm.AMOVW, &lo1, &bl) - - var p4 *obj.Prog - var p5 *obj.Prog - var n1 gc.Node - var p6 *obj.Prog - var s gc.Node - var p1 *obj.Prog - var p2 *obj.Prog - var creg gc.Node - var p3 *obj.Prog - if r.Op == gc.OLITERAL { - v := uint64(r.Int()) - if v >= 64 { - if bh.Type.Etype == gc.TINT32 { - // MOVW bh->31, al - gshift(arm.AMOVW, &bh, arm.SHIFT_AR, 31, &al) - - // MOVW bh->31, ah - gshift(arm.AMOVW, &bh, arm.SHIFT_AR, 31, &ah) - } else { - gins(arm.AEOR, &al, &al) - gins(arm.AEOR, &ah, &ah) - } - } else if v > 32 { - if bh.Type.Etype == gc.TINT32 { - // MOVW bh->(v-32), al - gshift(arm.AMOVW, &bh, arm.SHIFT_AR, int32(v-32), &al) - - // MOVW bh->31, ah - gshift(arm.AMOVW, &bh, arm.SHIFT_AR, 31, &ah) - } else { - // MOVW bh>>(v-32), al - gshift(arm.AMOVW, &bh, arm.SHIFT_LR, int32(v-32), &al) - - gins(arm.AEOR, &ah, &ah) - } - } else if v == 32 { - gins(arm.AMOVW, &bh, &al) - if bh.Type.Etype == gc.TINT32 { - // MOVW bh->31, ah - gshift(arm.AMOVW, &bh, arm.SHIFT_AR, 31, &ah) - } else { - gins(arm.AEOR, &ah, &ah) - } - } else if v > 0 { - // MOVW bl>>v, al - gshift(arm.AMOVW, &bl, arm.SHIFT_LR, int32(v), &al) - - // OR bh<<(32-v), al - gshift(arm.AORR, &bh, arm.SHIFT_LL, int32(32-v), &al) - - if bh.Type.Etype == gc.TINT32 { - // MOVW bh->v, ah - gshift(arm.AMOVW, &bh, arm.SHIFT_AR, int32(v), &ah) - } else { - // MOVW bh>>v, ah - gshift(arm.AMOVW, &bh, arm.SHIFT_LR, int32(v), &ah) - } - } else { - gins(arm.AMOVW, &bl, &al) - gins(arm.AMOVW, &bh, &ah) - } - - goto orsh_break - } - - gc.Regalloc(&s, gc.Types[gc.TUINT32], nil) - gc.Regalloc(&creg, gc.Types[gc.TUINT32], nil) - if gc.Is64(r.Type) { - // shift is >= 1<<32 - var ch gc.Node - var cl gc.Node - split64(r, &cl, &ch) - - gmove(&ch, &s) - gins(arm.ATST, &s, nil) - var p1 *obj.Prog - if bh.Type.Etype == gc.TINT32 { - p1 = gshift(arm.AMOVW, &bh, arm.SHIFT_AR, 31, &ah) - } else { - p1 = gins(arm.AEOR, &ah, &ah) - } - p1.Scond = arm.C_SCOND_NE - p6 = gc.Gbranch(arm.ABNE, nil, 0) - gmove(&cl, &s) - splitclean() - } else { - gmove(r, &s) - p6 = nil - } - - gins(arm.ATST, &s, nil) - - // shift == 0 - p1 = gins(arm.AMOVW, &bl, &al) - - p1.Scond = arm.C_SCOND_EQ - p1 = gins(arm.AMOVW, &bh, &ah) - p1.Scond = arm.C_SCOND_EQ - p2 = gc.Gbranch(arm.ABEQ, nil, 0) - - // check if shift is < 32 - gc.Nodconst(&n1, gc.Types[gc.TUINT32], 32) - - gmove(&n1, &creg) - gins(arm.ACMP, &s, &creg) - - // MOVW.LO bl>>s, al - p1 = gregshift(arm.AMOVW, &bl, arm.SHIFT_LR, &s, &al) - - p1.Scond = arm.C_SCOND_LO - - // SUB.LO s,creg - p1 = gins(arm.ASUB, &s, &creg) - - p1.Scond = arm.C_SCOND_LO - - // OR.LO bh<<(32-s), al - p1 = gregshift(arm.AORR, &bh, arm.SHIFT_LL, &creg, &al) - - p1.Scond = arm.C_SCOND_LO - - if bh.Type.Etype == gc.TINT32 { - // MOVW bh->s, ah - p1 = gregshift(arm.AMOVW, &bh, arm.SHIFT_AR, &s, &ah) - } else { - // MOVW bh>>s, ah - p1 = gregshift(arm.AMOVW, &bh, arm.SHIFT_LR, &s, &ah) - } - - p1.Scond = arm.C_SCOND_LO - - // BLO end - p3 = gc.Gbranch(arm.ABLO, nil, 0) - - // shift == 32 - p1 = gins(arm.AMOVW, &bh, &al) - - p1.Scond = arm.C_SCOND_EQ - if bh.Type.Etype == gc.TINT32 { - gshift(arm.AMOVW, &bh, arm.SHIFT_AR, 31, &ah) - } else { - gins(arm.AEOR, &ah, &ah) - } - p4 = gc.Gbranch(arm.ABEQ, nil, 0) - - // check if shift is < 64 - gc.Nodconst(&n1, gc.Types[gc.TUINT32], 64) - - gmove(&n1, &creg) - gins(arm.ACMP, &s, &creg) - - // MOVW.LO creg>>1, creg - p1 = gshift(arm.AMOVW, &creg, arm.SHIFT_LR, 1, &creg) - - p1.Scond = arm.C_SCOND_LO - - // SUB.LO creg, s - p1 = gins(arm.ASUB, &creg, &s) - - p1.Scond = arm.C_SCOND_LO - - if bh.Type.Etype == gc.TINT32 { - // MOVW bh->(s-32), al - p1 := gregshift(arm.AMOVW, &bh, arm.SHIFT_AR, &s, &al) - - p1.Scond = arm.C_SCOND_LO - } else { - // MOVW bh>>(v-32), al - p1 := gregshift(arm.AMOVW, &bh, arm.SHIFT_LR, &s, &al) - - p1.Scond = arm.C_SCOND_LO - } - - // BLO end - p5 = gc.Gbranch(arm.ABLO, nil, 0) - - // s >= 64 - if p6 != nil { - gc.Patch(p6, gc.Pc) - } - if bh.Type.Etype == gc.TINT32 { - // MOVW bh->31, al - gshift(arm.AMOVW, &bh, arm.SHIFT_AR, 31, &al) - } else { - gins(arm.AEOR, &al, &al) - } - - gc.Patch(p2, gc.Pc) - gc.Patch(p3, gc.Pc) - gc.Patch(p4, gc.Pc) - gc.Patch(p5, gc.Pc) - gc.Regfree(&s) - gc.Regfree(&creg) - - orsh_break: - gc.Regfree(&bl) - gc.Regfree(&bh) - - // TODO(kaib): literal optimizations - // make constant the right side (it usually is anyway). - // if(lo1.op == OLITERAL) { - // nswap(&lo1, &lo2); - // nswap(&hi1, &hi2); - // } - // if(lo2.op == OLITERAL) { - // // special cases for constants. - // lv = mpgetfix(lo2.val.u.xval); - // hv = mpgetfix(hi2.val.u.xval); - // splitclean(); // right side - // split64(res, &lo2, &hi2); - // switch(n->op) { - // case OXOR: - // gmove(&lo1, &lo2); - // gmove(&hi1, &hi2); - // switch(lv) { - // case 0: - // break; - // case 0xffffffffu: - // gins(ANOTL, N, &lo2); - // break; - // default: - // gins(AXORL, ncon(lv), &lo2); - // break; - // } - // switch(hv) { - // case 0: - // break; - // case 0xffffffffu: - // gins(ANOTL, N, &hi2); - // break; - // default: - // gins(AXORL, ncon(hv), &hi2); - // break; - // } - // break; - - // case OAND: - // switch(lv) { - // case 0: - // gins(AMOVL, ncon(0), &lo2); - // break; - // default: - // gmove(&lo1, &lo2); - // if(lv != 0xffffffffu) - // gins(AANDL, ncon(lv), &lo2); - // break; - // } - // switch(hv) { - // case 0: - // gins(AMOVL, ncon(0), &hi2); - // break; - // default: - // gmove(&hi1, &hi2); - // if(hv != 0xffffffffu) - // gins(AANDL, ncon(hv), &hi2); - // break; - // } - // break; - - // case OOR: - // switch(lv) { - // case 0: - // gmove(&lo1, &lo2); - // break; - // case 0xffffffffu: - // gins(AMOVL, ncon(0xffffffffu), &lo2); - // break; - // default: - // gmove(&lo1, &lo2); - // gins(AORL, ncon(lv), &lo2); - // break; - // } - // switch(hv) { - // case 0: - // gmove(&hi1, &hi2); - // break; - // case 0xffffffffu: - // gins(AMOVL, ncon(0xffffffffu), &hi2); - // break; - // default: - // gmove(&hi1, &hi2); - // gins(AORL, ncon(hv), &hi2); - // break; - // } - // break; - // } - // splitclean(); - // splitclean(); - // goto out; - // } - case gc.OXOR, - gc.OAND, - gc.OOR: - var n1 gc.Node - gc.Regalloc(&n1, lo1.Type, nil) - - gins(arm.AMOVW, &lo1, &al) - gins(arm.AMOVW, &hi1, &ah) - gins(arm.AMOVW, &lo2, &n1) - gins(optoas(int(n.Op), lo1.Type), &n1, &al) - gins(arm.AMOVW, &hi2, &n1) - gins(optoas(int(n.Op), lo1.Type), &n1, &ah) - gc.Regfree(&n1) - } - - if gc.Is64(r.Type) { - splitclean() - } - splitclean() - - split64(res, &lo1, &hi1) - gins(arm.AMOVW, &al, &lo1) - gins(arm.AMOVW, &ah, &hi1) - splitclean() - - //out: - gc.Regfree(&al) - - gc.Regfree(&ah) -} - -/* - * generate comparison of nl, nr, both 64-bit. - * nl is memory; nr is constant or memory. - */ -func cmp64(nl *gc.Node, nr *gc.Node, op int, likely int, to *obj.Prog) { - var lo1 gc.Node - var hi1 gc.Node - var lo2 gc.Node - var hi2 gc.Node - var r1 gc.Node - var r2 gc.Node - - split64(nl, &lo1, &hi1) - split64(nr, &lo2, &hi2) - - // compare most significant word; - // if they differ, we're done. - t := hi1.Type - - gc.Regalloc(&r1, gc.Types[gc.TINT32], nil) - gc.Regalloc(&r2, gc.Types[gc.TINT32], nil) - gins(arm.AMOVW, &hi1, &r1) - gins(arm.AMOVW, &hi2, &r2) - gins(arm.ACMP, &r1, &r2) - gc.Regfree(&r1) - gc.Regfree(&r2) - - var br *obj.Prog - switch op { - default: - gc.Fatal("cmp64 %v %v", gc.Oconv(int(op), 0), t) - - // cmp hi - // bne L - // cmp lo - // beq to - // L: - case gc.OEQ: - br = gc.Gbranch(arm.ABNE, nil, -likely) - - // cmp hi - // bne to - // cmp lo - // bne to - case gc.ONE: - gc.Patch(gc.Gbranch(arm.ABNE, nil, likely), to) - - // cmp hi - // bgt to - // blt L - // cmp lo - // bge to (or bgt to) - // L: - case gc.OGE, - gc.OGT: - gc.Patch(gc.Gbranch(optoas(gc.OGT, t), nil, likely), to) - - br = gc.Gbranch(optoas(gc.OLT, t), nil, -likely) - - // cmp hi - // blt to - // bgt L - // cmp lo - // ble to (or jlt to) - // L: - case gc.OLE, - gc.OLT: - gc.Patch(gc.Gbranch(optoas(gc.OLT, t), nil, likely), to) - - br = gc.Gbranch(optoas(gc.OGT, t), nil, -likely) - } - - // compare least significant word - t = lo1.Type - - gc.Regalloc(&r1, gc.Types[gc.TINT32], nil) - gc.Regalloc(&r2, gc.Types[gc.TINT32], nil) - gins(arm.AMOVW, &lo1, &r1) - gins(arm.AMOVW, &lo2, &r2) - gins(arm.ACMP, &r1, &r2) - gc.Regfree(&r1) - gc.Regfree(&r2) - - // jump again - gc.Patch(gc.Gbranch(optoas(op, t), nil, likely), to) - - // point first branch down here if appropriate - if br != nil { - gc.Patch(br, gc.Pc) - } - - splitclean() - splitclean() -} diff --git a/src/cmd/5g/galign.go b/src/cmd/5g/galign.go deleted file mode 100644 index 55782e1dae..0000000000 --- a/src/cmd/5g/galign.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2009 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/gc" - "cmd/internal/obj" - "cmd/internal/obj/arm" -) - -var thechar int = '5' - -var thestring string = "arm" - -var thelinkarch *obj.LinkArch = &arm.Linkarm - -func linkarchinit() { -} - -var MAXWIDTH int64 = (1 << 32) - 1 - -/* - * go declares several platform-specific type aliases: - * int, uint, and uintptr - */ -var typedefs = []gc.Typedef{ - gc.Typedef{"int", gc.TINT, gc.TINT32}, - gc.Typedef{"uint", gc.TUINT, gc.TUINT32}, - gc.Typedef{"uintptr", gc.TUINTPTR, gc.TUINT32}, -} - -func betypeinit() { - gc.Widthptr = 4 - gc.Widthint = 4 - gc.Widthreg = 4 -} - -func main() { - gc.Thearch.Thechar = thechar - gc.Thearch.Thestring = thestring - gc.Thearch.Thelinkarch = thelinkarch - gc.Thearch.Typedefs = typedefs - gc.Thearch.REGSP = arm.REGSP - gc.Thearch.REGCTXT = arm.REGCTXT - gc.Thearch.REGCALLX = arm.REG_R1 - gc.Thearch.REGCALLX2 = arm.REG_R2 - gc.Thearch.REGRETURN = arm.REG_R0 - gc.Thearch.REGMIN = arm.REG_R0 - gc.Thearch.REGMAX = arm.REGEXT - gc.Thearch.FREGMIN = arm.REG_F0 - gc.Thearch.FREGMAX = arm.FREGEXT - gc.Thearch.MAXWIDTH = MAXWIDTH - gc.Thearch.ReservedRegs = resvd - - gc.Thearch.Betypeinit = betypeinit - gc.Thearch.Cgen64 = cgen64 - gc.Thearch.Cgen_hmul = cgen_hmul - gc.Thearch.Cgen_shift = cgen_shift - gc.Thearch.Clearfat = clearfat - gc.Thearch.Cmp64 = cmp64 - gc.Thearch.Defframe = defframe - gc.Thearch.Excise = excise - 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 - gc.Thearch.Cgenindex = cgenindex - gc.Thearch.Linkarchinit = linkarchinit - gc.Thearch.Peep = peep - gc.Thearch.Proginfo = proginfo - gc.Thearch.Regtyp = regtyp - gc.Thearch.Sameaddr = sameaddr - gc.Thearch.Smallindir = smallindir - gc.Thearch.Stackaddr = stackaddr - gc.Thearch.Blockcopy = blockcopy - gc.Thearch.Sudoaddable = sudoaddable - gc.Thearch.Sudoclean = sudoclean - gc.Thearch.Excludedregs = excludedregs - gc.Thearch.RtoB = RtoB - gc.Thearch.FtoB = RtoB - gc.Thearch.BtoR = BtoR - gc.Thearch.BtoF = BtoF - gc.Thearch.Optoas = optoas - gc.Thearch.Doregbits = doregbits - gc.Thearch.Regnames = regnames - - gc.Main() - gc.Exit(0) -} diff --git a/src/cmd/5g/ggen.go b/src/cmd/5g/ggen.go deleted file mode 100644 index 2ab5d521bb..0000000000 --- a/src/cmd/5g/ggen.go +++ /dev/null @@ -1,529 +0,0 @@ -// Copyright 2009 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/gc" - "cmd/internal/obj" - "cmd/internal/obj/arm" -) - -func defframe(ptxt *obj.Prog) { - var n *gc.Node - - // fill in argument size, stack size - ptxt.To.Type = obj.TYPE_TEXTSIZE - - ptxt.To.Val = int32(gc.Rnd(gc.Curfn.Type.Argwid, int64(gc.Widthptr))) - frame := uint32(gc.Rnd(gc.Stksize+gc.Maxarg, int64(gc.Widthreg))) - ptxt.To.Offset = int64(frame) - - // insert code to contain ambiguously live variables - // so that garbage collector only sees initialized values - // when it looks for pointers. - p := ptxt - - hi := int64(0) - lo := hi - r0 := uint32(0) - for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next { - n = l.N - if !n.Name.Needzero { - continue - } - if n.Class != gc.PAUTO { - gc.Fatal("needzero class %d", n.Class) - } - if n.Type.Width%int64(gc.Widthptr) != 0 || n.Xoffset%int64(gc.Widthptr) != 0 || n.Type.Width == 0 { - gc.Fatal("var %v has size %d offset %d", gc.Nconv(n, obj.FmtLong), int(n.Type.Width), int(n.Xoffset)) - } - if lo != hi && n.Xoffset+n.Type.Width >= lo-int64(2*gc.Widthptr) { - // merge with range we already have - lo = gc.Rnd(n.Xoffset, int64(gc.Widthptr)) - - continue - } - - // zero old range - p = zerorange(p, int64(frame), lo, hi, &r0) - - // set new range - hi = n.Xoffset + n.Type.Width - - lo = n.Xoffset - } - - // zero final range - zerorange(p, int64(frame), lo, hi, &r0) -} - -func zerorange(p *obj.Prog, frame int64, lo int64, hi int64, r0 *uint32) *obj.Prog { - cnt := hi - lo - if cnt == 0 { - return p - } - if *r0 == 0 { - p = appendpp(p, arm.AMOVW, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, arm.REG_R0, 0) - *r0 = 1 - } - - if cnt < int64(4*gc.Widthptr) { - for i := int64(0); i < cnt; i += int64(gc.Widthptr) { - p = appendpp(p, arm.AMOVW, obj.TYPE_REG, arm.REG_R0, 0, obj.TYPE_MEM, arm.REGSP, int32(4+frame+lo+i)) - } - } else if !gc.Nacl && (cnt <= int64(128*gc.Widthptr)) { - p = appendpp(p, arm.AADD, obj.TYPE_CONST, 0, int32(4+frame+lo), obj.TYPE_REG, arm.REG_R1, 0) - p.Reg = arm.REGSP - p = appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0) - f := gc.Sysfunc("duffzero") - gc.Naddr(&p.To, f) - gc.Afunclit(&p.To, f) - p.To.Offset = 4 * (128 - cnt/int64(gc.Widthptr)) - } else { - p = appendpp(p, arm.AADD, obj.TYPE_CONST, 0, int32(4+frame+lo), obj.TYPE_REG, arm.REG_R1, 0) - p.Reg = arm.REGSP - p = appendpp(p, arm.AADD, obj.TYPE_CONST, 0, int32(cnt), obj.TYPE_REG, arm.REG_R2, 0) - p.Reg = arm.REG_R1 - p = appendpp(p, arm.AMOVW, obj.TYPE_REG, arm.REG_R0, 0, obj.TYPE_MEM, arm.REG_R1, 4) - p1 := p - p.Scond |= arm.C_PBIT - p = appendpp(p, arm.ACMP, obj.TYPE_REG, arm.REG_R1, 0, obj.TYPE_NONE, 0, 0) - p.Reg = arm.REG_R2 - p = appendpp(p, arm.ABNE, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0) - gc.Patch(p, p1) - } - - return p -} - -func appendpp(p *obj.Prog, as int, ftype int, freg int, foffset int32, ttype int, treg int, toffset int32) *obj.Prog { - q := gc.Ctxt.NewProg() - gc.Clearp(q) - q.As = int16(as) - q.Lineno = p.Lineno - q.From.Type = int16(ftype) - q.From.Reg = int16(freg) - q.From.Offset = int64(foffset) - q.To.Type = int16(ttype) - q.To.Reg = int16(treg) - q.To.Offset = int64(toffset) - q.Link = p.Link - p.Link = q - return q -} - -/* - * generate high multiply - * res = (nl * nr) >> wordsize - */ -func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) { - if nl.Ullman < nr.Ullman { - tmp := nl - nl = nr - nr = tmp - } - - t := nl.Type - w := int(t.Width * 8) - var n1 gc.Node - gc.Regalloc(&n1, t, res) - gc.Cgen(nl, &n1) - var n2 gc.Node - gc.Regalloc(&n2, t, nil) - gc.Cgen(nr, &n2) - switch gc.Simtype[t.Etype] { - case gc.TINT8, - gc.TINT16: - gins(optoas(gc.OMUL, t), &n2, &n1) - gshift(arm.AMOVW, &n1, arm.SHIFT_AR, int32(w), &n1) - - case gc.TUINT8, - gc.TUINT16: - gins(optoas(gc.OMUL, t), &n2, &n1) - gshift(arm.AMOVW, &n1, arm.SHIFT_LR, int32(w), &n1) - - // perform a long multiplication. - case gc.TINT32, - gc.TUINT32: - var p *obj.Prog - if gc.Issigned[t.Etype] { - p = gins(arm.AMULL, &n2, nil) - } else { - p = gins(arm.AMULLU, &n2, nil) - } - - // n2 * n1 -> (n1 n2) - p.Reg = n1.Reg - - p.To.Type = obj.TYPE_REGREG - p.To.Reg = n1.Reg - p.To.Offset = int64(n2.Reg) - - default: - gc.Fatal("cgen_hmul %v", t) - } - - gc.Cgen(&n1, res) - gc.Regfree(&n1) - gc.Regfree(&n2) -} - -/* - * generate shift according to op, one of: - * res = nl << nr - * res = nl >> nr - */ -func cgen_shift(op int, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) { - if nl.Type.Width > 4 { - gc.Fatal("cgen_shift %v", nl.Type) - } - - w := int(nl.Type.Width * 8) - - if op == gc.OLROT { - v := nr.Int() - var n1 gc.Node - gc.Regalloc(&n1, nl.Type, res) - if w == 32 { - gc.Cgen(nl, &n1) - gshift(arm.AMOVW, &n1, arm.SHIFT_RR, int32(w)-int32(v), &n1) - } else { - var n2 gc.Node - gc.Regalloc(&n2, nl.Type, nil) - gc.Cgen(nl, &n2) - gshift(arm.AMOVW, &n2, arm.SHIFT_LL, int32(v), &n1) - gshift(arm.AORR, &n2, arm.SHIFT_LR, int32(w)-int32(v), &n1) - gc.Regfree(&n2) - - // Ensure sign/zero-extended result. - gins(optoas(gc.OAS, nl.Type), &n1, &n1) - } - - gmove(&n1, res) - gc.Regfree(&n1) - return - } - - if nr.Op == gc.OLITERAL { - var n1 gc.Node - gc.Regalloc(&n1, nl.Type, res) - gc.Cgen(nl, &n1) - sc := uint64(nr.Int()) - if sc == 0 { - } else // nothing to do - if sc >= uint64(nl.Type.Width*8) { - if op == gc.ORSH && gc.Issigned[nl.Type.Etype] { - gshift(arm.AMOVW, &n1, arm.SHIFT_AR, int32(w), &n1) - } else { - gins(arm.AEOR, &n1, &n1) - } - } else { - if op == gc.ORSH && gc.Issigned[nl.Type.Etype] { - gshift(arm.AMOVW, &n1, arm.SHIFT_AR, int32(sc), &n1) - } else if op == gc.ORSH { - gshift(arm.AMOVW, &n1, arm.SHIFT_LR, int32(sc), &n1) // OLSH - } else { - gshift(arm.AMOVW, &n1, arm.SHIFT_LL, int32(sc), &n1) - } - } - - if w < 32 && op == gc.OLSH { - gins(optoas(gc.OAS, nl.Type), &n1, &n1) - } - gmove(&n1, res) - gc.Regfree(&n1) - return - } - - tr := nr.Type - var t gc.Node - var n1 gc.Node - var n2 gc.Node - var n3 gc.Node - if tr.Width > 4 { - var nt gc.Node - gc.Tempname(&nt, nr.Type) - if nl.Ullman >= nr.Ullman { - gc.Regalloc(&n2, nl.Type, res) - gc.Cgen(nl, &n2) - gc.Cgen(nr, &nt) - n1 = nt - } else { - gc.Cgen(nr, &nt) - gc.Regalloc(&n2, nl.Type, res) - gc.Cgen(nl, &n2) - } - - var hi gc.Node - var lo gc.Node - split64(&nt, &lo, &hi) - gc.Regalloc(&n1, gc.Types[gc.TUINT32], nil) - gc.Regalloc(&n3, gc.Types[gc.TUINT32], nil) - gmove(&lo, &n1) - gmove(&hi, &n3) - splitclean() - gins(arm.ATST, &n3, nil) - gc.Nodconst(&t, gc.Types[gc.TUINT32], int64(w)) - p1 := gins(arm.AMOVW, &t, &n1) - p1.Scond = arm.C_SCOND_NE - tr = gc.Types[gc.TUINT32] - gc.Regfree(&n3) - } else { - if nl.Ullman >= nr.Ullman { - gc.Regalloc(&n2, nl.Type, res) - gc.Cgen(nl, &n2) - gc.Regalloc(&n1, nr.Type, nil) - gc.Cgen(nr, &n1) - } else { - gc.Regalloc(&n1, nr.Type, nil) - gc.Cgen(nr, &n1) - gc.Regalloc(&n2, nl.Type, res) - gc.Cgen(nl, &n2) - } - } - - // test for shift being 0 - gins(arm.ATST, &n1, nil) - - p3 := gc.Gbranch(arm.ABEQ, nil, -1) - - // test and fix up large shifts - // TODO: if(!bounded), don't emit some of this. - gc.Regalloc(&n3, tr, nil) - - gc.Nodconst(&t, gc.Types[gc.TUINT32], int64(w)) - gmove(&t, &n3) - gins(arm.ACMP, &n1, &n3) - if op == gc.ORSH { - var p1 *obj.Prog - var p2 *obj.Prog - if gc.Issigned[nl.Type.Etype] { - p1 = gshift(arm.AMOVW, &n2, arm.SHIFT_AR, int32(w)-1, &n2) - p2 = gregshift(arm.AMOVW, &n2, arm.SHIFT_AR, &n1, &n2) - } else { - p1 = gins(arm.AEOR, &n2, &n2) - p2 = gregshift(arm.AMOVW, &n2, arm.SHIFT_LR, &n1, &n2) - } - - p1.Scond = arm.C_SCOND_HS - p2.Scond = arm.C_SCOND_LO - } else { - p1 := gins(arm.AEOR, &n2, &n2) - p2 := gregshift(arm.AMOVW, &n2, arm.SHIFT_LL, &n1, &n2) - p1.Scond = arm.C_SCOND_HS - p2.Scond = arm.C_SCOND_LO - } - - gc.Regfree(&n3) - - gc.Patch(p3, gc.Pc) - - // Left-shift of smaller word must be sign/zero-extended. - if w < 32 && op == gc.OLSH { - gins(optoas(gc.OAS, nl.Type), &n2, &n2) - } - gmove(&n2, res) - - gc.Regfree(&n1) - gc.Regfree(&n2) -} - -func clearfat(nl *gc.Node) { - /* clear a fat object */ - if gc.Debug['g'] != 0 { - gc.Dump("\nclearfat", nl) - } - - w := uint32(nl.Type.Width) - - // Avoid taking the address for simple enough types. - if gc.Componentgen(nil, nl) { - return - } - - c := w % 4 // bytes - q := w / 4 // quads - - var r0 gc.Node - r0.Op = gc.OREGISTER - - r0.Reg = arm.REG_R0 - var r1 gc.Node - r1.Op = gc.OREGISTER - r1.Reg = arm.REG_R1 - var dst gc.Node - gc.Regalloc(&dst, gc.Types[gc.Tptr], &r1) - gc.Agen(nl, &dst) - var nc gc.Node - gc.Nodconst(&nc, gc.Types[gc.TUINT32], 0) - var nz gc.Node - gc.Regalloc(&nz, gc.Types[gc.TUINT32], &r0) - gc.Cgen(&nc, &nz) - - if q > 128 { - var end gc.Node - gc.Regalloc(&end, gc.Types[gc.Tptr], nil) - p := gins(arm.AMOVW, &dst, &end) - p.From.Type = obj.TYPE_ADDR - p.From.Offset = int64(q) * 4 - - p = gins(arm.AMOVW, &nz, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = 4 - p.Scond |= arm.C_PBIT - pl := p - - p = gins(arm.ACMP, &dst, nil) - raddr(&end, p) - gc.Patch(gc.Gbranch(arm.ABNE, nil, 0), pl) - - gc.Regfree(&end) - } else if q >= 4 && !gc.Nacl { - f := gc.Sysfunc("duffzero") - p := gins(obj.ADUFFZERO, nil, f) - gc.Afunclit(&p.To, f) - - // 4 and 128 = magic constants: see ../../runtime/asm_arm.s - p.To.Offset = 4 * (128 - int64(q)) - } else { - var p *obj.Prog - for q > 0 { - p = gins(arm.AMOVW, &nz, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = 4 - p.Scond |= arm.C_PBIT - - //print("1. %P\n", p); - q-- - } - } - - var p *obj.Prog - for c > 0 { - p = gins(arm.AMOVB, &nz, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = 1 - p.Scond |= arm.C_PBIT - - //print("2. %P\n", p); - c-- - } - - gc.Regfree(&dst) - gc.Regfree(&nz) -} - -// Called after regopt and peep have run. -// Expand CHECKNIL pseudo-op into actual nil pointer check. -func expandchecks(firstp *obj.Prog) { - var reg int - var p1 *obj.Prog - - for p := firstp; p != nil; p = p.Link { - if p.As != obj.ACHECKNIL { - continue - } - if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers - gc.Warnl(int(p.Lineno), "generated nil check") - } - if p.From.Type != obj.TYPE_REG { - gc.Fatal("invalid nil check %v", p) - } - reg = int(p.From.Reg) - - // check is - // CMP arg, $0 - // MOV.EQ arg, 0(arg) - p1 = gc.Ctxt.NewProg() - - gc.Clearp(p1) - p1.Link = p.Link - p.Link = p1 - p1.Lineno = p.Lineno - p1.Pc = 9999 - p1.As = arm.AMOVW - p1.From.Type = obj.TYPE_REG - p1.From.Reg = int16(reg) - p1.To.Type = obj.TYPE_MEM - p1.To.Reg = int16(reg) - p1.To.Offset = 0 - p1.Scond = arm.C_SCOND_EQ - p.As = arm.ACMP - p.From.Type = obj.TYPE_CONST - p.From.Reg = 0 - p.From.Offset = 0 - p.Reg = int16(reg) - } -} - -func ginsnop() { - var r gc.Node - gc.Nodreg(&r, gc.Types[gc.TINT], arm.REG_R0) - p := gins(arm.AAND, &r, &r) - p.Scond = arm.C_SCOND_EQ -} - -/* - * generate - * as $c, n - */ -func ginscon(as int, c int64, n *gc.Node) { - var n1 gc.Node - gc.Nodconst(&n1, gc.Types[gc.TINT32], c) - var n2 gc.Node - gc.Regalloc(&n2, gc.Types[gc.TINT32], nil) - gmove(&n1, &n2) - gins(as, &n2, n) - gc.Regfree(&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 && n1.Int() == 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) - if gc.Isint[t.Etype] && n2.Op == gc.OLITERAL && n2.Int() == 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) - 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 { - case 2: - gshift(arm.AADD, index, arm.SHIFT_LL, 1, addr) - return true - case 4: - gshift(arm.AADD, index, arm.SHIFT_LL, 2, addr) - return true - case 8: - gshift(arm.AADD, index, arm.SHIFT_LL, 3, addr) - return true - } - return false -} - -// res = runtime.getg() -func getg(res *gc.Node) { - var n1 gc.Node - gc.Nodreg(&n1, res.Type, arm.REGG) - gmove(&n1, res) -} diff --git a/src/cmd/5g/gsubr.go b/src/cmd/5g/gsubr.go deleted file mode 100644 index 2a23580b58..0000000000 --- a/src/cmd/5g/gsubr.go +++ /dev/null @@ -1,1209 +0,0 @@ -// Derived from Inferno utils/5c/txt.c -// http://code.google.com/p/inferno-os/source/browse/utils/5c/txt.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package main - -import ( - "cmd/internal/gc" - "cmd/internal/obj" - "cmd/internal/obj/arm" - "fmt" -) - -var resvd = []int{ - arm.REG_R9, // formerly reserved for m; might be okay to reuse now; not sure about NaCl - arm.REG_R10, // reserved for g -} - -/* - * return constant i node. - * overwritten by next call, but useful in calls to gins. - */ - -var ncon_n gc.Node - -func ncon(i uint32) *gc.Node { - if ncon_n.Type == nil { - gc.Nodconst(&ncon_n, gc.Types[gc.TUINT32], 0) - } - ncon_n.SetInt(int64(i)) - return &ncon_n -} - -var sclean [10]gc.Node - -var nsclean int - -/* - * n is a 64-bit value. fill in lo and hi to refer to its 32-bit halves. - */ -func split64(n *gc.Node, lo *gc.Node, hi *gc.Node) { - if !gc.Is64(n.Type) { - gc.Fatal("split64 %v", n.Type) - } - - if nsclean >= len(sclean) { - gc.Fatal("split64 clean") - } - sclean[nsclean].Op = gc.OEMPTY - nsclean++ - switch n.Op { - default: - switch n.Op { - default: - var n1 gc.Node - if !dotaddable(n, &n1) { - gc.Igen(n, &n1, nil) - sclean[nsclean-1] = n1 - } - - n = &n1 - - case gc.ONAME: - if n.Class == gc.PPARAMREF { - var n1 gc.Node - gc.Cgen(n.Name.Heapaddr, &n1) - sclean[nsclean-1] = n1 - n = &n1 - } - - // nothing - case gc.OINDREG: - break - } - - *lo = *n - *hi = *n - lo.Type = gc.Types[gc.TUINT32] - if n.Type.Etype == gc.TINT64 { - hi.Type = gc.Types[gc.TINT32] - } else { - hi.Type = gc.Types[gc.TUINT32] - } - hi.Xoffset += 4 - - case gc.OLITERAL: - var n1 gc.Node - n.Convconst(&n1, n.Type) - i := n1.Int() - gc.Nodconst(lo, gc.Types[gc.TUINT32], int64(uint32(i))) - i >>= 32 - if n.Type.Etype == gc.TINT64 { - gc.Nodconst(hi, gc.Types[gc.TINT32], int64(int32(i))) - } else { - gc.Nodconst(hi, gc.Types[gc.TUINT32], int64(uint32(i))) - } - } -} - -func splitclean() { - if nsclean <= 0 { - gc.Fatal("splitclean") - } - nsclean-- - if sclean[nsclean].Op != gc.OEMPTY { - gc.Regfree(&sclean[nsclean]) - } -} - -func gmove(f *gc.Node, t *gc.Node) { - if gc.Debug['M'] != 0 { - fmt.Printf("gmove %v -> %v\n", f, t) - } - - ft := gc.Simsimtype(f.Type) - tt := gc.Simsimtype(t.Type) - cvt := t.Type - - if gc.Iscomplex[ft] || gc.Iscomplex[tt] { - gc.Complexmove(f, t) - return - } - - // cannot have two memory operands; - // except 64-bit, which always copies via registers anyway. - var a int - var r1 gc.Node - if !gc.Is64(f.Type) && !gc.Is64(t.Type) && gc.Ismem(f) && gc.Ismem(t) { - goto hard - } - - // convert constant to desired type - if f.Op == gc.OLITERAL { - var con gc.Node - switch tt { - default: - f.Convconst(&con, t.Type) - - case gc.TINT16, - gc.TINT8: - var con gc.Node - f.Convconst(&con, gc.Types[gc.TINT32]) - var r1 gc.Node - gc.Regalloc(&r1, con.Type, t) - gins(arm.AMOVW, &con, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - - case gc.TUINT16, - gc.TUINT8: - var con gc.Node - f.Convconst(&con, gc.Types[gc.TUINT32]) - var r1 gc.Node - gc.Regalloc(&r1, con.Type, t) - gins(arm.AMOVW, &con, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - } - - f = &con - ft = gc.Simsimtype(con.Type) - - // constants can't move directly to memory - if gc.Ismem(t) && !gc.Is64(t.Type) { - goto hard - } - } - - // value -> value copy, only one memory operand. - // figure out the instruction to use. - // break out of switch for one-instruction gins. - // goto rdst for "destination must be register". - // goto hard for "convert to cvt type first". - // otherwise handle and return. - - switch uint32(ft)<<16 | uint32(tt) { - default: - // should not happen - gc.Fatal("gmove %v -> %v", f, t) - return - - /* - * integer copy and truncate - */ - case gc.TINT8<<16 | gc.TINT8: // same size - if !gc.Ismem(f) { - a = arm.AMOVB - break - } - fallthrough - - case gc.TUINT8<<16 | gc.TINT8, - gc.TINT16<<16 | gc.TINT8, // truncate - gc.TUINT16<<16 | gc.TINT8, - gc.TINT32<<16 | gc.TINT8, - gc.TUINT32<<16 | gc.TINT8: - a = arm.AMOVBS - - case gc.TUINT8<<16 | gc.TUINT8: - if !gc.Ismem(f) { - a = arm.AMOVB - break - } - fallthrough - - case gc.TINT8<<16 | gc.TUINT8, - gc.TINT16<<16 | gc.TUINT8, - gc.TUINT16<<16 | gc.TUINT8, - gc.TINT32<<16 | gc.TUINT8, - gc.TUINT32<<16 | gc.TUINT8: - a = arm.AMOVBU - - case gc.TINT64<<16 | gc.TINT8, // truncate low word - gc.TUINT64<<16 | gc.TINT8: - a = arm.AMOVBS - - goto trunc64 - - case gc.TINT64<<16 | gc.TUINT8, - gc.TUINT64<<16 | gc.TUINT8: - a = arm.AMOVBU - goto trunc64 - - case gc.TINT16<<16 | gc.TINT16: // same size - if !gc.Ismem(f) { - a = arm.AMOVH - break - } - fallthrough - - case gc.TUINT16<<16 | gc.TINT16, - gc.TINT32<<16 | gc.TINT16, // truncate - gc.TUINT32<<16 | gc.TINT16: - a = arm.AMOVHS - - case gc.TUINT16<<16 | gc.TUINT16: - if !gc.Ismem(f) { - a = arm.AMOVH - break - } - fallthrough - - case gc.TINT16<<16 | gc.TUINT16, - gc.TINT32<<16 | gc.TUINT16, - gc.TUINT32<<16 | gc.TUINT16: - a = arm.AMOVHU - - case gc.TINT64<<16 | gc.TINT16, // truncate low word - gc.TUINT64<<16 | gc.TINT16: - a = arm.AMOVHS - - goto trunc64 - - case gc.TINT64<<16 | gc.TUINT16, - gc.TUINT64<<16 | gc.TUINT16: - a = arm.AMOVHU - goto trunc64 - - case gc.TINT32<<16 | gc.TINT32, // same size - gc.TINT32<<16 | gc.TUINT32, - gc.TUINT32<<16 | gc.TINT32, - gc.TUINT32<<16 | gc.TUINT32: - a = arm.AMOVW - - case gc.TINT64<<16 | gc.TINT32, // truncate - gc.TUINT64<<16 | gc.TINT32, - gc.TINT64<<16 | gc.TUINT32, - gc.TUINT64<<16 | gc.TUINT32: - var flo gc.Node - var fhi gc.Node - split64(f, &flo, &fhi) - - var r1 gc.Node - gc.Regalloc(&r1, t.Type, nil) - gins(arm.AMOVW, &flo, &r1) - gins(arm.AMOVW, &r1, t) - gc.Regfree(&r1) - splitclean() - return - - case gc.TINT64<<16 | gc.TINT64, // same size - gc.TINT64<<16 | gc.TUINT64, - gc.TUINT64<<16 | gc.TINT64, - gc.TUINT64<<16 | gc.TUINT64: - var fhi gc.Node - var flo gc.Node - split64(f, &flo, &fhi) - - var tlo gc.Node - var thi gc.Node - split64(t, &tlo, &thi) - var r1 gc.Node - gc.Regalloc(&r1, flo.Type, nil) - var r2 gc.Node - gc.Regalloc(&r2, fhi.Type, nil) - gins(arm.AMOVW, &flo, &r1) - gins(arm.AMOVW, &fhi, &r2) - gins(arm.AMOVW, &r1, &tlo) - gins(arm.AMOVW, &r2, &thi) - gc.Regfree(&r1) - gc.Regfree(&r2) - splitclean() - splitclean() - return - - /* - * integer up-conversions - */ - case gc.TINT8<<16 | gc.TINT16, // sign extend int8 - gc.TINT8<<16 | gc.TUINT16, - gc.TINT8<<16 | gc.TINT32, - gc.TINT8<<16 | gc.TUINT32: - a = arm.AMOVBS - - goto rdst - - case gc.TINT8<<16 | gc.TINT64, // convert via int32 - gc.TINT8<<16 | gc.TUINT64: - cvt = gc.Types[gc.TINT32] - - goto hard - - case gc.TUINT8<<16 | gc.TINT16, // zero extend uint8 - gc.TUINT8<<16 | gc.TUINT16, - gc.TUINT8<<16 | gc.TINT32, - gc.TUINT8<<16 | gc.TUINT32: - a = arm.AMOVBU - - goto rdst - - case gc.TUINT8<<16 | gc.TINT64, // convert via uint32 - gc.TUINT8<<16 | gc.TUINT64: - cvt = gc.Types[gc.TUINT32] - - goto hard - - case gc.TINT16<<16 | gc.TINT32, // sign extend int16 - gc.TINT16<<16 | gc.TUINT32: - a = arm.AMOVHS - - goto rdst - - case gc.TINT16<<16 | gc.TINT64, // convert via int32 - gc.TINT16<<16 | gc.TUINT64: - cvt = gc.Types[gc.TINT32] - - goto hard - - case gc.TUINT16<<16 | gc.TINT32, // zero extend uint16 - gc.TUINT16<<16 | gc.TUINT32: - a = arm.AMOVHU - - goto rdst - - case gc.TUINT16<<16 | gc.TINT64, // convert via uint32 - gc.TUINT16<<16 | gc.TUINT64: - cvt = gc.Types[gc.TUINT32] - - goto hard - - case gc.TINT32<<16 | gc.TINT64, // sign extend int32 - gc.TINT32<<16 | gc.TUINT64: - var tlo gc.Node - var thi gc.Node - split64(t, &tlo, &thi) - - var r1 gc.Node - gc.Regalloc(&r1, tlo.Type, nil) - var r2 gc.Node - gc.Regalloc(&r2, thi.Type, nil) - gmove(f, &r1) - p1 := gins(arm.AMOVW, &r1, &r2) - p1.From.Type = obj.TYPE_SHIFT - p1.From.Offset = 2<<5 | 31<<7 | int64(r1.Reg)&15 // r1->31 - p1.From.Reg = 0 - - //print("gmove: %P\n", p1); - gins(arm.AMOVW, &r1, &tlo) - - gins(arm.AMOVW, &r2, &thi) - gc.Regfree(&r1) - gc.Regfree(&r2) - splitclean() - return - - case gc.TUINT32<<16 | gc.TINT64, // zero extend uint32 - gc.TUINT32<<16 | gc.TUINT64: - var thi gc.Node - var tlo gc.Node - split64(t, &tlo, &thi) - - gmove(f, &tlo) - var r1 gc.Node - gc.Regalloc(&r1, thi.Type, nil) - gins(arm.AMOVW, ncon(0), &r1) - gins(arm.AMOVW, &r1, &thi) - gc.Regfree(&r1) - splitclean() - return - - // case CASE(TFLOAT64, TUINT64): - /* - * float to integer - */ - case gc.TFLOAT32<<16 | gc.TINT8, - gc.TFLOAT32<<16 | gc.TUINT8, - gc.TFLOAT32<<16 | gc.TINT16, - gc.TFLOAT32<<16 | gc.TUINT16, - gc.TFLOAT32<<16 | gc.TINT32, - gc.TFLOAT32<<16 | gc.TUINT32, - - // case CASE(TFLOAT32, TUINT64): - - gc.TFLOAT64<<16 | gc.TINT8, - gc.TFLOAT64<<16 | gc.TUINT8, - gc.TFLOAT64<<16 | gc.TINT16, - gc.TFLOAT64<<16 | gc.TUINT16, - gc.TFLOAT64<<16 | gc.TINT32, - gc.TFLOAT64<<16 | gc.TUINT32: - fa := arm.AMOVF - - a := arm.AMOVFW - if ft == gc.TFLOAT64 { - fa = arm.AMOVD - a = arm.AMOVDW - } - - ta := arm.AMOVW - switch tt { - case gc.TINT8: - ta = arm.AMOVBS - - case gc.TUINT8: - ta = arm.AMOVBU - - case gc.TINT16: - ta = arm.AMOVHS - - case gc.TUINT16: - ta = arm.AMOVHU - } - - var r1 gc.Node - gc.Regalloc(&r1, gc.Types[ft], f) - var r2 gc.Node - gc.Regalloc(&r2, gc.Types[tt], t) - gins(fa, f, &r1) // load to fpu - p1 := gins(a, &r1, &r1) // convert to w - switch tt { - case gc.TUINT8, - gc.TUINT16, - gc.TUINT32: - p1.Scond |= arm.C_UBIT - } - - gins(arm.AMOVW, &r1, &r2) // copy to cpu - gins(ta, &r2, t) // store - gc.Regfree(&r1) - gc.Regfree(&r2) - return - - /* - * integer to float - */ - case gc.TINT8<<16 | gc.TFLOAT32, - gc.TUINT8<<16 | gc.TFLOAT32, - gc.TINT16<<16 | gc.TFLOAT32, - gc.TUINT16<<16 | gc.TFLOAT32, - gc.TINT32<<16 | gc.TFLOAT32, - gc.TUINT32<<16 | gc.TFLOAT32, - gc.TINT8<<16 | gc.TFLOAT64, - gc.TUINT8<<16 | gc.TFLOAT64, - gc.TINT16<<16 | gc.TFLOAT64, - gc.TUINT16<<16 | gc.TFLOAT64, - gc.TINT32<<16 | gc.TFLOAT64, - gc.TUINT32<<16 | gc.TFLOAT64: - fa := arm.AMOVW - - switch ft { - case gc.TINT8: - fa = arm.AMOVBS - - case gc.TUINT8: - fa = arm.AMOVBU - - case gc.TINT16: - fa = arm.AMOVHS - - case gc.TUINT16: - fa = arm.AMOVHU - } - - a := arm.AMOVWF - ta := arm.AMOVF - if tt == gc.TFLOAT64 { - a = arm.AMOVWD - ta = arm.AMOVD - } - - var r1 gc.Node - gc.Regalloc(&r1, gc.Types[ft], f) - var r2 gc.Node - gc.Regalloc(&r2, gc.Types[tt], t) - gins(fa, f, &r1) // load to cpu - gins(arm.AMOVW, &r1, &r2) // copy to fpu - p1 := gins(a, &r2, &r2) // convert - switch ft { - case gc.TUINT8, - gc.TUINT16, - gc.TUINT32: - p1.Scond |= arm.C_UBIT - } - - gins(ta, &r2, t) // store - gc.Regfree(&r1) - gc.Regfree(&r2) - return - - case gc.TUINT64<<16 | gc.TFLOAT32, - gc.TUINT64<<16 | gc.TFLOAT64: - gc.Fatal("gmove UINT64, TFLOAT not implemented") - return - - /* - * float to float - */ - case gc.TFLOAT32<<16 | gc.TFLOAT32: - a = arm.AMOVF - - case gc.TFLOAT64<<16 | gc.TFLOAT64: - a = arm.AMOVD - - case gc.TFLOAT32<<16 | gc.TFLOAT64: - var r1 gc.Node - gc.Regalloc(&r1, gc.Types[gc.TFLOAT64], t) - gins(arm.AMOVF, f, &r1) - gins(arm.AMOVFD, &r1, &r1) - gins(arm.AMOVD, &r1, t) - gc.Regfree(&r1) - return - - case gc.TFLOAT64<<16 | gc.TFLOAT32: - var r1 gc.Node - gc.Regalloc(&r1, gc.Types[gc.TFLOAT64], t) - gins(arm.AMOVD, f, &r1) - gins(arm.AMOVDF, &r1, &r1) - gins(arm.AMOVF, &r1, t) - gc.Regfree(&r1) - return - } - - gins(a, f, t) - return - - // TODO(kaib): we almost always require a register dest anyway, this can probably be - // removed. - // requires register destination -rdst: - { - gc.Regalloc(&r1, t.Type, t) - - gins(a, f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - } - - // requires register intermediate -hard: - gc.Regalloc(&r1, cvt, t) - - gmove(f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - - // truncate 64 bit integer -trunc64: - var fhi gc.Node - var flo gc.Node - split64(f, &flo, &fhi) - - gc.Regalloc(&r1, t.Type, nil) - gins(a, &flo, &r1) - gins(a, &r1, t) - gc.Regfree(&r1) - splitclean() - return -} - -func samaddr(f *gc.Node, t *gc.Node) bool { - if f.Op != t.Op { - return false - } - - switch f.Op { - case gc.OREGISTER: - if f.Reg != t.Reg { - break - } - return true - } - - return false -} - -/* - * generate one instruction: - * as f, t - */ -func gins(as int, f *gc.Node, t *gc.Node) *obj.Prog { - // Node nod; - // int32 v; - - if f != nil && f.Op == gc.OINDEX { - gc.Fatal("gins OINDEX not implemented") - } - - // gc.Regalloc(&nod, ®node, Z); - // v = constnode.vconst; - // gc.Cgen(f->right, &nod); - // constnode.vconst = v; - // idx.reg = nod.reg; - // gc.Regfree(&nod); - if t != nil && t.Op == gc.OINDEX { - gc.Fatal("gins OINDEX not implemented") - } - - // gc.Regalloc(&nod, ®node, Z); - // v = constnode.vconst; - // gc.Cgen(t->right, &nod); - // constnode.vconst = v; - // idx.reg = nod.reg; - // gc.Regfree(&nod); - - p := gc.Prog(as) - gc.Naddr(&p.From, f) - gc.Naddr(&p.To, t) - - switch as { - case arm.ABL: - if p.To.Type == obj.TYPE_REG { - p.To.Type = obj.TYPE_MEM - } - - case arm.ACMP, arm.ACMPF, arm.ACMPD: - if t != nil { - if f.Op != gc.OREGISTER { - /* generate a comparison - TODO(kaib): one of the args can actually be a small constant. relax the constraint and fix call sites. - */ - gc.Fatal("bad operands to gcmp") - } - p.From = p.To - p.To = obj.Addr{} - raddr(f, p) - } - - case arm.AMULU: - if f != nil && f.Op != gc.OREGISTER { - gc.Fatal("bad operands to mul") - } - - case arm.AMOVW: - if (p.From.Type == obj.TYPE_MEM || p.From.Type == obj.TYPE_ADDR || p.From.Type == obj.TYPE_CONST) && (p.To.Type == obj.TYPE_MEM || p.To.Type == obj.TYPE_ADDR) { - gc.Fatal("gins double memory") - } - - case arm.AADD: - if p.To.Type == obj.TYPE_MEM { - gc.Fatal("gins arith to mem") - } - - case arm.ARSB: - if p.From.Type == obj.TYPE_NONE { - gc.Fatal("rsb with no from") - } - } - - if gc.Debug['g'] != 0 { - fmt.Printf("%v\n", p) - } - return p -} - -/* - * insert n into reg slot of p - */ -func raddr(n *gc.Node, p *obj.Prog) { - var a obj.Addr - gc.Naddr(&a, n) - if a.Type != obj.TYPE_REG { - if n != nil { - gc.Fatal("bad in raddr: %v", gc.Oconv(int(n.Op), 0)) - } else { - gc.Fatal("bad in raddr: ") - } - p.Reg = 0 - } else { - p.Reg = a.Reg - } -} - -/* generate a constant shift - * arm encodes a shift by 32 as 0, thus asking for 0 shift is illegal. - */ -func gshift(as int, lhs *gc.Node, stype int32, sval int32, rhs *gc.Node) *obj.Prog { - if sval <= 0 || sval > 32 { - gc.Fatal("bad shift value: %d", sval) - } - - sval = sval & 0x1f - - p := gins(as, nil, rhs) - p.From.Type = obj.TYPE_SHIFT - p.From.Offset = int64(stype) | int64(sval)<<7 | int64(lhs.Reg)&15 - return p -} - -/* generate a register shift - */ -func gregshift(as int, lhs *gc.Node, stype int32, reg *gc.Node, rhs *gc.Node) *obj.Prog { - p := gins(as, nil, rhs) - p.From.Type = obj.TYPE_SHIFT - p.From.Offset = int64(stype) | (int64(reg.Reg)&15)<<8 | 1<<4 | int64(lhs.Reg)&15 - return p -} - -/* - * return Axxx for Oxxx on type t. - */ -func optoas(op int, t *gc.Type) int { - if t == nil { - gc.Fatal("optoas: t is nil") - } - - a := obj.AXXX - switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) { - default: - gc.Fatal("optoas: no entry %v-%v etype %v simtype %v", gc.Oconv(int(op), 0), t, gc.Types[t.Etype], gc.Types[gc.Simtype[t.Etype]]) - - /* case CASE(OADDR, TPTR32): - a = ALEAL; - break; - - case CASE(OADDR, TPTR64): - a = ALEAQ; - break; - */ - // TODO(kaib): make sure the conditional branches work on all edge cases - case gc.OEQ<<16 | gc.TBOOL, - gc.OEQ<<16 | gc.TINT8, - gc.OEQ<<16 | gc.TUINT8, - gc.OEQ<<16 | gc.TINT16, - gc.OEQ<<16 | gc.TUINT16, - gc.OEQ<<16 | gc.TINT32, - gc.OEQ<<16 | gc.TUINT32, - gc.OEQ<<16 | gc.TINT64, - gc.OEQ<<16 | gc.TUINT64, - gc.OEQ<<16 | gc.TPTR32, - gc.OEQ<<16 | gc.TPTR64, - gc.OEQ<<16 | gc.TFLOAT32, - gc.OEQ<<16 | gc.TFLOAT64: - a = arm.ABEQ - - case gc.ONE<<16 | gc.TBOOL, - gc.ONE<<16 | gc.TINT8, - gc.ONE<<16 | gc.TUINT8, - gc.ONE<<16 | gc.TINT16, - gc.ONE<<16 | gc.TUINT16, - gc.ONE<<16 | gc.TINT32, - gc.ONE<<16 | gc.TUINT32, - gc.ONE<<16 | gc.TINT64, - gc.ONE<<16 | gc.TUINT64, - gc.ONE<<16 | gc.TPTR32, - gc.ONE<<16 | gc.TPTR64, - gc.ONE<<16 | gc.TFLOAT32, - gc.ONE<<16 | gc.TFLOAT64: - a = arm.ABNE - - case gc.OLT<<16 | gc.TINT8, - gc.OLT<<16 | gc.TINT16, - gc.OLT<<16 | gc.TINT32, - gc.OLT<<16 | gc.TINT64, - gc.OLT<<16 | gc.TFLOAT32, - gc.OLT<<16 | gc.TFLOAT64: - a = arm.ABLT - - case gc.OLT<<16 | gc.TUINT8, - gc.OLT<<16 | gc.TUINT16, - gc.OLT<<16 | gc.TUINT32, - gc.OLT<<16 | gc.TUINT64: - a = arm.ABLO - - case gc.OLE<<16 | gc.TINT8, - gc.OLE<<16 | gc.TINT16, - gc.OLE<<16 | gc.TINT32, - gc.OLE<<16 | gc.TINT64, - gc.OLE<<16 | gc.TFLOAT32, - gc.OLE<<16 | gc.TFLOAT64: - a = arm.ABLE - - case gc.OLE<<16 | gc.TUINT8, - gc.OLE<<16 | gc.TUINT16, - gc.OLE<<16 | gc.TUINT32, - gc.OLE<<16 | gc.TUINT64: - a = arm.ABLS - - case gc.OGT<<16 | gc.TINT8, - gc.OGT<<16 | gc.TINT16, - gc.OGT<<16 | gc.TINT32, - gc.OGT<<16 | gc.TINT64, - gc.OGT<<16 | gc.TFLOAT32, - gc.OGT<<16 | gc.TFLOAT64: - a = arm.ABGT - - case gc.OGT<<16 | gc.TUINT8, - gc.OGT<<16 | gc.TUINT16, - gc.OGT<<16 | gc.TUINT32, - gc.OGT<<16 | gc.TUINT64: - a = arm.ABHI - - case gc.OGE<<16 | gc.TINT8, - gc.OGE<<16 | gc.TINT16, - gc.OGE<<16 | gc.TINT32, - gc.OGE<<16 | gc.TINT64, - gc.OGE<<16 | gc.TFLOAT32, - gc.OGE<<16 | gc.TFLOAT64: - a = arm.ABGE - - case gc.OGE<<16 | gc.TUINT8, - gc.OGE<<16 | gc.TUINT16, - gc.OGE<<16 | gc.TUINT32, - gc.OGE<<16 | gc.TUINT64: - a = arm.ABHS - - case gc.OCMP<<16 | gc.TBOOL, - gc.OCMP<<16 | gc.TINT8, - gc.OCMP<<16 | gc.TUINT8, - gc.OCMP<<16 | gc.TINT16, - gc.OCMP<<16 | gc.TUINT16, - gc.OCMP<<16 | gc.TINT32, - gc.OCMP<<16 | gc.TUINT32, - gc.OCMP<<16 | gc.TPTR32: - a = arm.ACMP - - case gc.OCMP<<16 | gc.TFLOAT32: - a = arm.ACMPF - - case gc.OCMP<<16 | gc.TFLOAT64: - a = arm.ACMPD - - case gc.OPS<<16 | gc.TFLOAT32, - gc.OPS<<16 | gc.TFLOAT64: - a = arm.ABVS - - case gc.OAS<<16 | gc.TBOOL: - a = arm.AMOVB - - case gc.OAS<<16 | gc.TINT8: - a = arm.AMOVBS - - case gc.OAS<<16 | gc.TUINT8: - a = arm.AMOVBU - - case gc.OAS<<16 | gc.TINT16: - a = arm.AMOVHS - - case gc.OAS<<16 | gc.TUINT16: - a = arm.AMOVHU - - case gc.OAS<<16 | gc.TINT32, - gc.OAS<<16 | gc.TUINT32, - gc.OAS<<16 | gc.TPTR32: - a = arm.AMOVW - - case gc.OAS<<16 | gc.TFLOAT32: - a = arm.AMOVF - - case gc.OAS<<16 | gc.TFLOAT64: - a = arm.AMOVD - - case gc.OADD<<16 | gc.TINT8, - gc.OADD<<16 | gc.TUINT8, - gc.OADD<<16 | gc.TINT16, - gc.OADD<<16 | gc.TUINT16, - gc.OADD<<16 | gc.TINT32, - gc.OADD<<16 | gc.TUINT32, - gc.OADD<<16 | gc.TPTR32: - a = arm.AADD - - case gc.OADD<<16 | gc.TFLOAT32: - a = arm.AADDF - - case gc.OADD<<16 | gc.TFLOAT64: - a = arm.AADDD - - case gc.OSUB<<16 | gc.TINT8, - gc.OSUB<<16 | gc.TUINT8, - gc.OSUB<<16 | gc.TINT16, - gc.OSUB<<16 | gc.TUINT16, - gc.OSUB<<16 | gc.TINT32, - gc.OSUB<<16 | gc.TUINT32, - gc.OSUB<<16 | gc.TPTR32: - a = arm.ASUB - - case gc.OSUB<<16 | gc.TFLOAT32: - a = arm.ASUBF - - case gc.OSUB<<16 | gc.TFLOAT64: - a = arm.ASUBD - - case gc.OMINUS<<16 | gc.TINT8, - gc.OMINUS<<16 | gc.TUINT8, - gc.OMINUS<<16 | gc.TINT16, - gc.OMINUS<<16 | gc.TUINT16, - gc.OMINUS<<16 | gc.TINT32, - gc.OMINUS<<16 | gc.TUINT32, - gc.OMINUS<<16 | gc.TPTR32: - a = arm.ARSB - - case gc.OAND<<16 | gc.TINT8, - gc.OAND<<16 | gc.TUINT8, - gc.OAND<<16 | gc.TINT16, - gc.OAND<<16 | gc.TUINT16, - gc.OAND<<16 | gc.TINT32, - gc.OAND<<16 | gc.TUINT32, - gc.OAND<<16 | gc.TPTR32: - a = arm.AAND - - case gc.OOR<<16 | gc.TINT8, - gc.OOR<<16 | gc.TUINT8, - gc.OOR<<16 | gc.TINT16, - gc.OOR<<16 | gc.TUINT16, - gc.OOR<<16 | gc.TINT32, - gc.OOR<<16 | gc.TUINT32, - gc.OOR<<16 | gc.TPTR32: - a = arm.AORR - - case gc.OXOR<<16 | gc.TINT8, - gc.OXOR<<16 | gc.TUINT8, - gc.OXOR<<16 | gc.TINT16, - gc.OXOR<<16 | gc.TUINT16, - gc.OXOR<<16 | gc.TINT32, - gc.OXOR<<16 | gc.TUINT32, - gc.OXOR<<16 | gc.TPTR32: - a = arm.AEOR - - case gc.OLSH<<16 | gc.TINT8, - gc.OLSH<<16 | gc.TUINT8, - gc.OLSH<<16 | gc.TINT16, - gc.OLSH<<16 | gc.TUINT16, - gc.OLSH<<16 | gc.TINT32, - gc.OLSH<<16 | gc.TUINT32, - gc.OLSH<<16 | gc.TPTR32: - a = arm.ASLL - - case gc.ORSH<<16 | gc.TUINT8, - gc.ORSH<<16 | gc.TUINT16, - gc.ORSH<<16 | gc.TUINT32, - gc.ORSH<<16 | gc.TPTR32: - a = arm.ASRL - - case gc.ORSH<<16 | gc.TINT8, - gc.ORSH<<16 | gc.TINT16, - gc.ORSH<<16 | gc.TINT32: - a = arm.ASRA - - case gc.OMUL<<16 | gc.TUINT8, - gc.OMUL<<16 | gc.TUINT16, - gc.OMUL<<16 | gc.TUINT32, - gc.OMUL<<16 | gc.TPTR32: - a = arm.AMULU - - case gc.OMUL<<16 | gc.TINT8, - gc.OMUL<<16 | gc.TINT16, - gc.OMUL<<16 | gc.TINT32: - a = arm.AMUL - - case gc.OMUL<<16 | gc.TFLOAT32: - a = arm.AMULF - - case gc.OMUL<<16 | gc.TFLOAT64: - a = arm.AMULD - - case gc.ODIV<<16 | gc.TUINT8, - gc.ODIV<<16 | gc.TUINT16, - gc.ODIV<<16 | gc.TUINT32, - gc.ODIV<<16 | gc.TPTR32: - a = arm.ADIVU - - case gc.ODIV<<16 | gc.TINT8, - gc.ODIV<<16 | gc.TINT16, - gc.ODIV<<16 | gc.TINT32: - a = arm.ADIV - - case gc.OMOD<<16 | gc.TUINT8, - gc.OMOD<<16 | gc.TUINT16, - gc.OMOD<<16 | gc.TUINT32, - gc.OMOD<<16 | gc.TPTR32: - a = arm.AMODU - - case gc.OMOD<<16 | gc.TINT8, - gc.OMOD<<16 | gc.TINT16, - gc.OMOD<<16 | gc.TINT32: - a = arm.AMOD - - // case CASE(OEXTEND, TINT16): - // a = ACWD; - // break; - - // case CASE(OEXTEND, TINT32): - // a = ACDQ; - // break; - - // case CASE(OEXTEND, TINT64): - // a = ACQO; - // break; - - case gc.ODIV<<16 | gc.TFLOAT32: - a = arm.ADIVF - - case gc.ODIV<<16 | gc.TFLOAT64: - a = arm.ADIVD - - case gc.OSQRT<<16 | gc.TFLOAT64: - a = arm.ASQRTD - } - - return a -} - -const ( - ODynam = 1 << 0 - OPtrto = 1 << 1 -) - -var clean [20]gc.Node - -var cleani int = 0 - -func sudoclean() { - if clean[cleani-1].Op != gc.OEMPTY { - gc.Regfree(&clean[cleani-1]) - } - if clean[cleani-2].Op != gc.OEMPTY { - gc.Regfree(&clean[cleani-2]) - } - cleani -= 2 -} - -func dotaddable(n *gc.Node, n1 *gc.Node) bool { - if n.Op != gc.ODOT { - return false - } - - var oary [10]int64 - var nn *gc.Node - o := gc.Dotoffset(n, oary[:], &nn) - if nn != nil && nn.Addable && o == 1 && oary[0] >= 0 { - *n1 = *nn - n1.Type = n.Type - n1.Xoffset += oary[0] - return true - } - - return false -} - -/* - * generate code to compute address of n, - * a reference to a (perhaps nested) field inside - * an array or struct. - * return 0 on failure, 1 on success. - * on success, leaves usable address in a. - * - * caller is responsible for calling sudoclean - * after successful sudoaddable, - * to release the register used for a. - */ -func sudoaddable(as int, n *gc.Node, a *obj.Addr) bool { - if n.Type == nil { - return false - } - - *a = obj.Addr{} - - switch n.Op { - case gc.OLITERAL: - if !gc.Isconst(n, gc.CTINT) { - break - } - v := n.Int() - if v >= 32000 || v <= -32000 { - break - } - switch as { - default: - return false - - case arm.AADD, - arm.ASUB, - arm.AAND, - arm.AORR, - arm.AEOR, - arm.AMOVB, - arm.AMOVBS, - arm.AMOVBU, - arm.AMOVH, - arm.AMOVHS, - arm.AMOVHU, - arm.AMOVW: - break - } - - cleani += 2 - reg := &clean[cleani-1] - reg1 := &clean[cleani-2] - reg.Op = gc.OEMPTY - reg1.Op = gc.OEMPTY - gc.Naddr(a, n) - return true - - case gc.ODOT, - gc.ODOTPTR: - cleani += 2 - reg := &clean[cleani-1] - reg1 := &clean[cleani-2] - reg.Op = gc.OEMPTY - reg1.Op = gc.OEMPTY - var nn *gc.Node - var oary [10]int64 - o := gc.Dotoffset(n, oary[:], &nn) - if nn == nil { - sudoclean() - return false - } - - if nn.Addable && o == 1 && oary[0] >= 0 { - // directly addressable set of DOTs - n1 := *nn - - n1.Type = n.Type - n1.Xoffset += oary[0] - gc.Naddr(a, &n1) - return true - } - - gc.Regalloc(reg, gc.Types[gc.Tptr], nil) - n1 := *reg - n1.Op = gc.OINDREG - if oary[0] >= 0 { - gc.Agen(nn, reg) - n1.Xoffset = oary[0] - } else { - gc.Cgen(nn, reg) - gc.Cgen_checknil(reg) - n1.Xoffset = -(oary[0] + 1) - } - - for i := 1; i < o; i++ { - if oary[i] >= 0 { - gc.Fatal("can't happen") - } - gins(arm.AMOVW, &n1, reg) - gc.Cgen_checknil(reg) - n1.Xoffset = -(oary[i] + 1) - } - - a.Type = obj.TYPE_NONE - a.Name = obj.NAME_NONE - n1.Type = n.Type - gc.Naddr(a, &n1) - return true - - case gc.OINDEX: - return false - } - - return false -} diff --git a/src/cmd/5g/peep.go b/src/cmd/5g/peep.go deleted file mode 100644 index b76719d74e..0000000000 --- a/src/cmd/5g/peep.go +++ /dev/null @@ -1,1748 +0,0 @@ -// Inferno utils/5c/peep.c -// http://code.google.com/p/inferno-os/source/browse/utils/5c/peep.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package main - -import ( - "cmd/internal/gc" - "cmd/internal/obj" - "cmd/internal/obj/arm" - "fmt" -) - -var gactive uint32 - -// UNUSED -func peep(firstp *obj.Prog) { - g := (*gc.Graph)(gc.Flowstart(firstp, nil)) - if g == nil { - return - } - gactive = 0 - - var r *gc.Flow - var p *obj.Prog - var t int -loop1: - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - gc.Dumpit("loop1", g.Start, 0) - } - - t = 0 - for r = g.Start; r != nil; r = r.Link { - p = r.Prog - switch p.As { - /* - * elide shift into TYPE_SHIFT operand of subsequent instruction - */ - // if(shiftprop(r)) { - // excise(r); - // t++; - // break; - // } - case arm.ASLL, - arm.ASRL, - arm.ASRA: - break - - case arm.AMOVB, - arm.AMOVH, - arm.AMOVW, - arm.AMOVF, - arm.AMOVD: - if regtyp(&p.From) { - if p.From.Type == p.To.Type && isfloatreg(&p.From) == isfloatreg(&p.To) { - if p.Scond == arm.C_SCOND_NONE { - if copyprop(g, r) { - excise(r) - t++ - break - } - - if subprop(r) && copyprop(g, r) { - excise(r) - t++ - break - } - } - } - } - - case arm.AMOVHS, - arm.AMOVHU, - arm.AMOVBS, - arm.AMOVBU: - if p.From.Type == obj.TYPE_REG { - if shortprop(r) { - t++ - } - } - } - } - - /* - if(p->scond == C_SCOND_NONE) - if(regtyp(&p->to)) - if(isdconst(&p->from)) { - constprop(&p->from, &p->to, r->s1); - } - break; - */ - if t != 0 { - goto loop1 - } - - for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { - p = r.Prog - switch p.As { - /* - * EOR -1,x,y => MVN x,y - */ - case arm.AEOR: - if isdconst(&p.From) && p.From.Offset == -1 { - p.As = arm.AMVN - p.From.Type = obj.TYPE_REG - if p.Reg != 0 { - p.From.Reg = p.Reg - } else { - p.From.Reg = p.To.Reg - } - p.Reg = 0 - } - } - } - - for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { - p = r.Prog - switch p.As { - case arm.AMOVW, - arm.AMOVB, - arm.AMOVBS, - arm.AMOVBU: - if p.From.Type == obj.TYPE_MEM && p.From.Offset == 0 { - xtramodes(g, r, &p.From) - } else if p.To.Type == obj.TYPE_MEM && p.To.Offset == 0 { - xtramodes(g, r, &p.To) - } else { - continue - } - } - } - - // case ACMP: - // /* - // * elide CMP $0,x if calculation of x can set condition codes - // */ - // if(isdconst(&p->from) || p->from.offset != 0) - // continue; - // r2 = r->s1; - // if(r2 == nil) - // continue; - // t = r2->prog->as; - // switch(t) { - // default: - // continue; - // case ABEQ: - // case ABNE: - // case ABMI: - // case ABPL: - // break; - // case ABGE: - // t = ABPL; - // break; - // case ABLT: - // t = ABMI; - // break; - // case ABHI: - // t = ABNE; - // break; - // case ABLS: - // t = ABEQ; - // break; - // } - // r1 = r; - // do - // r1 = uniqp(r1); - // while (r1 != nil && r1->prog->as == ANOP); - // if(r1 == nil) - // continue; - // p1 = r1->prog; - // if(p1->to.type != TYPE_REG) - // continue; - // if(p1->to.reg != p->reg) - // if(!(p1->as == AMOVW && p1->from.type == TYPE_REG && p1->from.reg == p->reg)) - // continue; - // - // switch(p1->as) { - // default: - // continue; - // case AMOVW: - // if(p1->from.type != TYPE_REG) - // continue; - // case AAND: - // case AEOR: - // case AORR: - // case ABIC: - // case AMVN: - // case ASUB: - // case ARSB: - // case AADD: - // case AADC: - // case ASBC: - // case ARSC: - // break; - // } - // p1->scond |= C_SBIT; - // r2->prog->as = t; - // excise(r); - // continue; - - // predicate(g); - - gc.Flowend(g) -} - -func regtyp(a *obj.Addr) bool { - return a.Type == obj.TYPE_REG && (arm.REG_R0 <= a.Reg && a.Reg <= arm.REG_R15 || arm.REG_F0 <= a.Reg && a.Reg <= arm.REG_F15) -} - -/* - * the idea is to substitute - * one register for another - * from one MOV to another - * MOV a, R0 - * ADD b, R0 / no use of R1 - * MOV R0, R1 - * would be converted to - * MOV a, R1 - * ADD b, R1 - * MOV R1, R0 - * hopefully, then the former or latter MOV - * will be eliminated by copy propagation. - */ -func subprop(r0 *gc.Flow) bool { - p := (*obj.Prog)(r0.Prog) - v1 := (*obj.Addr)(&p.From) - if !regtyp(v1) { - return false - } - v2 := (*obj.Addr)(&p.To) - if !regtyp(v2) { - return false - } - for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { - if gc.Uniqs(r) == nil { - break - } - p = r.Prog - if p.As == obj.AVARDEF || p.As == obj.AVARKILL { - continue - } - if p.Info.Flags&gc.Call != 0 { - return false - } - - // TODO(rsc): Whatever invalidated the info should have done this call. - proginfo(p) - - if (p.Info.Flags&gc.CanRegRead != 0) && p.To.Type == obj.TYPE_REG { - p.Info.Flags |= gc.RegRead - p.Info.Flags &^= (gc.CanRegRead | gc.RightRead) - p.Reg = p.To.Reg - } - - switch p.As { - case arm.AMULLU, - arm.AMULA, - arm.AMVN: - return false - } - - if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightWrite { - if p.To.Type == v1.Type { - if p.To.Reg == v1.Reg { - if p.Scond == arm.C_SCOND_NONE { - copysub(&p.To, v1, v2, 1) - if gc.Debug['P'] != 0 { - fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog) - if p.From.Type == v2.Type { - fmt.Printf(" excise") - } - fmt.Printf("\n") - } - - for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { - p = r.Prog - copysub(&p.From, v1, v2, 1) - copysub1(p, v1, v2, 1) - copysub(&p.To, v1, v2, 1) - if gc.Debug['P'] != 0 { - fmt.Printf("%v\n", r.Prog) - } - } - - t := int(int(v1.Reg)) - v1.Reg = v2.Reg - v2.Reg = int16(t) - if gc.Debug['P'] != 0 { - fmt.Printf("%v last\n", r.Prog) - } - return true - } - } - } - } - - if copyau(&p.From, v2) || copyau1(p, v2) || copyau(&p.To, v2) { - break - } - if copysub(&p.From, v1, v2, 0) != 0 || copysub1(p, v1, v2, 0) != 0 || copysub(&p.To, v1, v2, 0) != 0 { - break - } - } - - return false -} - -/* - * The idea is to remove redundant copies. - * v1->v2 F=0 - * (use v2 s/v2/v1/)* - * set v1 F=1 - * use v2 return fail - * ----------------- - * v1->v2 F=0 - * (use v2 s/v2/v1/)* - * set v1 F=1 - * set v2 return success - */ -func copyprop(g *gc.Graph, r0 *gc.Flow) bool { - p := (*obj.Prog)(r0.Prog) - v1 := (*obj.Addr)(&p.From) - v2 := (*obj.Addr)(&p.To) - if copyas(v1, v2) { - return true - } - gactive++ - return copy1(v1, v2, r0.S1, 0) -} - -func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f int) bool { - if uint32(r.Active) == gactive { - if gc.Debug['P'] != 0 { - fmt.Printf("act set; return 1\n") - } - return true - } - - r.Active = int32(gactive) - if gc.Debug['P'] != 0 { - fmt.Printf("copy %v->%v f=%d\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), f) - } - var t int - var p *obj.Prog - for ; r != nil; r = r.S1 { - p = r.Prog - if gc.Debug['P'] != 0 { - fmt.Printf("%v", p) - } - if f == 0 && gc.Uniqp(r) == nil { - f = 1 - if gc.Debug['P'] != 0 { - fmt.Printf("; merge; f=%d", f) - } - } - - t = copyu(p, v2, nil) - switch t { - case 2: /* rar, can't split */ - if gc.Debug['P'] != 0 { - fmt.Printf("; %vrar; return 0\n", gc.Ctxt.Dconv(v2)) - } - return false - - case 3: /* set */ - if gc.Debug['P'] != 0 { - fmt.Printf("; %vset; return 1\n", gc.Ctxt.Dconv(v2)) - } - return true - - case 1, /* used, substitute */ - 4: /* use and set */ - if f != 0 { - if gc.Debug['P'] == 0 { - return false - } - if t == 4 { - fmt.Printf("; %vused+set and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f) - } else { - fmt.Printf("; %vused and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f) - } - return false - } - - if copyu(p, v2, v1) != 0 { - if gc.Debug['P'] != 0 { - fmt.Printf("; sub fail; return 0\n") - } - return false - } - - if gc.Debug['P'] != 0 { - fmt.Printf("; sub%v/%v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1)) - } - if t == 4 { - if gc.Debug['P'] != 0 { - fmt.Printf("; %vused+set; return 1\n", gc.Ctxt.Dconv(v2)) - } - return true - } - } - - if f == 0 { - t = copyu(p, v1, nil) - if f == 0 && (t == 2 || t == 3 || t == 4) { - f = 1 - if gc.Debug['P'] != 0 { - fmt.Printf("; %vset and !f; f=%d", gc.Ctxt.Dconv(v1), f) - } - } - } - - if gc.Debug['P'] != 0 { - fmt.Printf("\n") - } - if r.S2 != nil { - if !copy1(v1, v2, r.S2, f) { - return false - } - } - } - - return true -} - -// UNUSED -/* - * The idea is to remove redundant constants. - * $c1->v1 - * ($c1->v2 s/$c1/v1)* - * set v1 return - * The v1->v2 should be eliminated by copy propagation. - */ -func constprop(c1 *obj.Addr, v1 *obj.Addr, r *gc.Flow) { - if gc.Debug['P'] != 0 { - fmt.Printf("constprop %v->%v\n", gc.Ctxt.Dconv(c1), gc.Ctxt.Dconv(v1)) - } - var p *obj.Prog - for ; r != nil; r = r.S1 { - p = r.Prog - if gc.Debug['P'] != 0 { - fmt.Printf("%v", p) - } - if gc.Uniqp(r) == nil { - if gc.Debug['P'] != 0 { - fmt.Printf("; merge; return\n") - } - return - } - - if p.As == arm.AMOVW && copyas(&p.From, c1) { - if gc.Debug['P'] != 0 { - fmt.Printf("; sub%v/%v", gc.Ctxt.Dconv(&p.From), gc.Ctxt.Dconv(v1)) - } - p.From = *v1 - } else if copyu(p, v1, nil) > 1 { - if gc.Debug['P'] != 0 { - fmt.Printf("; %vset; return\n", gc.Ctxt.Dconv(v1)) - } - return - } - - if gc.Debug['P'] != 0 { - fmt.Printf("\n") - } - if r.S2 != nil { - constprop(c1, v1, r.S2) - } - } -} - -/* - * shortprop eliminates redundant zero/sign extensions. - * - * MOVBS x, R - * - * MOVBS R, R' - * - * changed to - * - * MOVBS x, R - * ... - * MOVB R, R' (compiled to mov) - * - * MOVBS above can be a MOVBS, MOVBU, MOVHS or MOVHU. - */ -func shortprop(r *gc.Flow) bool { - p := (*obj.Prog)(r.Prog) - r1 := (*gc.Flow)(findpre(r, &p.From)) - if r1 == nil { - return false - } - - p1 := (*obj.Prog)(r1.Prog) - if p1.As == p.As { - // Two consecutive extensions. - goto gotit - } - - if p1.As == arm.AMOVW && isdconst(&p1.From) && p1.From.Offset >= 0 && p1.From.Offset < 128 { - // Loaded an immediate. - goto gotit - } - - return false - -gotit: - if gc.Debug['P'] != 0 { - fmt.Printf("shortprop\n%v\n%v", p1, p) - } - switch p.As { - case arm.AMOVBS, - arm.AMOVBU: - p.As = arm.AMOVB - - case arm.AMOVHS, - arm.AMOVHU: - p.As = arm.AMOVH - } - - if gc.Debug['P'] != 0 { - fmt.Printf(" => %v\n", obj.Aconv(int(p.As))) - } - return true -} - -// UNUSED -/* - * ASLL x,y,w - * .. (not use w, not set x y w) - * AXXX w,a,b (a != w) - * .. (not use w) - * (set w) - * ----------- changed to - * .. - * AXXX (x< 1) || (a.Type == obj.TYPE_REG && copyu(p1, &a, nil) > 1) { - if gc.Debug['P'] != 0 { - fmt.Printf("\targs modified; FAILURE\n") - } - return false - } - - continue - case 3: /* set, not used */ - { - if gc.Debug['P'] != 0 { - fmt.Printf("\tBOTCH: noref; FAILURE\n") - } - return false - } - } - - break - } - - /* check whether substitution can be done */ - switch p1.As { - default: - if gc.Debug['P'] != 0 { - fmt.Printf("\tnon-dpi; FAILURE\n") - } - return false - - case arm.AAND, - arm.AEOR, - arm.AADD, - arm.AADC, - arm.AORR, - arm.ASUB, - arm.ASBC, - arm.ARSB, - arm.ARSC: - if int(p1.Reg) == n || (p1.Reg == 0 && p1.To.Type == obj.TYPE_REG && int(p1.To.Reg) == n) { - if p1.From.Type != obj.TYPE_REG { - if gc.Debug['P'] != 0 { - fmt.Printf("\tcan't swap; FAILURE\n") - } - return false - } - - p1.Reg = p1.From.Reg - p1.From.Reg = int16(n) - switch p1.As { - case arm.ASUB: - p1.As = arm.ARSB - - case arm.ARSB: - p1.As = arm.ASUB - - case arm.ASBC: - p1.As = arm.ARSC - - case arm.ARSC: - p1.As = arm.ASBC - } - - if gc.Debug['P'] != 0 { - fmt.Printf("\t=>%v", p1) - } - } - fallthrough - - case arm.ABIC, - arm.ATST, - arm.ACMP, - arm.ACMN: - if int(p1.Reg) == n { - if gc.Debug['P'] != 0 { - fmt.Printf("\tcan't swap; FAILURE\n") - } - return false - } - - if p1.Reg == 0 && int(p1.To.Reg) == n { - if gc.Debug['P'] != 0 { - fmt.Printf("\tshift result used twice; FAILURE\n") - } - return false - } - - // case AMVN: - if p1.From.Type == obj.TYPE_SHIFT { - if gc.Debug['P'] != 0 { - fmt.Printf("\tshift result used in shift; FAILURE\n") - } - return false - } - - if p1.From.Type != obj.TYPE_REG || int(p1.From.Reg) != n { - if gc.Debug['P'] != 0 { - fmt.Printf("\tBOTCH: where is it used?; FAILURE\n") - } - return false - } - } - - /* check whether shift result is used subsequently */ - p2 := (*obj.Prog)(p1) - - if int(p1.To.Reg) != n { - var p1 *obj.Prog - for { - r1 = gc.Uniqs(r1) - if r1 == nil { - if gc.Debug['P'] != 0 { - fmt.Printf("\tinconclusive; FAILURE\n") - } - return false - } - - p1 = r1.Prog - if gc.Debug['P'] != 0 { - fmt.Printf("\n%v", p1) - } - switch copyu(p1, &p.To, nil) { - case 0: /* not used or set */ - continue - - case 3: /* set, not used */ - break - - default: /* used */ - if gc.Debug['P'] != 0 { - fmt.Printf("\treused; FAILURE\n") - } - return false - } - - break - } - } - - /* make the substitution */ - p2.From.Reg = 0 - - o := int(int(p.Reg)) - if o == 0 { - o = int(p.To.Reg) - } - o &= 15 - - switch p.From.Type { - case obj.TYPE_CONST: - o |= int((p.From.Offset & 0x1f) << 7) - - case obj.TYPE_REG: - o |= 1<<4 | (int(p.From.Reg)&15)<<8 - } - - switch p.As { - case arm.ASLL: - o |= 0 << 5 - - case arm.ASRL: - o |= 1 << 5 - - case arm.ASRA: - o |= 2 << 5 - } - - p2.From = obj.Addr{} - p2.From.Type = obj.TYPE_SHIFT - p2.From.Offset = int64(o) - if gc.Debug['P'] != 0 { - fmt.Printf("\t=>%v\tSUCCEED\n", p2) - } - return true -} - -/* - * findpre returns the last instruction mentioning v - * before r. It must be a set, and there must be - * a unique path from that instruction to r. - */ -func findpre(r *gc.Flow, v *obj.Addr) *gc.Flow { - var r1 *gc.Flow - - for r1 = gc.Uniqp(r); r1 != nil; r, r1 = r1, gc.Uniqp(r1) { - if gc.Uniqs(r1) != r { - return nil - } - switch copyu(r1.Prog, v, nil) { - case 1, /* used */ - 2: /* read-alter-rewrite */ - return nil - - case 3, /* set */ - 4: /* set and used */ - return r1 - } - } - - return nil -} - -/* - * findinc finds ADD instructions with a constant - * argument which falls within the immed_12 range. - */ -func findinc(r *gc.Flow, r2 *gc.Flow, v *obj.Addr) *gc.Flow { - var r1 *gc.Flow - var p *obj.Prog - - for r1 = gc.Uniqs(r); r1 != nil && r1 != r2; r, r1 = r1, gc.Uniqs(r1) { - if gc.Uniqp(r1) != r { - return nil - } - switch copyu(r1.Prog, v, nil) { - case 0: /* not touched */ - continue - - case 4: /* set and used */ - p = r1.Prog - - if p.As == arm.AADD { - if isdconst(&p.From) { - if p.From.Offset > -4096 && p.From.Offset < 4096 { - return r1 - } - } - } - fallthrough - - default: - return nil - } - } - - return nil -} - -func nochange(r *gc.Flow, r2 *gc.Flow, p *obj.Prog) bool { - if r == r2 { - return true - } - n := int(0) - var a [3]obj.Addr - if p.Reg != 0 && p.Reg != p.To.Reg { - a[n].Type = obj.TYPE_REG - a[n].Reg = p.Reg - n++ - } - - switch p.From.Type { - case obj.TYPE_SHIFT: - a[n].Type = obj.TYPE_REG - a[n].Reg = int16(arm.REG_R0 + (p.From.Offset & 0xf)) - n++ - fallthrough - - case obj.TYPE_REG: - a[n].Type = obj.TYPE_REG - a[n].Reg = p.From.Reg - n++ - } - - if n == 0 { - return true - } - var i int - for ; r != nil && r != r2; r = gc.Uniqs(r) { - p = r.Prog - for i = 0; i < n; i++ { - if copyu(p, &a[i], nil) > 1 { - return false - } - } - } - - return true -} - -func findu1(r *gc.Flow, v *obj.Addr) bool { - for ; r != nil; r = r.S1 { - if r.Active != 0 { - return false - } - r.Active = 1 - switch copyu(r.Prog, v, nil) { - case 1, /* used */ - 2, /* read-alter-rewrite */ - 4: /* set and used */ - return true - - case 3: /* set */ - return false - } - - if r.S2 != nil { - if findu1(r.S2, v) { - return true - } - } - } - - return false -} - -func finduse(g *gc.Graph, r *gc.Flow, v *obj.Addr) bool { - for r1 := (*gc.Flow)(g.Start); r1 != nil; r1 = r1.Link { - r1.Active = 0 - } - return findu1(r, v) -} - -/* - * xtramodes enables the ARM post increment and - * shift offset addressing modes to transform - * MOVW 0(R3),R1 - * ADD $4,R3,R3 - * into - * MOVW.P 4(R3),R1 - * and - * ADD R0,R1 - * MOVBU 0(R1),R0 - * into - * MOVBU R0<<0(R1),R0 - */ -func xtramodes(g *gc.Graph, r *gc.Flow, a *obj.Addr) bool { - p := (*obj.Prog)(r.Prog) - v := obj.Addr(*a) - v.Type = obj.TYPE_REG - r1 := (*gc.Flow)(findpre(r, &v)) - if r1 != nil { - p1 := r1.Prog - if p1.To.Type == obj.TYPE_REG && p1.To.Reg == v.Reg { - switch p1.As { - case arm.AADD: - if p1.Scond&arm.C_SBIT != 0 { - // avoid altering ADD.S/ADC sequences. - break - } - - if p1.From.Type == obj.TYPE_REG || (p1.From.Type == obj.TYPE_SHIFT && p1.From.Offset&(1<<4) == 0 && ((p.As != arm.AMOVB && p.As != arm.AMOVBS) || (a == &p.From && p1.From.Offset&^0xf == 0))) || ((p1.From.Type == obj.TYPE_ADDR || p1.From.Type == obj.TYPE_CONST) && p1.From.Offset > -4096 && p1.From.Offset < 4096) { - if nochange(gc.Uniqs(r1), r, p1) { - if a != &p.From || v.Reg != p.To.Reg { - if finduse(g, r.S1, &v) { - if p1.Reg == 0 || p1.Reg == v.Reg { - /* pre-indexing */ - p.Scond |= arm.C_WBIT - } else { - return false - } - } - } - - switch p1.From.Type { - /* register offset */ - case obj.TYPE_REG: - if gc.Nacl { - return false - } - *a = obj.Addr{} - a.Type = obj.TYPE_SHIFT - a.Offset = int64(p1.From.Reg) & 15 - - /* scaled register offset */ - case obj.TYPE_SHIFT: - if gc.Nacl { - return false - } - *a = obj.Addr{} - a.Type = obj.TYPE_SHIFT - fallthrough - - /* immediate offset */ - case obj.TYPE_CONST, - obj.TYPE_ADDR: - a.Offset = p1.From.Offset - } - - if p1.Reg != 0 { - a.Reg = p1.Reg - } - excise(r1) - return true - } - } - - case arm.AMOVW: - if p1.From.Type == obj.TYPE_REG { - r2 := (*gc.Flow)(findinc(r1, r, &p1.From)) - if r2 != nil { - var r3 *gc.Flow - for r3 = gc.Uniqs(r2); r3.Prog.As == obj.ANOP; r3 = gc.Uniqs(r3) { - } - if r3 == r { - /* post-indexing */ - p1 := r2.Prog - - a.Reg = p1.To.Reg - a.Offset = p1.From.Offset - p.Scond |= arm.C_PBIT - if !finduse(g, r, &r1.Prog.To) { - excise(r1) - } - excise(r2) - return true - } - } - } - } - } - } - - if a != &p.From || a.Reg != p.To.Reg { - r1 := (*gc.Flow)(findinc(r, nil, &v)) - if r1 != nil { - /* post-indexing */ - p1 := r1.Prog - - a.Offset = p1.From.Offset - p.Scond |= arm.C_PBIT - excise(r1) - return true - } - } - - return false -} - -/* - * return - * 1 if v only used (and substitute), - * 2 if read-alter-rewrite - * 3 if set - * 4 if set and used - * 0 otherwise (not touched) - */ -func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int { - switch p.As { - default: - fmt.Printf("copyu: can't find %v\n", obj.Aconv(int(p.As))) - return 2 - - case arm.AMOVM: - if v.Type != obj.TYPE_REG { - return 0 - } - if p.From.Type == obj.TYPE_CONST { /* read reglist, read/rar */ - if s != nil { - if p.From.Offset&(1<type to v->name and enable. - //if(v->type == NAME_AUTO || v->type == NAME_PARAM) { - // if(v->offset == a->offset) - // return 1; - //} - return false -} - -/* - * either direct or indirect - */ -func copyau(a *obj.Addr, v *obj.Addr) bool { - if copyas(a, v) { - return true - } - if v.Type == obj.TYPE_REG { - if a.Type == obj.TYPE_ADDR && a.Reg != 0 { - if a.Reg == v.Reg { - return true - } - } else if a.Type == obj.TYPE_MEM { - if a.Reg == v.Reg { - return true - } - } else if a.Type == obj.TYPE_REGREG || a.Type == obj.TYPE_REGREG2 { - if a.Reg == v.Reg { - return true - } - if a.Offset == int64(v.Reg) { - return true - } - } else if a.Type == obj.TYPE_SHIFT { - if a.Offset&0xf == int64(v.Reg-arm.REG_R0) { - return true - } - if (a.Offset&(1<<4) != 0) && (a.Offset>>8)&0xf == int64(v.Reg-arm.REG_R0) { - return true - } - } - } - - return false -} - -/* - * compare v to the center - * register in p (p->reg) - */ -func copyau1(p *obj.Prog, v *obj.Addr) bool { - if v.Type == obj.TYPE_REG && v.Reg == 0 { - return false - } - return p.Reg == v.Reg -} - -/* - * substitute s for v in a - * return failure to substitute - */ -func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f int) int { - if f != 0 { - if copyau(a, v) { - if a.Type == obj.TYPE_SHIFT { - if a.Offset&0xf == int64(v.Reg-arm.REG_R0) { - a.Offset = a.Offset&^0xf | int64(s.Reg)&0xf - } - if (a.Offset&(1<<4) != 0) && (a.Offset>>8)&0xf == int64(v.Reg-arm.REG_R0) { - a.Offset = a.Offset&^(0xf<<8) | (int64(s.Reg)&0xf)<<8 - } - } else if a.Type == obj.TYPE_REGREG || a.Type == obj.TYPE_REGREG2 { - if a.Offset == int64(v.Reg) { - a.Offset = int64(s.Reg) - } - if a.Reg == v.Reg { - a.Reg = s.Reg - } - } else { - a.Reg = s.Reg - } - } - } - - return 0 -} - -func copysub1(p1 *obj.Prog, v *obj.Addr, s *obj.Addr, f int) int { - if f != 0 { - if copyau1(p1, v) { - p1.Reg = s.Reg - } - } - return 0 -} - -var predinfo = []struct { - opcode int - notopcode int - scond int - notscond int -}{ - {arm.ABEQ, arm.ABNE, 0x0, 0x1}, - {arm.ABNE, arm.ABEQ, 0x1, 0x0}, - {arm.ABCS, arm.ABCC, 0x2, 0x3}, - {arm.ABHS, arm.ABLO, 0x2, 0x3}, - {arm.ABCC, arm.ABCS, 0x3, 0x2}, - {arm.ABLO, arm.ABHS, 0x3, 0x2}, - {arm.ABMI, arm.ABPL, 0x4, 0x5}, - {arm.ABPL, arm.ABMI, 0x5, 0x4}, - {arm.ABVS, arm.ABVC, 0x6, 0x7}, - {arm.ABVC, arm.ABVS, 0x7, 0x6}, - {arm.ABHI, arm.ABLS, 0x8, 0x9}, - {arm.ABLS, arm.ABHI, 0x9, 0x8}, - {arm.ABGE, arm.ABLT, 0xA, 0xB}, - {arm.ABLT, arm.ABGE, 0xB, 0xA}, - {arm.ABGT, arm.ABLE, 0xC, 0xD}, - {arm.ABLE, arm.ABGT, 0xD, 0xC}, -} - -type Joininfo struct { - start *gc.Flow - last *gc.Flow - end *gc.Flow - len int -} - -const ( - Join = iota - Split - End - Branch - Setcond - Toolong -) - -const ( - Falsecond = iota - Truecond - Delbranch - Keepbranch -) - -func isbranch(p *obj.Prog) bool { - return (arm.ABEQ <= p.As) && (p.As <= arm.ABLE) -} - -func predicable(p *obj.Prog) bool { - switch p.As { - case obj.ANOP, - obj.AXXX, - obj.ADATA, - obj.AGLOBL, - obj.ATEXT, - arm.AWORD, - arm.ABCASE, - arm.ACASE: - return false - } - - if isbranch(p) { - return false - } - return true -} - -/* - * Depends on an analysis of the encodings performed by 5l. - * These seem to be all of the opcodes that lead to the "S" bit - * being set in the instruction encodings. - * - * C_SBIT may also have been set explicitly in p->scond. - */ -func modifiescpsr(p *obj.Prog) bool { - switch p.As { - case arm.AMULLU, - arm.AMULA, - arm.AMULU, - arm.ADIVU, - arm.ATEQ, - arm.ACMN, - arm.ATST, - arm.ACMP, - arm.AMUL, - arm.ADIV, - arm.AMOD, - arm.AMODU, - arm.ABL: - return true - } - - if p.Scond&arm.C_SBIT != 0 { - return true - } - return false -} - -/* - * Find the maximal chain of instructions starting with r which could - * be executed conditionally - */ -func joinsplit(r *gc.Flow, j *Joininfo) int { - j.start = r - j.last = r - j.len = 0 - for { - if r.P2 != nil && (r.P1 != nil || r.P2.P2link != nil) { - j.end = r - return Join - } - - if r.S1 != nil && r.S2 != nil { - j.end = r - return Split - } - - j.last = r - if r.Prog.As != obj.ANOP { - j.len++ - } - if r.S1 == nil && r.S2 == nil { - j.end = r.Link - return End - } - - if r.S2 != nil { - j.end = r.S2 - return Branch - } - - if modifiescpsr(r.Prog) { - j.end = r.S1 - return Setcond - } - - r = r.S1 - if j.len >= 4 { - break - } - } - - j.end = r - return Toolong -} - -func successor(r *gc.Flow) *gc.Flow { - if r.S1 != nil { - return r.S1 - } else { - return r.S2 - } -} - -func applypred(rstart *gc.Flow, j *Joininfo, cond int, branch int) { - if j.len == 0 { - return - } - var pred int - if cond == Truecond { - pred = predinfo[rstart.Prog.As-arm.ABEQ].scond - } else { - pred = predinfo[rstart.Prog.As-arm.ABEQ].notscond - } - - for r := (*gc.Flow)(j.start); ; r = successor(r) { - if r.Prog.As == arm.AB { - if r != j.last || branch == Delbranch { - excise(r) - } else { - if cond == Truecond { - r.Prog.As = int16(predinfo[rstart.Prog.As-arm.ABEQ].opcode) - } else { - r.Prog.As = int16(predinfo[rstart.Prog.As-arm.ABEQ].notopcode) - } - } - } else if predicable(r.Prog) { - r.Prog.Scond = uint8(int(r.Prog.Scond&^arm.C_SCOND) | pred) - } - if r.S1 != r.Link { - r.S1 = r.Link - r.Link.P1 = r - } - - if r == j.last { - break - } - } -} - -func predicate(g *gc.Graph) { - var t1 int - var t2 int - var j1 Joininfo - var j2 Joininfo - - for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { - if isbranch(r.Prog) { - t1 = joinsplit(r.S1, &j1) - t2 = joinsplit(r.S2, &j2) - if j1.last.Link != j2.start { - continue - } - if j1.end == j2.end { - if (t1 == Branch && (t2 == Join || t2 == Setcond)) || (t2 == Join && (t1 == Join || t1 == Setcond)) { - applypred(r, &j1, Falsecond, Delbranch) - applypred(r, &j2, Truecond, Delbranch) - excise(r) - continue - } - } - - if t1 == End || t1 == Branch { - applypred(r, &j1, Falsecond, Keepbranch) - excise(r) - continue - } - } - } -} - -func isdconst(a *obj.Addr) bool { - return a.Type == obj.TYPE_CONST -} - -func isfloatreg(a *obj.Addr) bool { - return arm.REG_F0 <= a.Reg && a.Reg <= arm.REG_F15 -} - -func stackaddr(a *obj.Addr) bool { - return regtyp(a) && a.Reg == arm.REGSP -} - -func smallindir(a *obj.Addr, reg *obj.Addr) bool { - return reg.Type == obj.TYPE_REG && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && 0 <= a.Offset && a.Offset < 4096 -} - -func excise(r *gc.Flow) { - p := (*obj.Prog)(r.Prog) - obj.Nopout(p) -} diff --git a/src/cmd/5g/prog.go b/src/cmd/5g/prog.go deleted file mode 100644 index c472cdf042..0000000000 --- a/src/cmd/5g/prog.go +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright 2013 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/gc" - "cmd/internal/obj" - "cmd/internal/obj/arm" -) - -const ( - RightRdwr = gc.RightRead | gc.RightWrite -) - -// This table gives the basic information about instruction -// generated by the compiler and processed in the optimizer. -// See opt.h for bit definitions. -// -// Instructions not generated need not be listed. -// As an exception to that rule, we typically write down all the -// size variants of an operation even if we just use a subset. -// -// The table is formatted for 8-space tabs. -var progtable = [arm.ALAST]obj.ProgInfo{ - obj.ATYPE: {gc.Pseudo | gc.Skip, 0, 0, 0}, - obj.ATEXT: {gc.Pseudo, 0, 0, 0}, - obj.AFUNCDATA: {gc.Pseudo, 0, 0, 0}, - obj.APCDATA: {gc.Pseudo, 0, 0, 0}, - obj.AUNDEF: {gc.Break, 0, 0, 0}, - obj.AUSEFIELD: {gc.OK, 0, 0, 0}, - obj.ACHECKNIL: {gc.LeftRead, 0, 0, 0}, - obj.AVARDEF: {gc.Pseudo | gc.RightWrite, 0, 0, 0}, - obj.AVARKILL: {gc.Pseudo | gc.RightWrite, 0, 0, 0}, - - // NOP is an internal no-op that also stands - // for USED and SET annotations, not the Intel opcode. - obj.ANOP: {gc.LeftRead | gc.RightWrite, 0, 0, 0}, - - // Integer. - arm.AADC: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm.AADD: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm.AAND: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm.ABIC: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm.ACMN: {gc.SizeL | gc.LeftRead | gc.RightRead, 0, 0, 0}, - arm.ACMP: {gc.SizeL | gc.LeftRead | gc.RightRead, 0, 0, 0}, - arm.ADIVU: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm.ADIV: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm.AEOR: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm.AMODU: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm.AMOD: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm.AMULALU: {gc.SizeL | gc.LeftRead | gc.RegRead | RightRdwr, 0, 0, 0}, - arm.AMULAL: {gc.SizeL | gc.LeftRead | gc.RegRead | RightRdwr, 0, 0, 0}, - arm.AMULA: {gc.SizeL | gc.LeftRead | gc.RegRead | RightRdwr, 0, 0, 0}, - arm.AMULU: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm.AMUL: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm.AMULL: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm.AMULLU: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm.AMVN: {gc.SizeL | gc.LeftRead | gc.RightWrite, 0, 0, 0}, - arm.AORR: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm.ARSB: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm.ARSC: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm.ASBC: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm.ASLL: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm.ASRA: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm.ASRL: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm.ASUB: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm.ATEQ: {gc.SizeL | gc.LeftRead | gc.RightRead, 0, 0, 0}, - arm.ATST: {gc.SizeL | gc.LeftRead | gc.RightRead, 0, 0, 0}, - - // Floating point. - arm.AADDD: {gc.SizeD | gc.LeftRead | RightRdwr, 0, 0, 0}, - arm.AADDF: {gc.SizeF | gc.LeftRead | RightRdwr, 0, 0, 0}, - arm.ACMPD: {gc.SizeD | gc.LeftRead | gc.RightRead, 0, 0, 0}, - arm.ACMPF: {gc.SizeF | gc.LeftRead | gc.RightRead, 0, 0, 0}, - arm.ADIVD: {gc.SizeD | gc.LeftRead | RightRdwr, 0, 0, 0}, - arm.ADIVF: {gc.SizeF | gc.LeftRead | RightRdwr, 0, 0, 0}, - arm.AMULD: {gc.SizeD | gc.LeftRead | RightRdwr, 0, 0, 0}, - arm.AMULF: {gc.SizeF | gc.LeftRead | RightRdwr, 0, 0, 0}, - arm.ASUBD: {gc.SizeD | gc.LeftRead | RightRdwr, 0, 0, 0}, - arm.ASUBF: {gc.SizeF | gc.LeftRead | RightRdwr, 0, 0, 0}, - arm.ASQRTD: {gc.SizeD | gc.LeftRead | RightRdwr, 0, 0, 0}, - - // Conversions. - arm.AMOVWD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - arm.AMOVWF: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - arm.AMOVDF: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - arm.AMOVDW: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - arm.AMOVFD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - arm.AMOVFW: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - - // Moves. - arm.AMOVB: {gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, - arm.AMOVD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, - arm.AMOVF: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, - arm.AMOVH: {gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, - arm.AMOVW: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, - - // In addtion, duffzero reads R0,R1 and writes R1. This fact is - // encoded in peep.c - obj.ADUFFZERO: {gc.Call, 0, 0, 0}, - - // In addtion, duffcopy reads R1,R2 and writes R0,R1,R2. This fact is - // encoded in peep.c - obj.ADUFFCOPY: {gc.Call, 0, 0, 0}, - - // These should be split into the two different conversions instead - // of overloading the one. - arm.AMOVBS: {gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - arm.AMOVBU: {gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - arm.AMOVHS: {gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - arm.AMOVHU: {gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - - // Jumps. - arm.AB: {gc.Jump | gc.Break, 0, 0, 0}, - arm.ABL: {gc.Call, 0, 0, 0}, - arm.ABEQ: {gc.Cjmp, 0, 0, 0}, - arm.ABNE: {gc.Cjmp, 0, 0, 0}, - arm.ABCS: {gc.Cjmp, 0, 0, 0}, - arm.ABHS: {gc.Cjmp, 0, 0, 0}, - arm.ABCC: {gc.Cjmp, 0, 0, 0}, - arm.ABLO: {gc.Cjmp, 0, 0, 0}, - arm.ABMI: {gc.Cjmp, 0, 0, 0}, - arm.ABPL: {gc.Cjmp, 0, 0, 0}, - arm.ABVS: {gc.Cjmp, 0, 0, 0}, - arm.ABVC: {gc.Cjmp, 0, 0, 0}, - arm.ABHI: {gc.Cjmp, 0, 0, 0}, - arm.ABLS: {gc.Cjmp, 0, 0, 0}, - arm.ABGE: {gc.Cjmp, 0, 0, 0}, - arm.ABLT: {gc.Cjmp, 0, 0, 0}, - arm.ABGT: {gc.Cjmp, 0, 0, 0}, - arm.ABLE: {gc.Cjmp, 0, 0, 0}, - obj.ARET: {gc.Break, 0, 0, 0}, -} - -func proginfo(p *obj.Prog) { - info := &p.Info - *info = progtable[p.As] - if info.Flags == 0 { - gc.Fatal("unknown instruction %v", p) - } - - if p.From.Type == obj.TYPE_ADDR && p.From.Sym != nil && (info.Flags&gc.LeftRead != 0) { - info.Flags &^= gc.LeftRead - info.Flags |= gc.LeftAddr - } - - if (info.Flags&gc.RegRead != 0) && p.Reg == 0 { - info.Flags &^= gc.RegRead - info.Flags |= gc.CanRegRead | gc.RightRead - } - - if (p.Scond&arm.C_SCOND != arm.C_SCOND_NONE) && (info.Flags&gc.RightWrite != 0) { - info.Flags |= gc.RightRead - } - - switch p.As { - case arm.ADIV, - arm.ADIVU, - arm.AMOD, - arm.AMODU: - info.Regset |= RtoB(arm.REG_R12) - } -} diff --git a/src/cmd/5g/reg.go b/src/cmd/5g/reg.go deleted file mode 100644 index 2afdf12416..0000000000 --- a/src/cmd/5g/reg.go +++ /dev/null @@ -1,136 +0,0 @@ -// Inferno utils/5c/reg.c -// http://code.google.com/p/inferno-os/source/browse/utils/5c/reg.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package main - -import "cmd/internal/obj/arm" -import "cmd/internal/gc" - -const ( - NREGVAR = 32 -) - -var regname = []string{ - ".R0", - ".R1", - ".R2", - ".R3", - ".R4", - ".R5", - ".R6", - ".R7", - ".R8", - ".R9", - ".R10", - ".R11", - ".R12", - ".R13", - ".R14", - ".R15", - ".F0", - ".F1", - ".F2", - ".F3", - ".F4", - ".F5", - ".F6", - ".F7", - ".F8", - ".F9", - ".F10", - ".F11", - ".F12", - ".F13", - ".F14", - ".F15", -} - -func regnames(n *int) []string { - *n = NREGVAR - return regname -} - -func excludedregs() uint64 { - return RtoB(arm.REGSP) | RtoB(arm.REGLINK) | RtoB(arm.REGPC) -} - -func doregbits(r int) uint64 { - return 0 -} - -/* - * bit reg - * 0 R0 - * 1 R1 - * ... ... - * 10 R10 - * 12 R12 - * - * bit reg - * 18 F2 - * 19 F3 - * ... ... - * 31 F15 - */ -func RtoB(r int) uint64 { - if arm.REG_R0 <= r && r <= arm.REG_R15 { - if r >= arm.REGTMP-2 && r != arm.REG_R12 { // excluded R9 and R10 for m and g, but not R12 - return 0 - } - return 1 << uint(r-arm.REG_R0) - } - - if arm.REG_F0 <= r && r <= arm.REG_F15 { - if r < arm.REG_F2 || r > arm.REG_F0+arm.NFREG-1 { - return 0 - } - return 1 << uint((r-arm.REG_F0)+16) - } - - return 0 -} - -func BtoR(b uint64) int { - // TODO Allow R0 and R1, but be careful with a 0 return - // TODO Allow R9. Only R10 is reserved now (just g, not m). - b &= 0x11fc // excluded R9 and R10 for m and g, but not R12 - if b == 0 { - return 0 - } - return gc.Bitno(b) + arm.REG_R0 -} - -func BtoF(b uint64) int { - b &= 0xfffc0000 - if b == 0 { - return 0 - } - return gc.Bitno(b) - 16 + arm.REG_F0 -} diff --git a/src/cmd/5l/asm.go b/src/cmd/5l/asm.go deleted file mode 100644 index 70d6790fc1..0000000000 --- a/src/cmd/5l/asm.go +++ /dev/null @@ -1,651 +0,0 @@ -// Inferno utils/5l/asm.c -// http://code.google.com/p/inferno-os/source/browse/utils/5l/asm.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package main - -import ( - "cmd/internal/ld" - "cmd/internal/obj" - "fmt" - "log" -) - -func gentext() { -} - -// Preserve highest 8 bits of a, and do addition to lower 24-bit -// of a and b; used to adjust ARM branch intruction's target -func braddoff(a int32, b int32) int32 { - return int32((uint32(a))&0xff000000 | 0x00ffffff&uint32(a+b)) -} - -func adddynrela(rel *ld.LSym, s *ld.LSym, r *ld.Reloc) { - ld.Addaddrplus(ld.Ctxt, rel, s, int64(r.Off)) - ld.Adduint32(ld.Ctxt, rel, ld.R_ARM_RELATIVE) -} - -func adddynrel(s *ld.LSym, r *ld.Reloc) { - targ := r.Sym - ld.Ctxt.Cursym = s - - switch r.Type { - default: - if r.Type >= 256 { - ld.Diag("unexpected relocation type %d", r.Type) - return - } - - // Handle relocations found in ELF object files. - case 256 + ld.R_ARM_PLT32: - r.Type = obj.R_CALLARM - - if targ.Type == obj.SDYNIMPORT { - addpltsym(ld.Ctxt, targ) - r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) - r.Add = int64(braddoff(int32(r.Add), targ.Plt/4)) - } - - return - - case 256 + ld.R_ARM_THM_PC22: // R_ARM_THM_CALL - ld.Exitf("R_ARM_THM_CALL, are you using -marm?") - return - - case 256 + ld.R_ARM_GOT32: // R_ARM_GOT_BREL - if targ.Type != obj.SDYNIMPORT { - addgotsyminternal(ld.Ctxt, targ) - } else { - addgotsym(ld.Ctxt, targ) - } - - r.Type = obj.R_CONST // write r->add during relocsym - r.Sym = nil - r.Add += int64(targ.Got) - return - - case 256 + ld.R_ARM_GOT_PREL: // GOT(nil) + A - nil - if targ.Type != obj.SDYNIMPORT { - addgotsyminternal(ld.Ctxt, targ) - } else { - addgotsym(ld.Ctxt, targ) - } - - r.Type = obj.R_PCREL - r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0) - r.Add += int64(targ.Got) + 4 - return - - case 256 + ld.R_ARM_GOTOFF: // R_ARM_GOTOFF32 - r.Type = obj.R_GOTOFF - - return - - case 256 + ld.R_ARM_GOTPC: // R_ARM_BASE_PREL - r.Type = obj.R_PCREL - - r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0) - r.Add += 4 - return - - case 256 + ld.R_ARM_CALL: - r.Type = obj.R_CALLARM - if targ.Type == obj.SDYNIMPORT { - addpltsym(ld.Ctxt, targ) - r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) - r.Add = int64(braddoff(int32(r.Add), targ.Plt/4)) - } - - return - - case 256 + ld.R_ARM_REL32: // R_ARM_REL32 - r.Type = obj.R_PCREL - - r.Add += 4 - return - - case 256 + ld.R_ARM_ABS32: - if targ.Type == obj.SDYNIMPORT { - ld.Diag("unexpected R_ARM_ABS32 relocation for dynamic symbol %s", targ.Name) - } - r.Type = obj.R_ADDR - return - - // we can just ignore this, because we are targeting ARM V5+ anyway - case 256 + ld.R_ARM_V4BX: - if r.Sym != nil { - // R_ARM_V4BX is ABS relocation, so this symbol is a dummy symbol, ignore it - r.Sym.Type = 0 - } - - r.Sym = nil - return - - case 256 + ld.R_ARM_PC24, - 256 + ld.R_ARM_JUMP24: - r.Type = obj.R_CALLARM - if targ.Type == obj.SDYNIMPORT { - addpltsym(ld.Ctxt, targ) - r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) - r.Add = int64(braddoff(int32(r.Add), targ.Plt/4)) - } - - return - } - - // Handle references to ELF symbols from our own object files. - if targ.Type != obj.SDYNIMPORT { - return - } - - switch r.Type { - case obj.R_CALLARM: - addpltsym(ld.Ctxt, targ) - r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) - r.Add = int64(targ.Plt) - return - - case obj.R_ADDR: - if s.Type != obj.SDATA { - break - } - if ld.Iself { - 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 - r.Type = obj.R_CONST // write r->add during relocsym - r.Sym = nil - return - } - } - - ld.Ctxt.Cursym = s - ld.Diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ.Name, r.Type, targ.Type) -} - -func elfreloc1(r *ld.Reloc, sectoff int64) int { - ld.Thearch.Lput(uint32(sectoff)) - - elfsym := r.Xsym.Elfsym - switch r.Type { - default: - return -1 - - case obj.R_ADDR: - if r.Siz == 4 { - ld.Thearch.Lput(ld.R_ARM_ABS32 | uint32(elfsym)<<8) - } else { - return -1 - } - - case obj.R_PCREL: - if r.Siz == 4 { - ld.Thearch.Lput(ld.R_ARM_REL32 | uint32(elfsym)<<8) - } else { - return -1 - } - - case obj.R_CALLARM: - if r.Siz == 4 { - if r.Add&0xff000000 == 0xeb000000 { // BL - ld.Thearch.Lput(ld.R_ARM_CALL | uint32(elfsym)<<8) - } else { - ld.Thearch.Lput(ld.R_ARM_JUMP24 | uint32(elfsym)<<8) - } - } else { - return -1 - } - - case obj.R_TLS: - if r.Siz == 4 { - if ld.Buildmode == ld.BuildmodeCShared { - ld.Thearch.Lput(ld.R_ARM_TLS_IE32 | uint32(elfsym)<<8) - } else { - ld.Thearch.Lput(ld.R_ARM_TLS_LE32 | uint32(elfsym)<<8) - } - } else { - return -1 - } - } - - return 0 -} - -func elfsetupplt() { - plt := ld.Linklookup(ld.Ctxt, ".plt", 0) - got := ld.Linklookup(ld.Ctxt, ".got.plt", 0) - if plt.Size == 0 { - // str lr, [sp, #-4]! - ld.Adduint32(ld.Ctxt, plt, 0xe52de004) - - // ldr lr, [pc, #4] - ld.Adduint32(ld.Ctxt, plt, 0xe59fe004) - - // add lr, pc, lr - ld.Adduint32(ld.Ctxt, plt, 0xe08fe00e) - - // ldr pc, [lr, #8]! - ld.Adduint32(ld.Ctxt, plt, 0xe5bef008) - - // .word &GLOBAL_OFFSET_TABLE[0] - . - ld.Addpcrelplus(ld.Ctxt, plt, got, 4) - - // the first .plt entry requires 3 .plt.got entries - ld.Adduint32(ld.Ctxt, got, 0) - - ld.Adduint32(ld.Ctxt, got, 0) - ld.Adduint32(ld.Ctxt, got, 0) - } -} - -func machoreloc1(r *ld.Reloc, sectoff int64) int { - var v uint32 - - rs := r.Xsym - - if rs.Type == obj.SHOSTOBJ || r.Type == obj.R_CALLARM { - if rs.Dynid < 0 { - ld.Diag("reloc %d to non-macho symbol %s type=%d", r.Type, rs.Name, rs.Type) - return -1 - } - - v = uint32(rs.Dynid) - v |= 1 << 27 // external relocation - } else { - v = uint32((rs.Sect.(*ld.Section)).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) - return -1 - } - } - - switch r.Type { - default: - return -1 - - case obj.R_ADDR: - v |= ld.MACHO_GENERIC_RELOC_VANILLA << 28 - - case obj.R_CALLARM: - v |= 1 << 24 // pc-relative bit - v |= ld.MACHO_ARM_RELOC_BR24 << 28 - } - - switch r.Siz { - default: - return -1 - - case 1: - v |= 0 << 25 - - case 2: - v |= 1 << 25 - - case 4: - v |= 2 << 25 - - case 8: - v |= 3 << 25 - } - - ld.Thearch.Lput(uint32(sectoff)) - ld.Thearch.Lput(v) - return 0 -} - -func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int { - if ld.Linkmode == ld.LinkExternal { - switch r.Type { - case obj.R_CALLARM: - r.Done = 0 - - // set up addend for eventual relocation via outer symbol. - rs := r.Sym - - r.Xadd = r.Add - if r.Xadd&0x800000 != 0 { - r.Xadd |= ^0xffffff - } - r.Xadd *= 4 - for rs.Outer != nil { - r.Xadd += ld.Symaddr(rs) - ld.Symaddr(rs.Outer) - rs = rs.Outer - } - - if rs.Type != obj.SHOSTOBJ && rs.Sect == nil { - ld.Diag("missing section for %s", rs.Name) - } - r.Xsym = rs - - // ld64 for arm seems to want the symbol table to contain offset - // into the section rather than pseudo virtual address that contains - // the section load address. - // we need to compensate that by removing the instruction's address - // from addend. - if ld.HEADTYPE == obj.Hdarwin { - r.Xadd -= ld.Symaddr(s) + int64(r.Off) - } - - *val = int64(braddoff(int32(0xff000000&uint32(r.Add)), int32(0xffffff&uint32(r.Xadd/4)))) - return 0 - } - - return -1 - } - - switch r.Type { - case obj.R_CONST: - *val = r.Add - return 0 - - case obj.R_GOTOFF: - *val = ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ld.Linklookup(ld.Ctxt, ".got", 0)) - return 0 - - // The following three arch specific relocations are only for generation of - // Linux/ARM ELF's PLT entry (3 assembler instruction) - case obj.R_PLT0: // add ip, pc, #0xXX00000 - if ld.Symaddr(ld.Linklookup(ld.Ctxt, ".got.plt", 0)) < ld.Symaddr(ld.Linklookup(ld.Ctxt, ".plt", 0)) { - ld.Diag(".got.plt should be placed after .plt section.") - } - *val = 0xe28fc600 + (0xff & (int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(ld.Linklookup(ld.Ctxt, ".plt", 0))+int64(r.Off))+r.Add)) >> 20)) - return 0 - - case obj.R_PLT1: // add ip, ip, #0xYY000 - *val = 0xe28cca00 + (0xff & (int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(ld.Linklookup(ld.Ctxt, ".plt", 0))+int64(r.Off))+r.Add+4)) >> 12)) - - return 0 - - case obj.R_PLT2: // ldr pc, [ip, #0xZZZ]! - *val = 0xe5bcf000 + (0xfff & int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(ld.Linklookup(ld.Ctxt, ".plt", 0))+int64(r.Off))+r.Add+8))) - - return 0 - - case obj.R_CALLARM: // bl XXXXXX or b YYYYYY - *val = int64(braddoff(int32(0xff000000&uint32(r.Add)), int32(0xffffff&uint32((ld.Symaddr(r.Sym)+int64((uint32(r.Add))*4)-(s.Value+int64(r.Off)))/4)))) - - return 0 - } - - return -1 -} - -func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 { - log.Fatalf("unexpected relocation variant") - return t -} - -func addpltreloc(ctxt *ld.Link, plt *ld.LSym, got *ld.LSym, sym *ld.LSym, typ int) *ld.Reloc { - r := ld.Addrel(plt) - r.Sym = got - r.Off = int32(plt.Size) - r.Siz = 4 - r.Type = int32(typ) - r.Add = int64(sym.Got) - 8 - - plt.Reachable = true - plt.Size += 4 - ld.Symgrow(ctxt, plt, plt.Size) - - return r -} - -func addpltsym(ctxt *ld.Link, s *ld.LSym) { - if s.Plt >= 0 { - return - } - - ld.Adddynsym(ctxt, s) - - if ld.Iself { - plt := ld.Linklookup(ctxt, ".plt", 0) - got := ld.Linklookup(ctxt, ".got.plt", 0) - rel := ld.Linklookup(ctxt, ".rel.plt", 0) - if plt.Size == 0 { - elfsetupplt() - } - - // .got entry - s.Got = int32(got.Size) - - // In theory, all GOT should point to the first PLT entry, - // Linux/ARM's dynamic linker will do that for us, but FreeBSD/ARM's - // dynamic linker won't, so we'd better do it ourselves. - ld.Addaddrplus(ctxt, got, plt, 0) - - // .plt entry, this depends on the .got entry - s.Plt = int32(plt.Size) - - addpltreloc(ctxt, plt, got, s, obj.R_PLT0) // add lr, pc, #0xXX00000 - addpltreloc(ctxt, plt, got, s, obj.R_PLT1) // add lr, lr, #0xYY000 - addpltreloc(ctxt, plt, got, s, obj.R_PLT2) // ldr pc, [lr, #0xZZZ]! - - // rel - ld.Addaddrplus(ctxt, rel, got, int64(s.Got)) - - ld.Adduint32(ctxt, rel, ld.ELF32_R_INFO(uint32(s.Dynid), ld.R_ARM_JUMP_SLOT)) - } else { - ld.Diag("addpltsym: unsupported binary format") - } -} - -func addgotsyminternal(ctxt *ld.Link, s *ld.LSym) { - if s.Got >= 0 { - return - } - - got := ld.Linklookup(ctxt, ".got", 0) - s.Got = int32(got.Size) - - ld.Addaddrplus(ctxt, got, s, 0) - - if ld.Iself { - } else { - ld.Diag("addgotsyminternal: unsupported binary format") - } -} - -func addgotsym(ctxt *ld.Link, s *ld.LSym) { - if s.Got >= 0 { - return - } - - ld.Adddynsym(ctxt, s) - got := ld.Linklookup(ctxt, ".got", 0) - s.Got = int32(got.Size) - ld.Adduint32(ctxt, got, 0) - - if ld.Iself { - rel := ld.Linklookup(ctxt, ".rel", 0) - ld.Addaddrplus(ctxt, rel, got, int64(s.Got)) - ld.Adduint32(ctxt, rel, ld.ELF32_R_INFO(uint32(s.Dynid), ld.R_ARM_GLOB_DAT)) - } else { - ld.Diag("addgotsym: unsupported binary format") - } -} - -func asmb() { - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime()) - } - ld.Bso.Flush() - - if ld.Iself { - ld.Asmbelfsetup() - } - - sect := ld.Segtext.Sect - ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) - ld.Codeblk(int64(sect.Vaddr), int64(sect.Length)) - for sect = sect.Next; sect != nil; sect = sect.Next { - ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) - ld.Datblk(int64(sect.Vaddr), int64(sect.Length)) - } - - if ld.Segrodata.Filelen > 0 { - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) - } - ld.Bso.Flush() - - ld.Cseek(int64(ld.Segrodata.Fileoff)) - ld.Datblk(int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) - } - - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f datblk\n", obj.Cputime()) - } - ld.Bso.Flush() - - ld.Cseek(int64(ld.Segdata.Fileoff)) - ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) - - machlink := uint32(0) - if ld.HEADTYPE == obj.Hdarwin { - if ld.Debug['v'] != 0 { - 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)) - - ld.Segdwarf.Fileoff = uint64(ld.Cpos()) - ld.Dwarfemitdebugsections() - ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff - - machlink = uint32(ld.Domacholink()) - } - - /* output symbol table */ - ld.Symsize = 0 - - ld.Lcsize = 0 - symo := uint32(0) - if ld.Debug['s'] == 0 { - // TODO: rationalize - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime()) - } - ld.Bso.Flush() - switch ld.HEADTYPE { - default: - if ld.Iself { - symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) - symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND))) - } - - case obj.Hplan9: - 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)) - } - - ld.Cseek(int64(symo)) - switch ld.HEADTYPE { - default: - if ld.Iself { - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f elfsym\n", obj.Cputime()) - } - ld.Asmelfsym() - ld.Cflush() - ld.Cwrite(ld.Elfstrdat) - - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) - } - ld.Dwarfemitdebugsections() - - if ld.Linkmode == ld.LinkExternal { - ld.Elfemitreloc() - } - } - - case obj.Hplan9: - ld.Asmplan9sym() - ld.Cflush() - - sym := ld.Linklookup(ld.Ctxt, "pclntab", 0) - if sym != nil { - ld.Lcsize = int32(len(sym.P)) - for i := 0; int32(i) < ld.Lcsize; i++ { - ld.Cput(uint8(sym.P[i])) - } - - ld.Cflush() - } - - case obj.Hdarwin: - if ld.Linkmode == ld.LinkExternal { - ld.Machoemitreloc() - } - } - } - - ld.Ctxt.Cursym = nil - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f header\n", obj.Cputime()) - } - ld.Bso.Flush() - ld.Cseek(0) - switch ld.HEADTYPE { - default: - case obj.Hplan9: /* plan 9 */ - ld.Thearch.Lput(0x647) /* magic */ - ld.Thearch.Lput(uint32(ld.Segtext.Filelen)) /* sizes */ - ld.Thearch.Lput(uint32(ld.Segdata.Filelen)) - ld.Thearch.Lput(uint32(ld.Segdata.Length - ld.Segdata.Filelen)) - ld.Thearch.Lput(uint32(ld.Symsize)) /* nsyms */ - ld.Thearch.Lput(uint32(ld.Entryvalue())) /* va of entry */ - ld.Thearch.Lput(0) - ld.Thearch.Lput(uint32(ld.Lcsize)) - - case obj.Hlinux, - obj.Hfreebsd, - obj.Hnetbsd, - obj.Hopenbsd, - obj.Hnacl: - ld.Asmbelf(int64(symo)) - - case obj.Hdarwin: - ld.Asmbmacho() - } - - ld.Cflush() - if ld.Debug['c'] != 0 { - fmt.Printf("textsize=%d\n", ld.Segtext.Filelen) - fmt.Printf("datsize=%d\n", ld.Segdata.Filelen) - fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen) - fmt.Printf("symsize=%d\n", ld.Symsize) - fmt.Printf("lcsize=%d\n", ld.Lcsize) - fmt.Printf("total=%d\n", ld.Segtext.Filelen+ld.Segdata.Length+uint64(ld.Symsize)+uint64(ld.Lcsize)) - } -} diff --git a/src/cmd/5l/l.go b/src/cmd/5l/l.go deleted file mode 100644 index adc8d286ae..0000000000 --- a/src/cmd/5l/l.go +++ /dev/null @@ -1,79 +0,0 @@ -// Inferno utils/5l/asm.c -// http://code.google.com/p/inferno-os/source/browse/utils/5l/asm.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package main - -// Writing object files. - -// Inferno utils/5l/l.h -// http://code.google.com/p/inferno-os/source/browse/utils/5l/l.h -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -const ( - thechar = '5' - PtrSize = 4 - IntSize = 4 - RegSize = 4 - MaxAlign = 8 // max data alignment - FuncAlign = 4 // single-instruction alignment - MINLC = 4 -) - -/* 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 deleted file mode 100644 index 9c9578343e..0000000000 --- a/src/cmd/5l/obj.go +++ /dev/null @@ -1,178 +0,0 @@ -// Inferno utils/5l/obj.c -// http://code.google.com/p/inferno-os/source/browse/utils/5l/obj.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package main - -import ( - "cmd/internal/ld" - "cmd/internal/obj" - "fmt" - "log" -) - -// Reading object files. - -func main() { - linkarchinit() - ld.Ldmain() -} - -func linkarchinit() { - ld.Thestring = "arm" - ld.Thelinkarch = &ld.Linkarm - - ld.Thearch.Thechar = thechar - ld.Thearch.Ptrsize = ld.Thelinkarch.Ptrsize - ld.Thearch.Intsize = ld.Thelinkarch.Ptrsize - ld.Thearch.Regsize = ld.Thelinkarch.Regsize - ld.Thearch.Funcalign = FuncAlign - ld.Thearch.Maxalign = MaxAlign - ld.Thearch.Minlc = MINLC - ld.Thearch.Dwarfregsp = DWARFREGSP - ld.Thearch.Dwarfreglr = DWARFREGLR - - ld.Thearch.Adddynrel = adddynrel - ld.Thearch.Archinit = archinit - ld.Thearch.Archreloc = archreloc - ld.Thearch.Archrelocvariant = archrelocvariant - ld.Thearch.Asmb = asmb - ld.Thearch.Elfreloc1 = elfreloc1 - ld.Thearch.Elfsetupplt = elfsetupplt - ld.Thearch.Gentext = gentext - ld.Thearch.Machoreloc1 = machoreloc1 - ld.Thearch.Lput = ld.Lputl - ld.Thearch.Wput = ld.Wputl - ld.Thearch.Vput = ld.Vputl - - ld.Thearch.Linuxdynld = "/lib/ld-linux.so.3" // 2 for OABI, 3 for EABI - ld.Thearch.Freebsddynld = "/usr/libexec/ld-elf.so.1" - ld.Thearch.Openbsddynld = "/usr/libexec/ld.so" - ld.Thearch.Netbsddynld = "/libexec/ld.elf_so" - ld.Thearch.Dragonflydynld = "XXX" - ld.Thearch.Solarisdynld = "XXX" -} - -func archinit() { - // getgoextlinkenabled is based on GO_EXTLINK_ENABLED when - // Go was built; see ../../make.bash. - if ld.Linkmode == ld.LinkAuto && obj.Getgoextlinkenabled() == "0" { - ld.Linkmode = ld.LinkInternal - } - - switch ld.HEADTYPE { - default: - if ld.Linkmode == ld.LinkAuto { - ld.Linkmode = ld.LinkInternal - } - if ld.Linkmode == ld.LinkExternal && obj.Getgoextlinkenabled() != "1" { - log.Fatalf("cannot use -linkmode=external with -H %s", ld.Headstr(int(ld.HEADTYPE))) - } - - case obj.Hlinux, - obj.Hfreebsd, - obj.Hnacl, - obj.Hdarwin: - break - } - - switch ld.HEADTYPE { - default: - ld.Exitf("unknown -H option: %v", ld.HEADTYPE) - - case obj.Hplan9: /* plan 9 */ - ld.HEADR = 32 - - if ld.INITTEXT == -1 { - ld.INITTEXT = 4128 - } - if ld.INITDAT == -1 { - ld.INITDAT = 0 - } - if ld.INITRND == -1 { - ld.INITRND = 4096 - } - - case obj.Hlinux, /* arm elf */ - obj.Hfreebsd, - obj.Hnetbsd, - obj.Hopenbsd: - ld.Debug['d'] = 0 - // with dynamic linking - ld.Elfinit() - ld.HEADR = ld.ELFRESERVE - if ld.INITTEXT == -1 { - ld.INITTEXT = 0x10000 + int64(ld.HEADR) - } - if ld.INITDAT == -1 { - ld.INITDAT = 0 - } - if ld.INITRND == -1 { - ld.INITRND = 4096 - } - - case obj.Hnacl: - ld.Elfinit() - ld.HEADR = 0x10000 - ld.Funcalign = 16 - if ld.INITTEXT == -1 { - ld.INITTEXT = 0x20000 - } - if ld.INITDAT == -1 { - ld.INITDAT = 0 - } - if ld.INITRND == -1 { - ld.INITRND = 0x10000 - } - - case obj.Hdarwin: /* apple MACH */ - ld.Debug['w'] = 1 // disable DWARF generataion - ld.Machoinit() - ld.HEADR = ld.INITIAL_MACHO_HEADR - if ld.INITTEXT == -1 { - ld.INITTEXT = 4096 + int64(ld.HEADR) - } - if ld.INITDAT == -1 { - ld.INITDAT = 0 - } - if ld.INITRND == -1 { - ld.INITRND = 4096 - } - } - - if ld.INITDAT != 0 && ld.INITRND != 0 { - fmt.Printf("warning: -D0x%x is ignored because of -R0x%x\n", uint64(ld.INITDAT), uint32(ld.INITRND)) - } - - // embed goarm to runtime.goarm - s := ld.Linklookup(ld.Ctxt, "runtime.goarm", 0) - - s.Type = obj.SRODATA - ld.Adduint8(ld.Ctxt, s, uint8(ld.Ctxt.Goarm)) -} diff --git a/src/cmd/6g/cgen.go b/src/cmd/6g/cgen.go deleted file mode 100644 index 23e2d1b57f..0000000000 --- a/src/cmd/6g/cgen.go +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2009 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/gc" - "cmd/internal/obj" - "cmd/internal/obj/x86" -) - -func blockcopy(n, ns *gc.Node, osrc, odst, w int64) { - var noddi gc.Node - gc.Nodreg(&noddi, gc.Types[gc.Tptr], x86.REG_DI) - var nodsi gc.Node - gc.Nodreg(&nodsi, gc.Types[gc.Tptr], x86.REG_SI) - - var nodl gc.Node - var nodr gc.Node - if n.Ullman >= ns.Ullman { - gc.Agenr(n, &nodr, &nodsi) - if ns.Op == gc.ONAME { - gc.Gvardef(ns) - } - gc.Agenr(ns, &nodl, &noddi) - } else { - if ns.Op == gc.ONAME { - gc.Gvardef(ns) - } - gc.Agenr(ns, &nodl, &noddi) - gc.Agenr(n, &nodr, &nodsi) - } - - if nodl.Reg != x86.REG_DI { - gmove(&nodl, &noddi) - } - if nodr.Reg != x86.REG_SI { - gmove(&nodr, &nodsi) - } - gc.Regfree(&nodl) - gc.Regfree(&nodr) - - c := w % 8 // bytes - q := w / 8 // quads - - var oldcx gc.Node - var cx gc.Node - savex(x86.REG_CX, &cx, &oldcx, nil, gc.Types[gc.TINT64]) - - // if we are copying forward on the stack and - // the src and dst overlap, then reverse direction - if osrc < odst && odst < osrc+w { - // reverse direction - gins(x86.ASTD, nil, nil) // set direction flag - if c > 0 { - gconreg(addptr, w-1, x86.REG_SI) - gconreg(addptr, w-1, x86.REG_DI) - - gconreg(movptr, c, x86.REG_CX) - gins(x86.AREP, nil, nil) // repeat - gins(x86.AMOVSB, nil, nil) // MOVB *(SI)-,*(DI)- - } - - if q > 0 { - if c > 0 { - gconreg(addptr, -7, x86.REG_SI) - gconreg(addptr, -7, x86.REG_DI) - } else { - gconreg(addptr, w-8, x86.REG_SI) - gconreg(addptr, w-8, x86.REG_DI) - } - - gconreg(movptr, q, x86.REG_CX) - gins(x86.AREP, nil, nil) // repeat - gins(x86.AMOVSQ, nil, nil) // MOVQ *(SI)-,*(DI)- - } - - // we leave with the flag clear - gins(x86.ACLD, nil, nil) - } else { - // normal direction - if q > 128 || (gc.Nacl && q >= 4) { - gconreg(movptr, q, x86.REG_CX) - gins(x86.AREP, nil, nil) // repeat - gins(x86.AMOVSQ, nil, nil) // MOVQ *(SI)+,*(DI)+ - } else if q >= 4 { - p := gins(obj.ADUFFCOPY, nil, nil) - p.To.Type = obj.TYPE_ADDR - p.To.Sym = gc.Linksym(gc.Pkglookup("duffcopy", gc.Runtimepkg)) - - // 14 and 128 = magic constants: see ../../runtime/asm_amd64.s - p.To.Offset = 14 * (128 - q) - } else if !gc.Nacl && c == 0 { - // We don't need the MOVSQ side-effect of updating SI and DI, - // and issuing a sequence of MOVQs directly is faster. - nodsi.Op = gc.OINDREG - - noddi.Op = gc.OINDREG - for q > 0 { - gmove(&nodsi, &cx) // MOVQ x+(SI),CX - gmove(&cx, &noddi) // MOVQ CX,x+(DI) - nodsi.Xoffset += 8 - noddi.Xoffset += 8 - q-- - } - } else { - for q > 0 { - gins(x86.AMOVSQ, nil, nil) // MOVQ *(SI)+,*(DI)+ - q-- - } - } - - // copy the remaining c bytes - if w < 4 || c <= 1 || (odst < osrc && osrc < odst+w) { - for c > 0 { - gins(x86.AMOVSB, nil, nil) // MOVB *(SI)+,*(DI)+ - c-- - } - } else if w < 8 || c <= 4 { - nodsi.Op = gc.OINDREG - noddi.Op = gc.OINDREG - cx.Type = gc.Types[gc.TINT32] - nodsi.Type = gc.Types[gc.TINT32] - noddi.Type = gc.Types[gc.TINT32] - if c > 4 { - nodsi.Xoffset = 0 - noddi.Xoffset = 0 - gmove(&nodsi, &cx) - gmove(&cx, &noddi) - } - - nodsi.Xoffset = c - 4 - noddi.Xoffset = c - 4 - gmove(&nodsi, &cx) - gmove(&cx, &noddi) - } else { - nodsi.Op = gc.OINDREG - noddi.Op = gc.OINDREG - cx.Type = gc.Types[gc.TINT64] - nodsi.Type = gc.Types[gc.TINT64] - noddi.Type = gc.Types[gc.TINT64] - nodsi.Xoffset = c - 8 - noddi.Xoffset = c - 8 - gmove(&nodsi, &cx) - gmove(&cx, &noddi) - } - } - - restx(&cx, &oldcx) -} diff --git a/src/cmd/6g/galign.go b/src/cmd/6g/galign.go deleted file mode 100644 index 17d78f399d..0000000000 --- a/src/cmd/6g/galign.go +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2009 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/gc" - "cmd/internal/obj" - "cmd/internal/obj/x86" -) - -var ( - thechar int = '6' - thestring string = "amd64" - thelinkarch *obj.LinkArch = &x86.Linkamd64 -) - -func linkarchinit() { - if obj.Getgoarch() == "amd64p32" { - thelinkarch = &x86.Linkamd64p32 - gc.Thearch.Thelinkarch = thelinkarch - thestring = "amd64p32" - gc.Thearch.Thestring = "amd64p32" - } -} - -var MAXWIDTH int64 = 1 << 50 - -var ( - addptr int = x86.AADDQ - movptr int = x86.AMOVQ - leaptr int = x86.ALEAQ - cmpptr int = x86.ACMPQ -) - -/* - * go declares several platform-specific type aliases: - * int, uint, and uintptr - */ -var typedefs = []gc.Typedef{ - gc.Typedef{"int", gc.TINT, gc.TINT64}, - gc.Typedef{"uint", gc.TUINT, gc.TUINT64}, - gc.Typedef{"uintptr", gc.TUINTPTR, gc.TUINT64}, -} - -func betypeinit() { - gc.Widthptr = 8 - gc.Widthint = 8 - gc.Widthreg = 8 - if obj.Getgoarch() == "amd64p32" { - gc.Widthptr = 4 - gc.Widthint = 4 - addptr = x86.AADDL - movptr = x86.AMOVL - leaptr = x86.ALEAL - cmpptr = x86.ACMPL - typedefs[0].Sameas = gc.TINT32 - typedefs[1].Sameas = gc.TUINT32 - typedefs[2].Sameas = gc.TUINT32 - } - - if gc.Ctxt.Flag_dynlink { - gc.Thearch.ReservedRegs = append(gc.Thearch.ReservedRegs, x86.REG_R15) - } -} - -func main() { - if obj.Getgoos() == "nacl" { - resvd = append(resvd, x86.REG_BP, x86.REG_R15) - } else if obj.Framepointer_enabled != 0 { - resvd = append(resvd, x86.REG_BP) - } - - gc.Thearch.Thechar = thechar - gc.Thearch.Thestring = thestring - gc.Thearch.Thelinkarch = thelinkarch - gc.Thearch.Typedefs = typedefs - gc.Thearch.REGSP = x86.REGSP - gc.Thearch.REGCTXT = x86.REGCTXT - gc.Thearch.REGCALLX = x86.REG_BX - gc.Thearch.REGCALLX2 = x86.REG_AX - gc.Thearch.REGRETURN = x86.REG_AX - gc.Thearch.REGMIN = x86.REG_AX - gc.Thearch.REGMAX = x86.REG_R15 - gc.Thearch.FREGMIN = x86.REG_X0 - gc.Thearch.FREGMAX = x86.REG_X15 - gc.Thearch.MAXWIDTH = MAXWIDTH - gc.Thearch.ReservedRegs = resvd - - gc.Thearch.AddIndex = addindex - gc.Thearch.Betypeinit = betypeinit - gc.Thearch.Cgen_bmul = cgen_bmul - gc.Thearch.Cgen_hmul = cgen_hmul - gc.Thearch.Cgen_shift = cgen_shift - gc.Thearch.Clearfat = clearfat - gc.Thearch.Defframe = defframe - gc.Thearch.Dodiv = dodiv - gc.Thearch.Excise = excise - gc.Thearch.Expandchecks = expandchecks - 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 - gc.Thearch.Linkarchinit = linkarchinit - gc.Thearch.Peep = peep - gc.Thearch.Proginfo = proginfo - gc.Thearch.Regtyp = regtyp - gc.Thearch.Sameaddr = sameaddr - gc.Thearch.Smallindir = smallindir - gc.Thearch.Stackaddr = stackaddr - gc.Thearch.Blockcopy = blockcopy - gc.Thearch.Sudoaddable = sudoaddable - gc.Thearch.Sudoclean = sudoclean - gc.Thearch.Excludedregs = excludedregs - gc.Thearch.RtoB = RtoB - gc.Thearch.FtoB = FtoB - gc.Thearch.BtoR = BtoR - gc.Thearch.BtoF = BtoF - gc.Thearch.Optoas = optoas - gc.Thearch.Doregbits = doregbits - gc.Thearch.Regnames = regnames - - gc.Main() - gc.Exit(0) -} diff --git a/src/cmd/6g/ggen.go b/src/cmd/6g/ggen.go deleted file mode 100644 index e0e1b8a4df..0000000000 --- a/src/cmd/6g/ggen.go +++ /dev/null @@ -1,743 +0,0 @@ -// Copyright 2009 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/gc" - "cmd/internal/obj" - "cmd/internal/obj/x86" -) - -func defframe(ptxt *obj.Prog) { - var n *gc.Node - - // fill in argument size, stack size - ptxt.To.Type = obj.TYPE_TEXTSIZE - - ptxt.To.Val = int32(gc.Rnd(gc.Curfn.Type.Argwid, int64(gc.Widthptr))) - frame := uint32(gc.Rnd(gc.Stksize+gc.Maxarg, int64(gc.Widthreg))) - ptxt.To.Offset = int64(frame) - - // insert code to zero ambiguously live variables - // so that the garbage collector only sees initialized values - // when it looks for pointers. - p := ptxt - - hi := int64(0) - lo := hi - ax := uint32(0) - - // 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.Name.Needzero { - continue - } - if n.Class != gc.PAUTO { - gc.Fatal("needzero class %d", n.Class) - } - if n.Type.Width%int64(gc.Widthptr) != 0 || n.Xoffset%int64(gc.Widthptr) != 0 || n.Type.Width == 0 { - gc.Fatal("var %v has size %d offset %d", gc.Nconv(n, obj.FmtLong), int(n.Type.Width), int(n.Xoffset)) - } - - if lo != hi && n.Xoffset+n.Type.Width >= lo-int64(2*gc.Widthreg) { - // merge with range we already have - lo = n.Xoffset - - continue - } - - // zero old range - p = zerorange(p, int64(frame), lo, hi, &ax) - - // set new range - hi = n.Xoffset + n.Type.Width - - lo = n.Xoffset - } - - // zero final range - zerorange(p, int64(frame), lo, hi, &ax) -} - -// DUFFZERO consists of repeated blocks of 4 MOVs + ADD, -// with 4 STOSQs at the very end. -// The trailing STOSQs prevent the need for a DI preadjustment -// for small numbers of words to clear. -// See runtime/mkduff.go. -const ( - dzBlocks = 31 // number of MOV/ADD blocks - dzBlockLen = 4 // number of clears per block - dzBlockSize = 19 // size of instructions in a single block - dzMovSize = 4 // size of single MOV instruction w/ offset - dzAddSize = 4 // size of single ADD instruction - dzDIStep = 8 // number of bytes cleared by each MOV instruction - - dzTailLen = 4 // number of final STOSQ instructions - dzTailSize = 2 // size of single STOSQ instruction - - dzSize = dzBlocks*dzBlockSize + dzTailLen*dzTailSize // total size of DUFFZERO routine -) - -// duffzeroDI returns the pre-adjustment to DI for a call to DUFFZERO. -// q is the number of words to zero. -func dzDI(q int64) int64 { - if q < dzTailLen { - return 0 - } - q -= dzTailLen - if q%dzBlockLen == 0 { - return 0 - } - return -dzDIStep * (dzBlockLen - q%dzBlockLen) -} - -// dzOff returns the offset for a jump into DUFFZERO. -// q is the number of words to zero. -func dzOff(q int64) int64 { - off := int64(dzSize) - if q < dzTailLen { - return off - q*dzTailSize - } - off -= dzTailLen * dzTailSize - q -= dzTailLen - blocks, steps := q/dzBlockLen, q%dzBlockLen - off -= dzBlockSize * blocks - if steps > 0 { - off -= dzAddSize + dzMovSize*steps - } - return off -} - -func zerorange(p *obj.Prog, frame int64, lo int64, hi int64, ax *uint32) *obj.Prog { - cnt := hi - lo - if cnt == 0 { - return p - } - if *ax == 0 { - p = appendpp(p, x86.AMOVQ, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, x86.REG_AX, 0) - *ax = 1 - } - - if cnt%int64(gc.Widthreg) != 0 { - // should only happen with nacl - if cnt%int64(gc.Widthptr) != 0 { - gc.Fatal("zerorange count not a multiple of widthptr %d", cnt) - } - p = appendpp(p, x86.AMOVL, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, frame+lo) - lo += int64(gc.Widthptr) - cnt -= int64(gc.Widthptr) - } - - if cnt <= int64(4*gc.Widthreg) { - for i := int64(0); i < cnt; i += int64(gc.Widthreg) { - p = appendpp(p, x86.AMOVQ, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, frame+lo+i) - } - } else if !gc.Nacl && (cnt <= int64(128*gc.Widthreg)) { - q := cnt / int64(gc.Widthreg) - p = appendpp(p, leaptr, obj.TYPE_MEM, x86.REG_SP, frame+lo+dzDI(q), obj.TYPE_REG, x86.REG_DI, 0) - p = appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_ADDR, 0, dzOff(q)) - p.To.Sym = gc.Linksym(gc.Pkglookup("duffzero", gc.Runtimepkg)) - } else { - p = appendpp(p, x86.AMOVQ, obj.TYPE_CONST, 0, cnt/int64(gc.Widthreg), obj.TYPE_REG, x86.REG_CX, 0) - p = appendpp(p, leaptr, obj.TYPE_MEM, x86.REG_SP, frame+lo, obj.TYPE_REG, x86.REG_DI, 0) - p = appendpp(p, x86.AREP, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0) - p = appendpp(p, x86.ASTOSQ, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0) - } - - return p -} - -func appendpp(p *obj.Prog, as int, ftype int, freg int, foffset int64, ttype int, treg int, toffset int64) *obj.Prog { - q := gc.Ctxt.NewProg() - gc.Clearp(q) - q.As = int16(as) - q.Lineno = p.Lineno - q.From.Type = int16(ftype) - q.From.Reg = int16(freg) - q.From.Offset = foffset - q.To.Type = int16(ttype) - q.To.Reg = int16(treg) - q.To.Offset = toffset - q.Link = p.Link - p.Link = q - return q -} - -var panicdiv *gc.Node - -/* - * generate division. - * generates one of: - * res = nl / nr - * res = nl % nr - * according to op. - */ -func dodiv(op int, nl *gc.Node, nr *gc.Node, res *gc.Node) { - // Have to be careful about handling - // most negative int divided by -1 correctly. - // The hardware will trap. - // Also the byte divide instruction needs AH, - // which we otherwise don't have to deal with. - // Easiest way to avoid for int8, int16: use int32. - // For int32 and int64, use explicit test. - // Could use int64 hw for int32. - t := nl.Type - - t0 := t - check := 0 - if gc.Issigned[t.Etype] { - check = 1 - if gc.Isconst(nl, gc.CTINT) && nl.Int() != -(1<= nr.Ullman { - savex(x86.REG_AX, &ax, &oldax, res, t0) - gc.Cgen(nl, &ax) - gc.Regalloc(&ax, t0, &ax) // mark ax live during cgen - gc.Cgen(nr, &n3) - gc.Regfree(&ax) - } else { - gc.Cgen(nr, &n3) - savex(x86.REG_AX, &ax, &oldax, res, t0) - gc.Cgen(nl, &ax) - } - - if t != t0 { - // Convert - ax1 := ax - - n31 := n3 - ax.Type = t - n3.Type = t - gmove(&ax1, &ax) - gmove(&n31, &n3) - } - - var n4 gc.Node - if gc.Nacl { - // Native Client does not relay the divide-by-zero trap - // to the executing program, so we must insert a check - // for ourselves. - gc.Nodconst(&n4, t, 0) - - gins(optoas(gc.OCMP, t), &n3, &n4) - p1 := gc.Gbranch(optoas(gc.ONE, t), nil, +1) - if panicdiv == nil { - panicdiv = gc.Sysfunc("panicdivide") - } - gc.Ginscall(panicdiv, -1) - gc.Patch(p1, gc.Pc) - } - - var p2 *obj.Prog - if check != 0 { - gc.Nodconst(&n4, t, -1) - gins(optoas(gc.OCMP, t), &n3, &n4) - p1 := gc.Gbranch(optoas(gc.ONE, t), nil, +1) - if op == gc.ODIV { - // a / (-1) is -a. - gins(optoas(gc.OMINUS, t), nil, &ax) - - gmove(&ax, res) - } else { - // a % (-1) is 0. - gc.Nodconst(&n4, t, 0) - - gmove(&n4, res) - } - - p2 = gc.Gbranch(obj.AJMP, nil, 0) - gc.Patch(p1, gc.Pc) - } - - var olddx gc.Node - var dx gc.Node - savex(x86.REG_DX, &dx, &olddx, res, t) - if !gc.Issigned[t.Etype] { - gc.Nodconst(&n4, t, 0) - gmove(&n4, &dx) - } else { - gins(optoas(gc.OEXTEND, t), nil, nil) - } - gins(a, &n3, nil) - gc.Regfree(&n3) - if op == gc.ODIV { - gmove(&ax, res) - } else { - gmove(&dx, res) - } - restx(&dx, &olddx) - if check != 0 { - gc.Patch(p2, gc.Pc) - } - restx(&ax, &oldax) -} - -/* - * register dr is one of the special ones (AX, CX, DI, SI, etc.). - * we need to use it. if it is already allocated as a temporary - * (r > 1; can only happen if a routine like sgen passed a - * special as cgen's res and then cgen used regalloc to reuse - * it as its own temporary), then move it for now to another - * register. caller must call restx to move it back. - * the move is not necessary if dr == res, because res is - * known to be dead. - */ -func savex(dr int, x *gc.Node, oldx *gc.Node, res *gc.Node, t *gc.Type) { - r := reg[dr] - - // save current ax and dx if they are live - // and not the destination - *oldx = gc.Node{} - - gc.Nodreg(x, t, dr) - if r > 1 && !gc.Samereg(x, res) { - gc.Regalloc(oldx, gc.Types[gc.TINT64], nil) - x.Type = gc.Types[gc.TINT64] - gmove(x, oldx) - x.Type = t - oldx.Etype = r // squirrel away old r value - reg[dr] = 1 - } -} - -func restx(x *gc.Node, oldx *gc.Node) { - if oldx.Op != 0 { - x.Type = gc.Types[gc.TINT64] - reg[x.Reg] = oldx.Etype - gmove(oldx, x) - gc.Regfree(oldx) - } -} - -/* - * generate high multiply: - * res = (nl*nr) >> width - */ -func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) { - t := nl.Type - a := optoas(gc.OHMUL, t) - if nl.Ullman < nr.Ullman { - tmp := nl - nl = nr - nr = tmp - } - - var n1 gc.Node - gc.Cgenr(nl, &n1, res) - var n2 gc.Node - gc.Cgenr(nr, &n2, nil) - var ax gc.Node - gc.Nodreg(&ax, t, x86.REG_AX) - gmove(&n1, &ax) - gins(a, &n2, nil) - gc.Regfree(&n2) - gc.Regfree(&n1) - - var dx gc.Node - if t.Width == 1 { - // byte multiply behaves differently. - gc.Nodreg(&ax, t, x86.REG_AH) - - gc.Nodreg(&dx, t, x86.REG_DX) - gmove(&ax, &dx) - } - - gc.Nodreg(&dx, t, x86.REG_DX) - gmove(&dx, res) -} - -/* - * generate shift according to op, one of: - * res = nl << nr - * res = nl >> nr - */ -func cgen_shift(op int, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) { - a := optoas(op, nl.Type) - - if nr.Op == gc.OLITERAL { - var n1 gc.Node - gc.Regalloc(&n1, nl.Type, res) - gc.Cgen(nl, &n1) - sc := uint64(nr.Int()) - if sc >= uint64(nl.Type.Width*8) { - // large shift gets 2 shifts by width-1 - var n3 gc.Node - gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1) - - gins(a, &n3, &n1) - gins(a, &n3, &n1) - } else { - gins(a, nr, &n1) - } - gmove(&n1, res) - gc.Regfree(&n1) - return - } - - if nl.Ullman >= gc.UINF { - var n4 gc.Node - gc.Tempname(&n4, nl.Type) - gc.Cgen(nl, &n4) - nl = &n4 - } - - if nr.Ullman >= gc.UINF { - var n5 gc.Node - gc.Tempname(&n5, nr.Type) - gc.Cgen(nr, &n5) - nr = &n5 - } - - rcx := int(reg[x86.REG_CX]) - var n1 gc.Node - gc.Nodreg(&n1, gc.Types[gc.TUINT32], x86.REG_CX) - - // Allow either uint32 or uint64 as shift type, - // to avoid unnecessary conversion from uint32 to uint64 - // just to do the comparison. - tcount := gc.Types[gc.Simtype[nr.Type.Etype]] - - if tcount.Etype < gc.TUINT32 { - tcount = gc.Types[gc.TUINT32] - } - - gc.Regalloc(&n1, nr.Type, &n1) // to hold the shift type in CX - var n3 gc.Node - gc.Regalloc(&n3, tcount, &n1) // to clear high bits of CX - - var cx gc.Node - gc.Nodreg(&cx, gc.Types[gc.TUINT64], x86.REG_CX) - - var oldcx gc.Node - if rcx > 0 && !gc.Samereg(&cx, res) { - gc.Regalloc(&oldcx, gc.Types[gc.TUINT64], nil) - gmove(&cx, &oldcx) - } - - cx.Type = tcount - - var n2 gc.Node - if gc.Samereg(&cx, res) { - gc.Regalloc(&n2, nl.Type, nil) - } else { - gc.Regalloc(&n2, nl.Type, res) - } - if nl.Ullman >= nr.Ullman { - gc.Cgen(nl, &n2) - gc.Cgen(nr, &n1) - gmove(&n1, &n3) - } else { - gc.Cgen(nr, &n1) - gmove(&n1, &n3) - gc.Cgen(nl, &n2) - } - - gc.Regfree(&n3) - - // test and fix up large shifts - if !bounded { - gc.Nodconst(&n3, tcount, nl.Type.Width*8) - gins(optoas(gc.OCMP, tcount), &n1, &n3) - p1 := gc.Gbranch(optoas(gc.OLT, tcount), nil, +1) - if op == gc.ORSH && gc.Issigned[nl.Type.Etype] { - gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1) - gins(a, &n3, &n2) - } else { - gc.Nodconst(&n3, nl.Type, 0) - gmove(&n3, &n2) - } - - gc.Patch(p1, gc.Pc) - } - - gins(a, &n1, &n2) - - if oldcx.Op != 0 { - cx.Type = gc.Types[gc.TUINT64] - gmove(&oldcx, &cx) - gc.Regfree(&oldcx) - } - - gmove(&n2, res) - - gc.Regfree(&n1) - gc.Regfree(&n2) -} - -/* - * generate byte multiply: - * res = nl * nr - * there is no 2-operand byte multiply instruction so - * we do a full-width multiplication and truncate afterwards. - */ -func cgen_bmul(op int, nl *gc.Node, nr *gc.Node, res *gc.Node) bool { - if optoas(op, nl.Type) != x86.AIMULB { - return false - } - - // largest ullman on left. - if nl.Ullman < nr.Ullman { - tmp := nl - nl = nr - nr = tmp - } - - // generate operands in "8-bit" registers. - var n1b gc.Node - gc.Regalloc(&n1b, nl.Type, res) - - gc.Cgen(nl, &n1b) - var n2b gc.Node - gc.Regalloc(&n2b, nr.Type, nil) - gc.Cgen(nr, &n2b) - - // perform full-width multiplication. - t := gc.Types[gc.TUINT64] - - if gc.Issigned[nl.Type.Etype] { - t = gc.Types[gc.TINT64] - } - var n1 gc.Node - gc.Nodreg(&n1, t, int(n1b.Reg)) - var n2 gc.Node - gc.Nodreg(&n2, t, int(n2b.Reg)) - a := optoas(op, t) - gins(a, &n2, &n1) - - // truncate. - gmove(&n1, res) - - gc.Regfree(&n1b) - gc.Regfree(&n2b) - return true -} - -func clearfat(nl *gc.Node) { - /* clear a fat object */ - if gc.Debug['g'] != 0 { - gc.Dump("\nclearfat", nl) - } - - w := nl.Type.Width - - // Avoid taking the address for simple enough types. - if gc.Componentgen(nil, nl) { - return - } - - c := w % 8 // bytes - q := w / 8 // quads - - if q < 4 { - // Write sequence of MOV 0, off(base) instead of using STOSQ. - // The hope is that although the code will be slightly longer, - // the MOVs will have no dependencies and pipeline better - // than the unrolled STOSQ loop. - // NOTE: Must use agen, not igen, so that optimizer sees address - // being taken. We are not writing on field boundaries. - var n1 gc.Node - gc.Agenr(nl, &n1, nil) - - n1.Op = gc.OINDREG - var z gc.Node - gc.Nodconst(&z, gc.Types[gc.TUINT64], 0) - for { - tmp14 := q - q-- - if tmp14 <= 0 { - break - } - n1.Type = z.Type - gins(x86.AMOVQ, &z, &n1) - n1.Xoffset += 8 - } - - if c >= 4 { - gc.Nodconst(&z, gc.Types[gc.TUINT32], 0) - n1.Type = z.Type - gins(x86.AMOVL, &z, &n1) - n1.Xoffset += 4 - c -= 4 - } - - gc.Nodconst(&z, gc.Types[gc.TUINT8], 0) - for { - tmp15 := c - c-- - if tmp15 <= 0 { - break - } - n1.Type = z.Type - gins(x86.AMOVB, &z, &n1) - n1.Xoffset++ - } - - gc.Regfree(&n1) - return - } - - var oldn1 gc.Node - var n1 gc.Node - savex(x86.REG_DI, &n1, &oldn1, nil, gc.Types[gc.Tptr]) - gc.Agen(nl, &n1) - - var ax gc.Node - var oldax gc.Node - savex(x86.REG_AX, &ax, &oldax, nil, gc.Types[gc.Tptr]) - gconreg(x86.AMOVL, 0, x86.REG_AX) - - if q > 128 || gc.Nacl { - gconreg(movptr, q, x86.REG_CX) - gins(x86.AREP, nil, nil) // repeat - gins(x86.ASTOSQ, nil, nil) // STOQ AL,*(DI)+ - } else { - if di := dzDI(q); di != 0 { - gconreg(addptr, di, x86.REG_DI) - } - p := gins(obj.ADUFFZERO, nil, nil) - p.To.Type = obj.TYPE_ADDR - p.To.Sym = gc.Linksym(gc.Pkglookup("duffzero", gc.Runtimepkg)) - p.To.Offset = dzOff(q) - } - - z := ax - di := n1 - if w >= 8 && c >= 4 { - di.Op = gc.OINDREG - z.Type = gc.Types[gc.TINT64] - di.Type = z.Type - p := gins(x86.AMOVQ, &z, &di) - p.To.Scale = 1 - p.To.Offset = c - 8 - } else if c >= 4 { - di.Op = gc.OINDREG - z.Type = gc.Types[gc.TINT32] - di.Type = z.Type - gins(x86.AMOVL, &z, &di) - if c > 4 { - p := gins(x86.AMOVL, &z, &di) - p.To.Scale = 1 - p.To.Offset = c - 4 - } - } else { - for c > 0 { - gins(x86.ASTOSB, nil, nil) // STOB AL,*(DI)+ - c-- - } - } - - restx(&n1, &oldn1) - restx(&ax, &oldax) -} - -// Called after regopt and peep have run. -// Expand CHECKNIL pseudo-op into actual nil pointer check. -func expandchecks(firstp *obj.Prog) { - var p1 *obj.Prog - var p2 *obj.Prog - - for p := firstp; p != nil; p = p.Link { - if p.As != obj.ACHECKNIL { - continue - } - if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers - gc.Warnl(int(p.Lineno), "generated nil check") - } - - // check is - // CMP arg, $0 - // JNE 2(PC) (likely) - // MOV AX, 0 - p1 = gc.Ctxt.NewProg() - - p2 = gc.Ctxt.NewProg() - gc.Clearp(p1) - gc.Clearp(p2) - p1.Link = p2 - p2.Link = p.Link - p.Link = p1 - p1.Lineno = p.Lineno - p2.Lineno = p.Lineno - p1.Pc = 9999 - p2.Pc = 9999 - p.As = int16(cmpptr) - p.To.Type = obj.TYPE_CONST - p.To.Offset = 0 - p1.As = x86.AJNE - p1.From.Type = obj.TYPE_CONST - p1.From.Offset = 1 // likely - p1.To.Type = obj.TYPE_BRANCH - p1.To.Val = p2.Link - - // crash by write to memory address 0. - // if possible, since we know arg is 0, use 0(arg), - // which will be shorter to encode than plain 0. - p2.As = x86.AMOVL - - p2.From.Type = obj.TYPE_REG - p2.From.Reg = x86.REG_AX - if regtyp(&p.From) { - p2.To.Type = obj.TYPE_MEM - p2.To.Reg = p.From.Reg - } else { - p2.To.Type = obj.TYPE_MEM - p2.To.Reg = x86.REG_NONE - } - - p2.To.Offset = 0 - } -} - -// addr += index*width if possible. -func addindex(index *gc.Node, width int64, addr *gc.Node) bool { - switch width { - case 1, 2, 4, 8: - p1 := gins(x86.ALEAQ, index, addr) - p1.From.Type = obj.TYPE_MEM - p1.From.Scale = int16(width) - p1.From.Index = p1.From.Reg - p1.From.Reg = p1.To.Reg - return true - } - return false -} - -// res = runtime.getg() -func getg(res *gc.Node) { - var n1 gc.Node - gc.Regalloc(&n1, res.Type, res) - mov := optoas(gc.OAS, gc.Types[gc.Tptr]) - p := gins(mov, nil, &n1) - p.From.Type = obj.TYPE_REG - p.From.Reg = x86.REG_TLS - p = gins(mov, nil, &n1) - p.From = p.To - p.From.Type = obj.TYPE_MEM - p.From.Index = x86.REG_TLS - p.From.Scale = 1 - gmove(&n1, res) - gc.Regfree(&n1) -} diff --git a/src/cmd/6g/gsubr.go b/src/cmd/6g/gsubr.go deleted file mode 100644 index 9b9141468e..0000000000 --- a/src/cmd/6g/gsubr.go +++ /dev/null @@ -1,1380 +0,0 @@ -// Derived from Inferno utils/6c/txt.c -// http://code.google.com/p/inferno-os/source/browse/utils/6c/txt.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package main - -import ( - "cmd/internal/gc" - "cmd/internal/gc/big" - "cmd/internal/obj" - "cmd/internal/obj/x86" - "fmt" -) - -var resvd = []int{ - x86.REG_DI, // for movstring - x86.REG_SI, // for movstring - - x86.REG_AX, // for divide - x86.REG_CX, // for shift - x86.REG_DX, // for divide - x86.REG_SP, // for stack -} - -/* - * generate - * as $c, reg - */ -func gconreg(as int, c int64, reg int) { - var nr gc.Node - - switch as { - case x86.AADDL, - x86.AMOVL, - x86.ALEAL: - gc.Nodreg(&nr, gc.Types[gc.TINT32], reg) - - default: - gc.Nodreg(&nr, gc.Types[gc.TINT64], reg) - } - - ginscon(as, c, &nr) -} - -/* - * generate - * as $c, n - */ -func ginscon(as int, c int64, n2 *gc.Node) { - var n1 gc.Node - - switch as { - case x86.AADDL, - x86.AMOVL, - x86.ALEAL: - gc.Nodconst(&n1, gc.Types[gc.TINT32], c) - - default: - gc.Nodconst(&n1, gc.Types[gc.TINT64], c) - } - - if as != x86.AMOVQ && (c < -(1<<31) || c >= 1<<31) { - // cannot have 64-bit immediate in ADD, etc. - // instead, MOV into register first. - var ntmp gc.Node - gc.Regalloc(&ntmp, gc.Types[gc.TINT64], nil) - - gins(x86.AMOVQ, &n1, &ntmp) - gins(as, &ntmp, n2) - gc.Regfree(&ntmp) - return - } - - 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) -} - -// set up nodes representing 2^63 -var ( - bigi gc.Node - bigf gc.Node - bignodes_did bool -) - -func bignodes() { - if bignodes_did { - return - } - bignodes_did = true - - var i big.Int - i.SetInt64(1) - i.Lsh(&i, 63) - - gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0) - bigi.SetBigInt(&i) - - bigi.Convconst(&bigf, gc.Types[gc.TFLOAT64]) -} - -/* - * generate move: - * t = f - * hard part is conversions. - */ -func gmove(f *gc.Node, t *gc.Node) { - if gc.Debug['M'] != 0 { - fmt.Printf("gmove %v -> %v\n", gc.Nconv(f, obj.FmtLong), gc.Nconv(t, obj.FmtLong)) - } - - ft := gc.Simsimtype(f.Type) - tt := gc.Simsimtype(t.Type) - cvt := t.Type - - if gc.Iscomplex[ft] || gc.Iscomplex[tt] { - gc.Complexmove(f, t) - return - } - - // cannot have two memory operands - var a int - if gc.Ismem(f) && gc.Ismem(t) { - goto hard - } - - // convert constant to desired type - if f.Op == gc.OLITERAL { - var con gc.Node - f.Convconst(&con, t.Type) - f = &con - ft = tt // so big switch will choose a simple mov - - // some constants can't move directly to memory. - if gc.Ismem(t) { - // float constants come from memory. - if gc.Isfloat[tt] { - goto hard - } - - // 64-bit immediates are really 32-bit sign-extended - // unless moving into a register. - if gc.Isint[tt] { - if i := con.Int(); int64(int32(i)) != i { - goto hard - } - } - } - } - - // value -> value copy, only one memory operand. - // figure out the instruction to use. - // break out of switch for one-instruction gins. - // goto rdst for "destination must be register". - // goto hard for "convert to cvt type first". - // otherwise handle and return. - - switch uint32(ft)<<16 | uint32(tt) { - default: - gc.Fatal("gmove %v -> %v", gc.Tconv(f.Type, obj.FmtLong), gc.Tconv(t.Type, obj.FmtLong)) - - /* - * integer copy and truncate - */ - case gc.TINT8<<16 | gc.TINT8, // same size - gc.TINT8<<16 | gc.TUINT8, - gc.TUINT8<<16 | gc.TINT8, - gc.TUINT8<<16 | gc.TUINT8, - gc.TINT16<<16 | gc.TINT8, - // truncate - gc.TUINT16<<16 | gc.TINT8, - gc.TINT32<<16 | gc.TINT8, - gc.TUINT32<<16 | gc.TINT8, - gc.TINT64<<16 | gc.TINT8, - gc.TUINT64<<16 | gc.TINT8, - gc.TINT16<<16 | gc.TUINT8, - gc.TUINT16<<16 | gc.TUINT8, - gc.TINT32<<16 | gc.TUINT8, - gc.TUINT32<<16 | gc.TUINT8, - gc.TINT64<<16 | gc.TUINT8, - gc.TUINT64<<16 | gc.TUINT8: - a = x86.AMOVB - - case gc.TINT16<<16 | gc.TINT16, // same size - gc.TINT16<<16 | gc.TUINT16, - gc.TUINT16<<16 | gc.TINT16, - gc.TUINT16<<16 | gc.TUINT16, - gc.TINT32<<16 | gc.TINT16, - // truncate - gc.TUINT32<<16 | gc.TINT16, - gc.TINT64<<16 | gc.TINT16, - gc.TUINT64<<16 | gc.TINT16, - gc.TINT32<<16 | gc.TUINT16, - gc.TUINT32<<16 | gc.TUINT16, - gc.TINT64<<16 | gc.TUINT16, - gc.TUINT64<<16 | gc.TUINT16: - a = x86.AMOVW - - case gc.TINT32<<16 | gc.TINT32, // same size - gc.TINT32<<16 | gc.TUINT32, - gc.TUINT32<<16 | gc.TINT32, - gc.TUINT32<<16 | gc.TUINT32: - a = x86.AMOVL - - case gc.TINT64<<16 | gc.TINT32, // truncate - gc.TUINT64<<16 | gc.TINT32, - gc.TINT64<<16 | gc.TUINT32, - gc.TUINT64<<16 | gc.TUINT32: - a = x86.AMOVQL - - case gc.TINT64<<16 | gc.TINT64, // same size - gc.TINT64<<16 | gc.TUINT64, - gc.TUINT64<<16 | gc.TINT64, - gc.TUINT64<<16 | gc.TUINT64: - a = x86.AMOVQ - - /* - * integer up-conversions - */ - case gc.TINT8<<16 | gc.TINT16, // sign extend int8 - gc.TINT8<<16 | gc.TUINT16: - a = x86.AMOVBWSX - - goto rdst - - case gc.TINT8<<16 | gc.TINT32, - gc.TINT8<<16 | gc.TUINT32: - a = x86.AMOVBLSX - goto rdst - - case gc.TINT8<<16 | gc.TINT64, - gc.TINT8<<16 | gc.TUINT64: - a = x86.AMOVBQSX - goto rdst - - case gc.TUINT8<<16 | gc.TINT16, // zero extend uint8 - gc.TUINT8<<16 | gc.TUINT16: - a = x86.AMOVBWZX - - goto rdst - - case gc.TUINT8<<16 | gc.TINT32, - gc.TUINT8<<16 | gc.TUINT32: - a = x86.AMOVBLZX - goto rdst - - case gc.TUINT8<<16 | gc.TINT64, - gc.TUINT8<<16 | gc.TUINT64: - a = x86.AMOVBQZX - goto rdst - - case gc.TINT16<<16 | gc.TINT32, // sign extend int16 - gc.TINT16<<16 | gc.TUINT32: - a = x86.AMOVWLSX - - goto rdst - - case gc.TINT16<<16 | gc.TINT64, - gc.TINT16<<16 | gc.TUINT64: - a = x86.AMOVWQSX - goto rdst - - case gc.TUINT16<<16 | gc.TINT32, // zero extend uint16 - gc.TUINT16<<16 | gc.TUINT32: - a = x86.AMOVWLZX - - goto rdst - - case gc.TUINT16<<16 | gc.TINT64, - gc.TUINT16<<16 | gc.TUINT64: - a = x86.AMOVWQZX - goto rdst - - case gc.TINT32<<16 | gc.TINT64, // sign extend int32 - gc.TINT32<<16 | gc.TUINT64: - a = x86.AMOVLQSX - - goto rdst - - // AMOVL into a register zeros the top of the register, - // so this is not always necessary, but if we rely on AMOVL - // the optimizer is almost certain to screw with us. - case gc.TUINT32<<16 | gc.TINT64, // zero extend uint32 - gc.TUINT32<<16 | gc.TUINT64: - a = x86.AMOVLQZX - - goto rdst - - /* - * float to integer - */ - case gc.TFLOAT32<<16 | gc.TINT32: - a = x86.ACVTTSS2SL - - goto rdst - - case gc.TFLOAT64<<16 | gc.TINT32: - a = x86.ACVTTSD2SL - goto rdst - - case gc.TFLOAT32<<16 | gc.TINT64: - a = x86.ACVTTSS2SQ - goto rdst - - case gc.TFLOAT64<<16 | gc.TINT64: - a = x86.ACVTTSD2SQ - goto rdst - - // convert via int32. - case gc.TFLOAT32<<16 | gc.TINT16, - gc.TFLOAT32<<16 | gc.TINT8, - gc.TFLOAT32<<16 | gc.TUINT16, - gc.TFLOAT32<<16 | gc.TUINT8, - gc.TFLOAT64<<16 | gc.TINT16, - gc.TFLOAT64<<16 | gc.TINT8, - gc.TFLOAT64<<16 | gc.TUINT16, - gc.TFLOAT64<<16 | gc.TUINT8: - cvt = gc.Types[gc.TINT32] - - goto hard - - // convert via int64. - case gc.TFLOAT32<<16 | gc.TUINT32, - gc.TFLOAT64<<16 | gc.TUINT32: - cvt = gc.Types[gc.TINT64] - - goto hard - - // algorithm is: - // if small enough, use native float64 -> int64 conversion. - // otherwise, subtract 2^63, convert, and add it back. - case gc.TFLOAT32<<16 | gc.TUINT64, - gc.TFLOAT64<<16 | gc.TUINT64: - a := x86.ACVTTSS2SQ - - if ft == gc.TFLOAT64 { - a = x86.ACVTTSD2SQ - } - bignodes() - var r1 gc.Node - gc.Regalloc(&r1, gc.Types[ft], nil) - var r2 gc.Node - gc.Regalloc(&r2, gc.Types[tt], t) - var r3 gc.Node - gc.Regalloc(&r3, gc.Types[ft], nil) - var r4 gc.Node - gc.Regalloc(&r4, gc.Types[tt], nil) - gins(optoas(gc.OAS, f.Type), f, &r1) - gins(optoas(gc.OCMP, f.Type), &bigf, &r1) - p1 := gc.Gbranch(optoas(gc.OLE, f.Type), nil, +1) - gins(a, &r1, &r2) - p2 := gc.Gbranch(obj.AJMP, nil, 0) - gc.Patch(p1, gc.Pc) - gins(optoas(gc.OAS, f.Type), &bigf, &r3) - gins(optoas(gc.OSUB, f.Type), &r3, &r1) - gins(a, &r1, &r2) - gins(x86.AMOVQ, &bigi, &r4) - gins(x86.AXORQ, &r4, &r2) - gc.Patch(p2, gc.Pc) - gmove(&r2, t) - gc.Regfree(&r4) - gc.Regfree(&r3) - gc.Regfree(&r2) - gc.Regfree(&r1) - return - - /* - * integer to float - */ - case gc.TINT32<<16 | gc.TFLOAT32: - a = x86.ACVTSL2SS - - goto rdst - - case gc.TINT32<<16 | gc.TFLOAT64: - a = x86.ACVTSL2SD - goto rdst - - case gc.TINT64<<16 | gc.TFLOAT32: - a = x86.ACVTSQ2SS - goto rdst - - case gc.TINT64<<16 | gc.TFLOAT64: - a = x86.ACVTSQ2SD - goto rdst - - // convert via int32 - case gc.TINT16<<16 | gc.TFLOAT32, - gc.TINT16<<16 | gc.TFLOAT64, - gc.TINT8<<16 | gc.TFLOAT32, - gc.TINT8<<16 | gc.TFLOAT64, - gc.TUINT16<<16 | gc.TFLOAT32, - gc.TUINT16<<16 | gc.TFLOAT64, - gc.TUINT8<<16 | gc.TFLOAT32, - gc.TUINT8<<16 | gc.TFLOAT64: - cvt = gc.Types[gc.TINT32] - - goto hard - - // convert via int64. - case gc.TUINT32<<16 | gc.TFLOAT32, - gc.TUINT32<<16 | gc.TFLOAT64: - cvt = gc.Types[gc.TINT64] - - goto hard - - // algorithm is: - // if small enough, use native int64 -> uint64 conversion. - // otherwise, halve (rounding to odd?), convert, and double. - case gc.TUINT64<<16 | gc.TFLOAT32, - gc.TUINT64<<16 | gc.TFLOAT64: - a := x86.ACVTSQ2SS - - if tt == gc.TFLOAT64 { - a = x86.ACVTSQ2SD - } - var zero gc.Node - gc.Nodconst(&zero, gc.Types[gc.TUINT64], 0) - var one gc.Node - gc.Nodconst(&one, gc.Types[gc.TUINT64], 1) - var r1 gc.Node - gc.Regalloc(&r1, f.Type, f) - var r2 gc.Node - gc.Regalloc(&r2, t.Type, t) - var r3 gc.Node - gc.Regalloc(&r3, f.Type, nil) - var r4 gc.Node - gc.Regalloc(&r4, f.Type, nil) - gmove(f, &r1) - gins(x86.ACMPQ, &r1, &zero) - p1 := gc.Gbranch(x86.AJLT, nil, +1) - gins(a, &r1, &r2) - p2 := gc.Gbranch(obj.AJMP, nil, 0) - gc.Patch(p1, gc.Pc) - gmove(&r1, &r3) - gins(x86.ASHRQ, &one, &r3) - gmove(&r1, &r4) - gins(x86.AANDL, &one, &r4) - gins(x86.AORQ, &r4, &r3) - gins(a, &r3, &r2) - gins(optoas(gc.OADD, t.Type), &r2, &r2) - gc.Patch(p2, gc.Pc) - gmove(&r2, t) - gc.Regfree(&r4) - gc.Regfree(&r3) - gc.Regfree(&r2) - gc.Regfree(&r1) - return - - /* - * float to float - */ - case gc.TFLOAT32<<16 | gc.TFLOAT32: - a = x86.AMOVSS - - case gc.TFLOAT64<<16 | gc.TFLOAT64: - a = x86.AMOVSD - - case gc.TFLOAT32<<16 | gc.TFLOAT64: - a = x86.ACVTSS2SD - goto rdst - - case gc.TFLOAT64<<16 | gc.TFLOAT32: - a = x86.ACVTSD2SS - goto rdst - } - - gins(a, f, t) - return - - // requires register destination -rdst: - { - var r1 gc.Node - gc.Regalloc(&r1, t.Type, t) - - gins(a, f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - } - - // requires register intermediate -hard: - var r1 gc.Node - gc.Regalloc(&r1, cvt, t) - - gmove(f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return -} - -func samaddr(f *gc.Node, t *gc.Node) bool { - if f.Op != t.Op { - return false - } - - switch f.Op { - case gc.OREGISTER: - if f.Reg != t.Reg { - break - } - return true - } - - return false -} - -/* - * generate one instruction: - * as f, t - */ -func gins(as int, f *gc.Node, t *gc.Node) *obj.Prog { - // Node nod; - - // if(f != N && f->op == OINDEX) { - // gc.Regalloc(&nod, ®node, Z); - // v = constnode.vconst; - // gc.Cgen(f->right, &nod); - // constnode.vconst = v; - // idx.reg = nod.reg; - // gc.Regfree(&nod); - // } - // if(t != N && t->op == OINDEX) { - // gc.Regalloc(&nod, ®node, Z); - // v = constnode.vconst; - // gc.Cgen(t->right, &nod); - // constnode.vconst = v; - // idx.reg = nod.reg; - // gc.Regfree(&nod); - // } - - if f != nil && f.Op == gc.OADDR && (as == x86.AMOVL || as == x86.AMOVQ) { - // Turn MOVL $xxx into LEAL xxx. - // These should be equivalent but most of the backend - // only expects to see LEAL, because that's what we had - // historically generated. Various hidden assumptions are baked in by now. - if as == x86.AMOVL { - as = x86.ALEAL - } else { - as = x86.ALEAQ - } - f = f.Left - } - - switch as { - case x86.AMOVB, - x86.AMOVW, - x86.AMOVL, - x86.AMOVQ, - x86.AMOVSS, - x86.AMOVSD: - if f != nil && t != nil && samaddr(f, t) { - return nil - } - - case x86.ALEAQ: - if f != nil && gc.Isconst(f, gc.CTNIL) { - gc.Fatal("gins LEAQ nil %v", f.Type) - } - } - - p := gc.Prog(as) - gc.Naddr(&p.From, f) - gc.Naddr(&p.To, t) - - if gc.Debug['g'] != 0 { - fmt.Printf("%v\n", p) - } - - w := int32(0) - switch as { - case x86.AMOVB: - w = 1 - - case x86.AMOVW: - w = 2 - - case x86.AMOVL: - w = 4 - - case x86.AMOVQ: - w = 8 - } - - if w != 0 && ((f != nil && p.From.Width < int64(w)) || (t != nil && p.To.Width > int64(w))) { - gc.Dump("f", f) - gc.Dump("t", t) - gc.Fatal("bad width: %v (%d, %d)\n", p, p.From.Width, p.To.Width) - } - - if p.To.Type == obj.TYPE_ADDR && w > 0 { - gc.Fatal("bad use of addr: %v", p) - } - - return p -} - -func ginsnop() { - // This is actually not the x86 NOP anymore, - // but at the point where it gets used, AX is dead - // so it's okay if we lose the high bits. - var reg gc.Node - gc.Nodreg(®, gc.Types[gc.TINT], x86.REG_AX) - gins(x86.AXCHGL, ®, ®) -} - -/* - * return Axxx for Oxxx on type t. - */ -func optoas(op int, t *gc.Type) int { - if t == nil { - gc.Fatal("optoas: t is nil") - } - - a := obj.AXXX - switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) { - default: - gc.Fatal("optoas: no entry %v-%v", gc.Oconv(int(op), 0), t) - - case gc.OADDR<<16 | gc.TPTR32: - a = x86.ALEAL - - case gc.OADDR<<16 | gc.TPTR64: - a = x86.ALEAQ - - case gc.OEQ<<16 | gc.TBOOL, - gc.OEQ<<16 | gc.TINT8, - gc.OEQ<<16 | gc.TUINT8, - gc.OEQ<<16 | gc.TINT16, - gc.OEQ<<16 | gc.TUINT16, - gc.OEQ<<16 | gc.TINT32, - gc.OEQ<<16 | gc.TUINT32, - gc.OEQ<<16 | gc.TINT64, - gc.OEQ<<16 | gc.TUINT64, - gc.OEQ<<16 | gc.TPTR32, - gc.OEQ<<16 | gc.TPTR64, - gc.OEQ<<16 | gc.TFLOAT32, - gc.OEQ<<16 | gc.TFLOAT64: - a = x86.AJEQ - - case gc.ONE<<16 | gc.TBOOL, - gc.ONE<<16 | gc.TINT8, - gc.ONE<<16 | gc.TUINT8, - gc.ONE<<16 | gc.TINT16, - gc.ONE<<16 | gc.TUINT16, - gc.ONE<<16 | gc.TINT32, - gc.ONE<<16 | gc.TUINT32, - gc.ONE<<16 | gc.TINT64, - gc.ONE<<16 | gc.TUINT64, - gc.ONE<<16 | gc.TPTR32, - gc.ONE<<16 | gc.TPTR64, - gc.ONE<<16 | gc.TFLOAT32, - gc.ONE<<16 | gc.TFLOAT64: - a = x86.AJNE - - case gc.OPS<<16 | gc.TBOOL, - gc.OPS<<16 | gc.TINT8, - gc.OPS<<16 | gc.TUINT8, - gc.OPS<<16 | gc.TINT16, - gc.OPS<<16 | gc.TUINT16, - gc.OPS<<16 | gc.TINT32, - gc.OPS<<16 | gc.TUINT32, - gc.OPS<<16 | gc.TINT64, - gc.OPS<<16 | gc.TUINT64, - gc.OPS<<16 | gc.TPTR32, - gc.OPS<<16 | gc.TPTR64, - gc.OPS<<16 | gc.TFLOAT32, - gc.OPS<<16 | gc.TFLOAT64: - a = x86.AJPS - - case gc.OPC<<16 | gc.TBOOL, - gc.OPC<<16 | gc.TINT8, - gc.OPC<<16 | gc.TUINT8, - gc.OPC<<16 | gc.TINT16, - gc.OPC<<16 | gc.TUINT16, - gc.OPC<<16 | gc.TINT32, - gc.OPC<<16 | gc.TUINT32, - gc.OPC<<16 | gc.TINT64, - gc.OPC<<16 | gc.TUINT64, - gc.OPC<<16 | gc.TPTR32, - gc.OPC<<16 | gc.TPTR64, - gc.OPC<<16 | gc.TFLOAT32, - gc.OPC<<16 | gc.TFLOAT64: - a = x86.AJPC - - case gc.OLT<<16 | gc.TINT8, - gc.OLT<<16 | gc.TINT16, - gc.OLT<<16 | gc.TINT32, - gc.OLT<<16 | gc.TINT64: - a = x86.AJLT - - case gc.OLT<<16 | gc.TUINT8, - gc.OLT<<16 | gc.TUINT16, - gc.OLT<<16 | gc.TUINT32, - gc.OLT<<16 | gc.TUINT64: - a = x86.AJCS - - case gc.OLE<<16 | gc.TINT8, - gc.OLE<<16 | gc.TINT16, - gc.OLE<<16 | gc.TINT32, - gc.OLE<<16 | gc.TINT64: - a = x86.AJLE - - case gc.OLE<<16 | gc.TUINT8, - gc.OLE<<16 | gc.TUINT16, - gc.OLE<<16 | gc.TUINT32, - gc.OLE<<16 | gc.TUINT64: - a = x86.AJLS - - case gc.OGT<<16 | gc.TINT8, - gc.OGT<<16 | gc.TINT16, - gc.OGT<<16 | gc.TINT32, - gc.OGT<<16 | gc.TINT64: - a = x86.AJGT - - case gc.OGT<<16 | gc.TUINT8, - gc.OGT<<16 | gc.TUINT16, - gc.OGT<<16 | gc.TUINT32, - gc.OGT<<16 | gc.TUINT64, - gc.OLT<<16 | gc.TFLOAT32, - gc.OLT<<16 | gc.TFLOAT64: - a = x86.AJHI - - case gc.OGE<<16 | gc.TINT8, - gc.OGE<<16 | gc.TINT16, - gc.OGE<<16 | gc.TINT32, - gc.OGE<<16 | gc.TINT64: - a = x86.AJGE - - case gc.OGE<<16 | gc.TUINT8, - gc.OGE<<16 | gc.TUINT16, - gc.OGE<<16 | gc.TUINT32, - gc.OGE<<16 | gc.TUINT64, - gc.OLE<<16 | gc.TFLOAT32, - gc.OLE<<16 | gc.TFLOAT64: - a = x86.AJCC - - case gc.OCMP<<16 | gc.TBOOL, - gc.OCMP<<16 | gc.TINT8, - gc.OCMP<<16 | gc.TUINT8: - a = x86.ACMPB - - case gc.OCMP<<16 | gc.TINT16, - gc.OCMP<<16 | gc.TUINT16: - a = x86.ACMPW - - case gc.OCMP<<16 | gc.TINT32, - gc.OCMP<<16 | gc.TUINT32, - gc.OCMP<<16 | gc.TPTR32: - a = x86.ACMPL - - case gc.OCMP<<16 | gc.TINT64, - gc.OCMP<<16 | gc.TUINT64, - gc.OCMP<<16 | gc.TPTR64: - a = x86.ACMPQ - - case gc.OCMP<<16 | gc.TFLOAT32: - a = x86.AUCOMISS - - case gc.OCMP<<16 | gc.TFLOAT64: - a = x86.AUCOMISD - - case gc.OAS<<16 | gc.TBOOL, - gc.OAS<<16 | gc.TINT8, - gc.OAS<<16 | gc.TUINT8: - a = x86.AMOVB - - case gc.OAS<<16 | gc.TINT16, - gc.OAS<<16 | gc.TUINT16: - a = x86.AMOVW - - case gc.OAS<<16 | gc.TINT32, - gc.OAS<<16 | gc.TUINT32, - gc.OAS<<16 | gc.TPTR32: - a = x86.AMOVL - - case gc.OAS<<16 | gc.TINT64, - gc.OAS<<16 | gc.TUINT64, - gc.OAS<<16 | gc.TPTR64: - a = x86.AMOVQ - - case gc.OAS<<16 | gc.TFLOAT32: - a = x86.AMOVSS - - case gc.OAS<<16 | gc.TFLOAT64: - a = x86.AMOVSD - - case gc.OADD<<16 | gc.TINT8, - gc.OADD<<16 | gc.TUINT8: - a = x86.AADDB - - case gc.OADD<<16 | gc.TINT16, - gc.OADD<<16 | gc.TUINT16: - a = x86.AADDW - - case gc.OADD<<16 | gc.TINT32, - gc.OADD<<16 | gc.TUINT32, - gc.OADD<<16 | gc.TPTR32: - a = x86.AADDL - - case gc.OADD<<16 | gc.TINT64, - gc.OADD<<16 | gc.TUINT64, - gc.OADD<<16 | gc.TPTR64: - a = x86.AADDQ - - case gc.OADD<<16 | gc.TFLOAT32: - a = x86.AADDSS - - case gc.OADD<<16 | gc.TFLOAT64: - a = x86.AADDSD - - case gc.OSUB<<16 | gc.TINT8, - gc.OSUB<<16 | gc.TUINT8: - a = x86.ASUBB - - case gc.OSUB<<16 | gc.TINT16, - gc.OSUB<<16 | gc.TUINT16: - a = x86.ASUBW - - case gc.OSUB<<16 | gc.TINT32, - gc.OSUB<<16 | gc.TUINT32, - gc.OSUB<<16 | gc.TPTR32: - a = x86.ASUBL - - case gc.OSUB<<16 | gc.TINT64, - gc.OSUB<<16 | gc.TUINT64, - gc.OSUB<<16 | gc.TPTR64: - a = x86.ASUBQ - - case gc.OSUB<<16 | gc.TFLOAT32: - a = x86.ASUBSS - - case gc.OSUB<<16 | gc.TFLOAT64: - a = x86.ASUBSD - - case gc.OINC<<16 | gc.TINT8, - gc.OINC<<16 | gc.TUINT8: - a = x86.AINCB - - case gc.OINC<<16 | gc.TINT16, - gc.OINC<<16 | gc.TUINT16: - a = x86.AINCW - - case gc.OINC<<16 | gc.TINT32, - gc.OINC<<16 | gc.TUINT32, - gc.OINC<<16 | gc.TPTR32: - a = x86.AINCL - - case gc.OINC<<16 | gc.TINT64, - gc.OINC<<16 | gc.TUINT64, - gc.OINC<<16 | gc.TPTR64: - a = x86.AINCQ - - case gc.ODEC<<16 | gc.TINT8, - gc.ODEC<<16 | gc.TUINT8: - a = x86.ADECB - - case gc.ODEC<<16 | gc.TINT16, - gc.ODEC<<16 | gc.TUINT16: - a = x86.ADECW - - case gc.ODEC<<16 | gc.TINT32, - gc.ODEC<<16 | gc.TUINT32, - gc.ODEC<<16 | gc.TPTR32: - a = x86.ADECL - - case gc.ODEC<<16 | gc.TINT64, - gc.ODEC<<16 | gc.TUINT64, - gc.ODEC<<16 | gc.TPTR64: - a = x86.ADECQ - - case gc.OMINUS<<16 | gc.TINT8, - gc.OMINUS<<16 | gc.TUINT8: - a = x86.ANEGB - - case gc.OMINUS<<16 | gc.TINT16, - gc.OMINUS<<16 | gc.TUINT16: - a = x86.ANEGW - - case gc.OMINUS<<16 | gc.TINT32, - gc.OMINUS<<16 | gc.TUINT32, - gc.OMINUS<<16 | gc.TPTR32: - a = x86.ANEGL - - case gc.OMINUS<<16 | gc.TINT64, - gc.OMINUS<<16 | gc.TUINT64, - gc.OMINUS<<16 | gc.TPTR64: - a = x86.ANEGQ - - case gc.OAND<<16 | gc.TBOOL, - gc.OAND<<16 | gc.TINT8, - gc.OAND<<16 | gc.TUINT8: - a = x86.AANDB - - case gc.OAND<<16 | gc.TINT16, - gc.OAND<<16 | gc.TUINT16: - a = x86.AANDW - - case gc.OAND<<16 | gc.TINT32, - gc.OAND<<16 | gc.TUINT32, - gc.OAND<<16 | gc.TPTR32: - a = x86.AANDL - - case gc.OAND<<16 | gc.TINT64, - gc.OAND<<16 | gc.TUINT64, - gc.OAND<<16 | gc.TPTR64: - a = x86.AANDQ - - case gc.OOR<<16 | gc.TBOOL, - gc.OOR<<16 | gc.TINT8, - gc.OOR<<16 | gc.TUINT8: - a = x86.AORB - - case gc.OOR<<16 | gc.TINT16, - gc.OOR<<16 | gc.TUINT16: - a = x86.AORW - - case gc.OOR<<16 | gc.TINT32, - gc.OOR<<16 | gc.TUINT32, - gc.OOR<<16 | gc.TPTR32: - a = x86.AORL - - case gc.OOR<<16 | gc.TINT64, - gc.OOR<<16 | gc.TUINT64, - gc.OOR<<16 | gc.TPTR64: - a = x86.AORQ - - case gc.OXOR<<16 | gc.TINT8, - gc.OXOR<<16 | gc.TUINT8: - a = x86.AXORB - - case gc.OXOR<<16 | gc.TINT16, - gc.OXOR<<16 | gc.TUINT16: - a = x86.AXORW - - case gc.OXOR<<16 | gc.TINT32, - gc.OXOR<<16 | gc.TUINT32, - gc.OXOR<<16 | gc.TPTR32: - a = x86.AXORL - - case gc.OXOR<<16 | gc.TINT64, - gc.OXOR<<16 | gc.TUINT64, - gc.OXOR<<16 | gc.TPTR64: - a = x86.AXORQ - - case gc.OLROT<<16 | gc.TINT8, - gc.OLROT<<16 | gc.TUINT8: - a = x86.AROLB - - case gc.OLROT<<16 | gc.TINT16, - gc.OLROT<<16 | gc.TUINT16: - a = x86.AROLW - - case gc.OLROT<<16 | gc.TINT32, - gc.OLROT<<16 | gc.TUINT32, - gc.OLROT<<16 | gc.TPTR32: - a = x86.AROLL - - case gc.OLROT<<16 | gc.TINT64, - gc.OLROT<<16 | gc.TUINT64, - gc.OLROT<<16 | gc.TPTR64: - a = x86.AROLQ - - case gc.OLSH<<16 | gc.TINT8, - gc.OLSH<<16 | gc.TUINT8: - a = x86.ASHLB - - case gc.OLSH<<16 | gc.TINT16, - gc.OLSH<<16 | gc.TUINT16: - a = x86.ASHLW - - case gc.OLSH<<16 | gc.TINT32, - gc.OLSH<<16 | gc.TUINT32, - gc.OLSH<<16 | gc.TPTR32: - a = x86.ASHLL - - case gc.OLSH<<16 | gc.TINT64, - gc.OLSH<<16 | gc.TUINT64, - gc.OLSH<<16 | gc.TPTR64: - a = x86.ASHLQ - - case gc.ORSH<<16 | gc.TUINT8: - a = x86.ASHRB - - case gc.ORSH<<16 | gc.TUINT16: - a = x86.ASHRW - - case gc.ORSH<<16 | gc.TUINT32, - gc.ORSH<<16 | gc.TPTR32: - a = x86.ASHRL - - case gc.ORSH<<16 | gc.TUINT64, - gc.ORSH<<16 | gc.TPTR64: - a = x86.ASHRQ - - case gc.ORSH<<16 | gc.TINT8: - a = x86.ASARB - - case gc.ORSH<<16 | gc.TINT16: - a = x86.ASARW - - case gc.ORSH<<16 | gc.TINT32: - a = x86.ASARL - - case gc.ORSH<<16 | gc.TINT64: - a = x86.ASARQ - - case gc.ORROTC<<16 | gc.TINT8, - gc.ORROTC<<16 | gc.TUINT8: - a = x86.ARCRB - - case gc.ORROTC<<16 | gc.TINT16, - gc.ORROTC<<16 | gc.TUINT16: - a = x86.ARCRW - - case gc.ORROTC<<16 | gc.TINT32, - gc.ORROTC<<16 | gc.TUINT32: - a = x86.ARCRL - - case gc.ORROTC<<16 | gc.TINT64, - gc.ORROTC<<16 | gc.TUINT64: - a = x86.ARCRQ - - case gc.OHMUL<<16 | gc.TINT8, - gc.OMUL<<16 | gc.TINT8, - gc.OMUL<<16 | gc.TUINT8: - a = x86.AIMULB - - case gc.OHMUL<<16 | gc.TINT16, - gc.OMUL<<16 | gc.TINT16, - gc.OMUL<<16 | gc.TUINT16: - a = x86.AIMULW - - case gc.OHMUL<<16 | gc.TINT32, - gc.OMUL<<16 | gc.TINT32, - gc.OMUL<<16 | gc.TUINT32, - gc.OMUL<<16 | gc.TPTR32: - a = x86.AIMULL - - case gc.OHMUL<<16 | gc.TINT64, - gc.OMUL<<16 | gc.TINT64, - gc.OMUL<<16 | gc.TUINT64, - gc.OMUL<<16 | gc.TPTR64: - a = x86.AIMULQ - - case gc.OHMUL<<16 | gc.TUINT8: - a = x86.AMULB - - case gc.OHMUL<<16 | gc.TUINT16: - a = x86.AMULW - - case gc.OHMUL<<16 | gc.TUINT32, - gc.OHMUL<<16 | gc.TPTR32: - a = x86.AMULL - - case gc.OHMUL<<16 | gc.TUINT64, - gc.OHMUL<<16 | gc.TPTR64: - a = x86.AMULQ - - case gc.OMUL<<16 | gc.TFLOAT32: - a = x86.AMULSS - - case gc.OMUL<<16 | gc.TFLOAT64: - a = x86.AMULSD - - case gc.ODIV<<16 | gc.TINT8, - gc.OMOD<<16 | gc.TINT8: - a = x86.AIDIVB - - case gc.ODIV<<16 | gc.TUINT8, - gc.OMOD<<16 | gc.TUINT8: - a = x86.ADIVB - - case gc.ODIV<<16 | gc.TINT16, - gc.OMOD<<16 | gc.TINT16: - a = x86.AIDIVW - - case gc.ODIV<<16 | gc.TUINT16, - gc.OMOD<<16 | gc.TUINT16: - a = x86.ADIVW - - case gc.ODIV<<16 | gc.TINT32, - gc.OMOD<<16 | gc.TINT32: - a = x86.AIDIVL - - case gc.ODIV<<16 | gc.TUINT32, - gc.ODIV<<16 | gc.TPTR32, - gc.OMOD<<16 | gc.TUINT32, - gc.OMOD<<16 | gc.TPTR32: - a = x86.ADIVL - - case gc.ODIV<<16 | gc.TINT64, - gc.OMOD<<16 | gc.TINT64: - a = x86.AIDIVQ - - case gc.ODIV<<16 | gc.TUINT64, - gc.ODIV<<16 | gc.TPTR64, - gc.OMOD<<16 | gc.TUINT64, - gc.OMOD<<16 | gc.TPTR64: - a = x86.ADIVQ - - case gc.OEXTEND<<16 | gc.TINT16: - a = x86.ACWD - - case gc.OEXTEND<<16 | gc.TINT32: - a = x86.ACDQ - - case gc.OEXTEND<<16 | gc.TINT64: - a = x86.ACQO - - case gc.ODIV<<16 | gc.TFLOAT32: - a = x86.ADIVSS - - case gc.ODIV<<16 | gc.TFLOAT64: - a = x86.ADIVSD - - case gc.OSQRT<<16 | gc.TFLOAT64: - a = x86.ASQRTSD - } - - return a -} - -// jmptoset returns ASETxx for AJxx. -func jmptoset(jmp int) int { - switch jmp { - case x86.AJEQ: - return x86.ASETEQ - case x86.AJNE: - return x86.ASETNE - case x86.AJLT: - return x86.ASETLT - case x86.AJCS: - return x86.ASETCS - case x86.AJLE: - return x86.ASETLE - case x86.AJLS: - return x86.ASETLS - case x86.AJGT: - return x86.ASETGT - case x86.AJHI: - return x86.ASETHI - case x86.AJGE: - return x86.ASETGE - case x86.AJCC: - return x86.ASETCC - case x86.AJMI: - return x86.ASETMI - case x86.AJOC: - return x86.ASETOC - case x86.AJOS: - return x86.ASETOS - case x86.AJPC: - return x86.ASETPC - case x86.AJPL: - return x86.ASETPL - case x86.AJPS: - return x86.ASETPS - } - gc.Fatal("jmptoset: no entry for %v", gc.Oconv(jmp, 0)) - panic("unreachable") -} - -const ( - ODynam = 1 << 0 - OAddable = 1 << 1 -) - -var clean [20]gc.Node - -var cleani int = 0 - -func sudoclean() { - if clean[cleani-1].Op != gc.OEMPTY { - gc.Regfree(&clean[cleani-1]) - } - if clean[cleani-2].Op != gc.OEMPTY { - gc.Regfree(&clean[cleani-2]) - } - cleani -= 2 -} - -/* - * generate code to compute address of n, - * a reference to a (perhaps nested) field inside - * an array or struct. - * return 0 on failure, 1 on success. - * on success, leaves usable address in a. - * - * caller is responsible for calling sudoclean - * after successful sudoaddable, - * to release the register used for a. - */ -func sudoaddable(as int, n *gc.Node, a *obj.Addr) bool { - if n.Type == nil { - return false - } - - *a = obj.Addr{} - - switch n.Op { - case gc.OLITERAL: - if !gc.Isconst(n, gc.CTINT) { - break - } - v := n.Int() - if v >= 32000 || v <= -32000 { - break - } - switch as { - default: - return false - - case x86.AADDB, - x86.AADDW, - x86.AADDL, - x86.AADDQ, - x86.ASUBB, - x86.ASUBW, - x86.ASUBL, - x86.ASUBQ, - x86.AANDB, - x86.AANDW, - x86.AANDL, - x86.AANDQ, - x86.AORB, - x86.AORW, - x86.AORL, - x86.AORQ, - x86.AXORB, - x86.AXORW, - x86.AXORL, - x86.AXORQ, - x86.AINCB, - x86.AINCW, - x86.AINCL, - x86.AINCQ, - x86.ADECB, - x86.ADECW, - x86.ADECL, - x86.ADECQ, - x86.AMOVB, - x86.AMOVW, - x86.AMOVL, - x86.AMOVQ: - break - } - - cleani += 2 - reg := &clean[cleani-1] - reg1 := &clean[cleani-2] - reg.Op = gc.OEMPTY - reg1.Op = gc.OEMPTY - gc.Naddr(a, n) - return true - - case gc.ODOT, - gc.ODOTPTR: - cleani += 2 - reg := &clean[cleani-1] - reg1 := &clean[cleani-2] - reg.Op = gc.OEMPTY - reg1.Op = gc.OEMPTY - var nn *gc.Node - var oary [10]int64 - o := gc.Dotoffset(n, oary[:], &nn) - if nn == nil { - sudoclean() - return false - } - - if nn.Addable && o == 1 && oary[0] >= 0 { - // directly addressable set of DOTs - n1 := *nn - - n1.Type = n.Type - n1.Xoffset += oary[0] - gc.Naddr(a, &n1) - return true - } - - gc.Regalloc(reg, gc.Types[gc.Tptr], nil) - n1 := *reg - n1.Op = gc.OINDREG - if oary[0] >= 0 { - gc.Agen(nn, reg) - n1.Xoffset = oary[0] - } else { - gc.Cgen(nn, reg) - gc.Cgen_checknil(reg) - n1.Xoffset = -(oary[0] + 1) - } - - for i := 1; i < o; i++ { - if oary[i] >= 0 { - gc.Fatal("can't happen") - } - gins(movptr, &n1, reg) - gc.Cgen_checknil(reg) - n1.Xoffset = -(oary[i] + 1) - } - - a.Type = obj.TYPE_NONE - a.Index = obj.TYPE_NONE - gc.Fixlargeoffset(&n1) - gc.Naddr(a, &n1) - return true - - case gc.OINDEX: - return false - } - - return false -} diff --git a/src/cmd/6g/peep.go b/src/cmd/6g/peep.go deleted file mode 100644 index cd07199ed1..0000000000 --- a/src/cmd/6g/peep.go +++ /dev/null @@ -1,1038 +0,0 @@ -// Derived from Inferno utils/6c/peep.c -// http://code.google.com/p/inferno-os/source/browse/utils/6c/peep.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package main - -import ( - "cmd/internal/gc" - "cmd/internal/obj" - "cmd/internal/obj/x86" - "fmt" -) - -var gactive uint32 - -const ( - exregoffset = x86.REG_R15 -) - -// do we need the carry bit -func needc(p *obj.Prog) bool { - for p != nil { - flags := progcarryflags(p) - if flags&gc.UseCarry != 0 { - return true - } - if flags&(gc.SetCarry|gc.KillCarry) != 0 { - return false - } - p = p.Link - } - - return false -} - -func rnops(r *gc.Flow) *gc.Flow { - if r != nil { - var p *obj.Prog - var r1 *gc.Flow - for { - p = r.Prog - if p.As != obj.ANOP || p.From.Type != obj.TYPE_NONE || p.To.Type != obj.TYPE_NONE { - break - } - r1 = gc.Uniqs(r) - if r1 == nil { - break - } - r = r1 - } - } - - return r -} - -func peep(firstp *obj.Prog) { - g := (*gc.Graph)(gc.Flowstart(firstp, nil)) - if g == nil { - return - } - gactive = 0 - - // byte, word arithmetic elimination. - elimshortmov(g) - - // constant propagation - // find MOV $con,R followed by - // another MOV $con,R without - // setting R in the interim - var p *obj.Prog - for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { - p = r.Prog - switch p.As { - case x86.ALEAL, - x86.ALEAQ: - if regtyp(&p.To) { - if p.From.Sym != nil { - if p.From.Index == x86.REG_NONE { - conprop(r) - } - } - } - - case x86.AMOVB, - x86.AMOVW, - x86.AMOVL, - x86.AMOVQ, - x86.AMOVSS, - x86.AMOVSD: - if regtyp(&p.To) { - if p.From.Type == obj.TYPE_CONST || p.From.Type == obj.TYPE_FCONST { - conprop(r) - } - } - } - } - - var r *gc.Flow - var r1 *gc.Flow - var p1 *obj.Prog - var t int -loop1: - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - gc.Dumpit("loop1", g.Start, 0) - } - - t = 0 - for r = g.Start; r != nil; r = r.Link { - p = r.Prog - switch p.As { - case x86.AMOVL, - x86.AMOVQ, - x86.AMOVSS, - x86.AMOVSD: - if regtyp(&p.To) { - if regtyp(&p.From) { - if copyprop(g, r) { - excise(r) - t++ - } else if subprop(r) && copyprop(g, r) { - excise(r) - t++ - } - } - } - - case x86.AMOVBLZX, - x86.AMOVWLZX, - x86.AMOVBLSX, - x86.AMOVWLSX: - if regtyp(&p.To) { - r1 = rnops(gc.Uniqs(r)) - if r1 != nil { - p1 = r1.Prog - if p.As == p1.As && p.To.Type == p1.From.Type && p.To.Reg == p1.From.Reg { - p1.As = x86.AMOVL - t++ - } - } - } - - case x86.AMOVBQSX, - x86.AMOVBQZX, - x86.AMOVWQSX, - x86.AMOVWQZX, - x86.AMOVLQSX, - x86.AMOVLQZX, - x86.AMOVQL: - if regtyp(&p.To) { - r1 = rnops(gc.Uniqs(r)) - if r1 != nil { - p1 = r1.Prog - if p.As == p1.As && p.To.Type == p1.From.Type && p.To.Reg == p1.From.Reg { - p1.As = x86.AMOVQ - t++ - } - } - } - - case x86.AADDL, - x86.AADDQ, - x86.AADDW: - if p.From.Type != obj.TYPE_CONST || needc(p.Link) { - break - } - if p.From.Offset == -1 { - if p.As == x86.AADDQ { - p.As = x86.ADECQ - } else if p.As == x86.AADDL { - p.As = x86.ADECL - } else { - p.As = x86.ADECW - } - p.From = obj.Addr{} - break - } - - if p.From.Offset == 1 { - if p.As == x86.AADDQ { - p.As = x86.AINCQ - } else if p.As == x86.AADDL { - p.As = x86.AINCL - } else { - p.As = x86.AINCW - } - p.From = obj.Addr{} - break - } - - case x86.ASUBL, - x86.ASUBQ, - x86.ASUBW: - if p.From.Type != obj.TYPE_CONST || needc(p.Link) { - break - } - if p.From.Offset == -1 { - if p.As == x86.ASUBQ { - p.As = x86.AINCQ - } else if p.As == x86.ASUBL { - p.As = x86.AINCL - } else { - p.As = x86.AINCW - } - p.From = obj.Addr{} - break - } - - if p.From.Offset == 1 { - if p.As == x86.ASUBQ { - p.As = x86.ADECQ - } else if p.As == x86.ASUBL { - p.As = x86.ADECL - } else { - p.As = x86.ADECW - } - p.From = obj.Addr{} - break - } - } - } - - if t != 0 { - goto loop1 - } - - // MOVLQZX removal. - // The MOVLQZX exists to avoid being confused for a - // MOVL that is just copying 32-bit data around during - // copyprop. Now that copyprop is done, remov MOVLQZX R1, R2 - // if it is dominated by an earlier ADDL/MOVL/etc into R1 that - // will have already cleared the high bits. - // - // MOVSD removal. - // We never use packed registers, so a MOVSD between registers - // can be replaced by MOVAPD, which moves the pair of float64s - // instead of just the lower one. We only use the lower one, but - // the processor can do better if we do moves using both. - for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { - p = r.Prog - if p.As == x86.AMOVLQZX { - if regtyp(&p.From) { - if p.From.Type == p.To.Type && p.From.Reg == p.To.Reg { - if prevl(r, int(p.From.Reg)) { - excise(r) - } - } - } - } - - if p.As == x86.AMOVSD { - if regtyp(&p.From) { - if regtyp(&p.To) { - p.As = x86.AMOVAPD - } - } - } - } - - // load pipelining - // push any load from memory as early as possible - // to give it time to complete before use. - for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { - p = r.Prog - switch p.As { - case x86.AMOVB, - x86.AMOVW, - x86.AMOVL, - x86.AMOVQ, - x86.AMOVLQZX: - if regtyp(&p.To) && !regconsttyp(&p.From) { - pushback(r) - } - } - } - - gc.Flowend(g) -} - -func pushback(r0 *gc.Flow) { - var r *gc.Flow - var p *obj.Prog - - var b *gc.Flow - p0 := (*obj.Prog)(r0.Prog) - for r = gc.Uniqp(r0); r != nil && gc.Uniqs(r) != nil; r = gc.Uniqp(r) { - p = r.Prog - if p.As != obj.ANOP { - if !regconsttyp(&p.From) || !regtyp(&p.To) { - break - } - if copyu(p, &p0.To, nil) != 0 || copyu(p0, &p.To, nil) != 0 { - break - } - } - - if p.As == obj.ACALL { - break - } - b = r - } - - if b == nil { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("no pushback: %v\n", r0.Prog) - if r != nil { - fmt.Printf("\t%v [%v]\n", r.Prog, gc.Uniqs(r) != nil) - } - } - - return - } - - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("pushback\n") - for r := (*gc.Flow)(b); ; r = r.Link { - fmt.Printf("\t%v\n", r.Prog) - if r == r0 { - break - } - } - } - - t := obj.Prog(*r0.Prog) - for r = gc.Uniqp(r0); ; r = gc.Uniqp(r) { - p0 = r.Link.Prog - p = r.Prog - p0.As = p.As - p0.Lineno = p.Lineno - p0.From = p.From - p0.To = p.To - - if r == b { - break - } - } - - p0 = r.Prog - p0.As = t.As - p0.Lineno = t.Lineno - p0.From = t.From - p0.To = t.To - - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("\tafter\n") - for r := (*gc.Flow)(b); ; r = r.Link { - fmt.Printf("\t%v\n", r.Prog) - if r == r0 { - break - } - } - } -} - -func excise(r *gc.Flow) { - p := (*obj.Prog)(r.Prog) - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("%v ===delete===\n", p) - } - - obj.Nopout(p) - - gc.Ostats.Ndelmov++ -} - -func regtyp(a *obj.Addr) bool { - return a.Type == obj.TYPE_REG && (x86.REG_AX <= a.Reg && a.Reg <= x86.REG_R15 || x86.REG_X0 <= a.Reg && a.Reg <= x86.REG_X15) -} - -// movb elimination. -// movb is simulated by the linker -// when a register other than ax, bx, cx, dx -// is used, so rewrite to other instructions -// when possible. a movb into a register -// can smash the entire 32-bit register without -// causing any trouble. -// -// TODO: Using the Q forms here instead of the L forms -// seems unnecessary, and it makes the instructions longer. -func elimshortmov(g *gc.Graph) { - var p *obj.Prog - - for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { - p = r.Prog - if regtyp(&p.To) { - switch p.As { - case x86.AINCB, - x86.AINCW: - p.As = x86.AINCQ - - case x86.ADECB, - x86.ADECW: - p.As = x86.ADECQ - - case x86.ANEGB, - x86.ANEGW: - p.As = x86.ANEGQ - - case x86.ANOTB, - x86.ANOTW: - p.As = x86.ANOTQ - } - - if regtyp(&p.From) || p.From.Type == obj.TYPE_CONST { - // move or artihmetic into partial register. - // from another register or constant can be movl. - // we don't switch to 64-bit arithmetic if it can - // change how the carry bit is set (and the carry bit is needed). - switch p.As { - case x86.AMOVB, - x86.AMOVW: - p.As = x86.AMOVQ - - case x86.AADDB, - x86.AADDW: - if !needc(p.Link) { - p.As = x86.AADDQ - } - - case x86.ASUBB, - x86.ASUBW: - if !needc(p.Link) { - p.As = x86.ASUBQ - } - - case x86.AMULB, - x86.AMULW: - p.As = x86.AMULQ - - case x86.AIMULB, - x86.AIMULW: - p.As = x86.AIMULQ - - case x86.AANDB, - x86.AANDW: - p.As = x86.AANDQ - - case x86.AORB, - x86.AORW: - p.As = x86.AORQ - - case x86.AXORB, - x86.AXORW: - p.As = x86.AXORQ - - case x86.ASHLB, - x86.ASHLW: - p.As = x86.ASHLQ - } - } else if p.From.Type != obj.TYPE_REG { - // explicit zero extension, but don't - // do that if source is a byte register - // (only AH can occur and it's forbidden). - switch p.As { - case x86.AMOVB: - p.As = x86.AMOVBQZX - - case x86.AMOVW: - p.As = x86.AMOVWQZX - } - } - } - } -} - -// is 'a' a register or constant? -func regconsttyp(a *obj.Addr) bool { - if regtyp(a) { - return true - } - switch a.Type { - case obj.TYPE_CONST, - obj.TYPE_FCONST, - obj.TYPE_SCONST, - obj.TYPE_ADDR: // TODO(rsc): Not all TYPE_ADDRs are constants. - return true - } - - return false -} - -// is reg guaranteed to be truncated by a previous L instruction? -func prevl(r0 *gc.Flow, reg int) bool { - for r := (*gc.Flow)(gc.Uniqp(r0)); r != nil; r = gc.Uniqp(r) { - p := r.Prog - if p.To.Type == obj.TYPE_REG && int(p.To.Reg) == reg { - flags := progflags(p) - if flags&gc.RightWrite != 0 { - if flags&gc.SizeL != 0 { - return true - } - return false - } - } - } - - return false -} - -/* - * the idea is to substitute - * one register for another - * from one MOV to another - * MOV a, R0 - * ADD b, R0 / no use of R1 - * MOV R0, R1 - * would be converted to - * MOV a, R1 - * ADD b, R1 - * MOV R1, R0 - * hopefully, then the former or latter MOV - * will be eliminated by copy propagation. - */ -func subprop(r0 *gc.Flow) bool { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("subprop %v\n", r0.Prog) - } - p := (*obj.Prog)(r0.Prog) - v1 := (*obj.Addr)(&p.From) - if !regtyp(v1) { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("\tnot regtype %v; return 0\n", gc.Ctxt.Dconv(v1)) - } - return false - } - - v2 := (*obj.Addr)(&p.To) - if !regtyp(v2) { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("\tnot regtype %v; return 0\n", gc.Ctxt.Dconv(v2)) - } - return false - } - - for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("\t? %v\n", r.Prog) - } - if gc.Uniqs(r) == nil { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("\tno unique successor\n") - } - break - } - - p = r.Prog - if p.As == obj.AVARDEF || p.As == obj.AVARKILL { - continue - } - if p.Info.Flags&gc.Call != 0 { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("\tfound %v; return 0\n", p) - } - return false - } - - if p.Info.Reguse|p.Info.Regset != 0 { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("\tfound %v; return 0\n", p) - } - return false - } - - if (p.Info.Flags&gc.Move != 0) && (p.Info.Flags&(gc.SizeL|gc.SizeQ|gc.SizeF|gc.SizeD) != 0) && p.To.Type == v1.Type && p.To.Reg == v1.Reg { - copysub(&p.To, v1, v2, 1) - if gc.Debug['P'] != 0 { - fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog) - if p.From.Type == v2.Type && p.From.Reg == v2.Reg { - fmt.Printf(" excise") - } - fmt.Printf("\n") - } - - for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { - p = r.Prog - copysub(&p.From, v1, v2, 1) - copysub(&p.To, v1, v2, 1) - if gc.Debug['P'] != 0 { - fmt.Printf("%v\n", r.Prog) - } - } - - t := int(int(v1.Reg)) - v1.Reg = v2.Reg - v2.Reg = int16(t) - if gc.Debug['P'] != 0 { - fmt.Printf("%v last\n", r.Prog) - } - return true - } - - if copyau(&p.From, v2) || copyau(&p.To, v2) { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("\tcopyau %v failed\n", gc.Ctxt.Dconv(v2)) - } - break - } - - if copysub(&p.From, v1, v2, 0) != 0 || copysub(&p.To, v1, v2, 0) != 0 { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("\tcopysub failed\n") - } - break - } - } - - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("\tran off end; return 0\n") - } - return false -} - -/* - * The idea is to remove redundant copies. - * v1->v2 F=0 - * (use v2 s/v2/v1/)* - * set v1 F=1 - * use v2 return fail - * ----------------- - * v1->v2 F=0 - * (use v2 s/v2/v1/)* - * set v1 F=1 - * set v2 return success - */ -func copyprop(g *gc.Graph, r0 *gc.Flow) bool { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("copyprop %v\n", r0.Prog) - } - p := (*obj.Prog)(r0.Prog) - v1 := (*obj.Addr)(&p.From) - v2 := (*obj.Addr)(&p.To) - if copyas(v1, v2) { - return true - } - gactive++ - return copy1(v1, v2, r0.S1, 0) -} - -func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f int) bool { - if uint32(r.Active) == gactive { - if gc.Debug['P'] != 0 { - fmt.Printf("act set; return 1\n") - } - return true - } - - r.Active = int32(gactive) - if gc.Debug['P'] != 0 { - fmt.Printf("copy %v->%v f=%d\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), f) - } - var t int - var p *obj.Prog - for ; r != nil; r = r.S1 { - p = r.Prog - if gc.Debug['P'] != 0 { - fmt.Printf("%v", p) - } - if f == 0 && gc.Uniqp(r) == nil { - f = 1 - if gc.Debug['P'] != 0 { - fmt.Printf("; merge; f=%d", f) - } - } - - t = copyu(p, v2, nil) - switch t { - case 2: /* rar, can't split */ - if gc.Debug['P'] != 0 { - fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2)) - } - return false - - case 3: /* set */ - if gc.Debug['P'] != 0 { - fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2)) - } - return true - - case 1, /* used, substitute */ - 4: /* use and set */ - if f != 0 { - if gc.Debug['P'] == 0 { - return false - } - if t == 4 { - fmt.Printf("; %v used+set and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f) - } else { - fmt.Printf("; %v used and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f) - } - return false - } - - if copyu(p, v2, v1) != 0 { - if gc.Debug['P'] != 0 { - fmt.Printf("; sub fail; return 0\n") - } - return false - } - - if gc.Debug['P'] != 0 { - fmt.Printf("; sub %v/%v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1)) - } - if t == 4 { - if gc.Debug['P'] != 0 { - fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2)) - } - return true - } - } - - if f == 0 { - t = copyu(p, v1, nil) - if f == 0 && (t == 2 || t == 3 || t == 4) { - f = 1 - if gc.Debug['P'] != 0 { - fmt.Printf("; %v set and !f; f=%d", gc.Ctxt.Dconv(v1), f) - } - } - } - - if gc.Debug['P'] != 0 { - fmt.Printf("\n") - } - if r.S2 != nil { - if !copy1(v1, v2, r.S2, f) { - return false - } - } - } - - return true -} - -/* - * return - * 1 if v only used (and substitute), - * 2 if read-alter-rewrite - * 3 if set - * 4 if set and used - * 0 otherwise (not touched) - */ -func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int { - switch p.As { - case obj.AJMP: - if s != nil { - if copysub(&p.To, v, s, 1) != 0 { - return 1 - } - return 0 - } - - if copyau(&p.To, v) { - return 1 - } - return 0 - - case obj.ARET: - if s != nil { - return 1 - } - return 3 - - case obj.ACALL: - if x86.REGEXT != 0 /*TypeKind(100016)*/ && v.Type == obj.TYPE_REG && v.Reg <= x86.REGEXT && v.Reg > exregoffset { - return 2 - } - if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG { - return 2 - } - if v.Type == p.From.Type && v.Reg == p.From.Reg { - return 2 - } - - if s != nil { - if copysub(&p.To, v, s, 1) != 0 { - return 1 - } - return 0 - } - - if copyau(&p.To, v) { - return 4 - } - return 3 - - case obj.ATEXT: - if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG { - return 3 - } - return 0 - } - - if p.As == obj.AVARDEF || p.As == obj.AVARKILL { - return 0 - } - - if (p.Info.Reguse|p.Info.Regset)&RtoB(int(v.Reg)) != 0 { - return 2 - } - - if p.Info.Flags&gc.LeftAddr != 0 { - if copyas(&p.From, v) { - return 2 - } - } - - if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightRead|gc.RightWrite { - if copyas(&p.To, v) { - return 2 - } - } - - if p.Info.Flags&gc.RightWrite != 0 { - if copyas(&p.To, v) { - if s != nil { - return copysub(&p.From, v, s, 1) - } - if copyau(&p.From, v) { - return 4 - } - return 3 - } - } - - if p.Info.Flags&(gc.LeftAddr|gc.LeftRead|gc.LeftWrite|gc.RightAddr|gc.RightRead|gc.RightWrite) != 0 { - if s != nil { - if copysub(&p.From, v, s, 1) != 0 { - return 1 - } - return copysub(&p.To, v, s, 1) - } - - if copyau(&p.From, v) { - return 1 - } - if copyau(&p.To, v) { - return 1 - } - } - - return 0 -} - -/* - * direct reference, - * could be set/use depending on - * semantics - */ -func copyas(a *obj.Addr, v *obj.Addr) bool { - if x86.REG_AL <= a.Reg && a.Reg <= x86.REG_R15B { - gc.Fatal("use of byte register") - } - if x86.REG_AL <= v.Reg && v.Reg <= x86.REG_R15B { - gc.Fatal("use of byte register") - } - - if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg { - return false - } - if regtyp(v) { - return true - } - if v.Type == obj.TYPE_MEM && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) { - if v.Offset == a.Offset { - return true - } - } - return false -} - -func sameaddr(a *obj.Addr, v *obj.Addr) bool { - if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg { - return false - } - if regtyp(v) { - return true - } - if v.Type == obj.TYPE_MEM && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) { - if v.Offset == a.Offset { - return true - } - } - return false -} - -/* - * either direct or indirect - */ -func copyau(a *obj.Addr, v *obj.Addr) bool { - if copyas(a, v) { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("\tcopyau: copyas returned 1\n") - } - return true - } - - if regtyp(v) { - if a.Type == obj.TYPE_MEM && a.Reg == v.Reg { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("\tcopyau: found indir use - return 1\n") - } - return true - } - - if a.Index == v.Reg { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("\tcopyau: found index use - return 1\n") - } - return true - } - } - - return false -} - -/* - * substitute s for v in a - * return failure to substitute - */ -func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f int) int { - if copyas(a, v) { - reg := int(int(s.Reg)) - if reg >= x86.REG_AX && reg <= x86.REG_R15 || reg >= x86.REG_X0 && reg <= x86.REG_X0+15 { - if f != 0 { - a.Reg = int16(reg) - } - } - - return 0 - } - - if regtyp(v) { - reg := int(int(v.Reg)) - if a.Type == obj.TYPE_MEM && int(a.Reg) == reg { - if (s.Reg == x86.REG_BP || s.Reg == x86.REG_R13) && a.Index != x86.REG_NONE { - return 1 /* can't use BP-base with index */ - } - if f != 0 { - a.Reg = s.Reg - } - } - - // return 0; - if int(a.Index) == reg { - if f != 0 { - a.Index = s.Reg - } - return 0 - } - - return 0 - } - - return 0 -} - -func conprop(r0 *gc.Flow) { - var p *obj.Prog - var t int - - p0 := (*obj.Prog)(r0.Prog) - v0 := (*obj.Addr)(&p0.To) - r := (*gc.Flow)(r0) - -loop: - r = gc.Uniqs(r) - if r == nil || r == r0 { - return - } - if gc.Uniqp(r) == nil { - return - } - - p = r.Prog - t = copyu(p, v0, nil) - switch t { - case 0, // miss - 1: // use - goto loop - - case 2, // rar - 4: // use and set - break - - case 3: // set - if p.As == p0.As { - if p.From.Type == p0.From.Type { - if p.From.Reg == p0.From.Reg { - if p.From.Node == p0.From.Node { - if p.From.Offset == p0.From.Offset { - if p.From.Scale == p0.From.Scale { - if p.From.Type == obj.TYPE_FCONST && p.From.Val.(float64) == p0.From.Val.(float64) { - if p.From.Index == p0.From.Index { - excise(r) - goto loop - } - } - } - } - } - } - } - } - } -} - -func smallindir(a *obj.Addr, reg *obj.Addr) bool { - return regtyp(reg) && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && a.Index == x86.REG_NONE && 0 <= a.Offset && a.Offset < 4096 -} - -func stackaddr(a *obj.Addr) bool { - return a.Type == obj.TYPE_REG && a.Reg == x86.REG_SP -} diff --git a/src/cmd/6g/prog.go b/src/cmd/6g/prog.go deleted file mode 100644 index 5f604742c3..0000000000 --- a/src/cmd/6g/prog.go +++ /dev/null @@ -1,308 +0,0 @@ -// Copyright 2013 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/gc" - "cmd/internal/obj" - "cmd/internal/obj/x86" -) - -const ( - LeftRdwr uint32 = gc.LeftRead | gc.LeftWrite - RightRdwr uint32 = gc.RightRead | gc.RightWrite -) - -// This table gives the basic information about instruction -// generated by the compiler and processed in the optimizer. -// See opt.h for bit definitions. -// -// Instructions not generated need not be listed. -// As an exception to that rule, we typically write down all the -// size variants of an operation even if we just use a subset. -// -// The table is formatted for 8-space tabs. -var progtable = [x86.ALAST]obj.ProgInfo{ - obj.ATYPE: {gc.Pseudo | gc.Skip, 0, 0, 0}, - obj.ATEXT: {gc.Pseudo, 0, 0, 0}, - obj.AFUNCDATA: {gc.Pseudo, 0, 0, 0}, - obj.APCDATA: {gc.Pseudo, 0, 0, 0}, - obj.AUNDEF: {gc.Break, 0, 0, 0}, - obj.AUSEFIELD: {gc.OK, 0, 0, 0}, - obj.ACHECKNIL: {gc.LeftRead, 0, 0, 0}, - obj.AVARDEF: {gc.Pseudo | gc.RightWrite, 0, 0, 0}, - obj.AVARKILL: {gc.Pseudo | gc.RightWrite, 0, 0, 0}, - - // NOP is an internal no-op that also stands - // for USED and SET annotations, not the Intel opcode. - obj.ANOP: {gc.LeftRead | gc.RightWrite, 0, 0, 0}, - x86.AADCL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry | gc.UseCarry, 0, 0, 0}, - x86.AADCQ: {gc.SizeQ | gc.LeftRead | RightRdwr | gc.SetCarry | gc.UseCarry, 0, 0, 0}, - x86.AADCW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry | gc.UseCarry, 0, 0, 0}, - x86.AADDB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.AADDL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.AADDW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.AADDQ: {gc.SizeQ | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.AADDSD: {gc.SizeD | gc.LeftRead | RightRdwr, 0, 0, 0}, - x86.AADDSS: {gc.SizeF | gc.LeftRead | RightRdwr, 0, 0, 0}, - x86.AANDB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.AANDL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.AANDQ: {gc.SizeQ | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.AANDW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - obj.ACALL: {gc.RightAddr | gc.Call | gc.KillCarry, 0, 0, 0}, - x86.ACDQ: {gc.OK, AX, AX | DX, 0}, - x86.ACQO: {gc.OK, AX, AX | DX, 0}, - x86.ACWD: {gc.OK, AX, AX | DX, 0}, - x86.ACLD: {gc.OK, 0, 0, 0}, - x86.ASTD: {gc.OK, 0, 0, 0}, - x86.ACMPB: {gc.SizeB | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, - x86.ACMPL: {gc.SizeL | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, - x86.ACMPQ: {gc.SizeQ | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, - x86.ACMPW: {gc.SizeW | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, - x86.ACOMISD: {gc.SizeD | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, - x86.ACOMISS: {gc.SizeF | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, - x86.ACVTSD2SL: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.ACVTSD2SQ: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.ACVTSD2SS: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.ACVTSL2SD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.ACVTSL2SS: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.ACVTSQ2SD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.ACVTSQ2SS: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.ACVTSS2SD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.ACVTSS2SL: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.ACVTSS2SQ: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.ACVTTSD2SL: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.ACVTTSD2SQ: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.ACVTTSS2SL: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.ACVTTSS2SQ: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.ADECB: {gc.SizeB | RightRdwr, 0, 0, 0}, - x86.ADECL: {gc.SizeL | RightRdwr, 0, 0, 0}, - x86.ADECQ: {gc.SizeQ | RightRdwr, 0, 0, 0}, - x86.ADECW: {gc.SizeW | RightRdwr, 0, 0, 0}, - x86.ADIVB: {gc.SizeB | gc.LeftRead | gc.SetCarry, AX, AX, 0}, - x86.ADIVL: {gc.SizeL | gc.LeftRead | gc.SetCarry, AX | DX, AX | DX, 0}, - x86.ADIVQ: {gc.SizeQ | gc.LeftRead | gc.SetCarry, AX | DX, AX | DX, 0}, - x86.ADIVW: {gc.SizeW | gc.LeftRead | gc.SetCarry, AX | DX, AX | DX, 0}, - x86.ADIVSD: {gc.SizeD | gc.LeftRead | RightRdwr, 0, 0, 0}, - x86.ADIVSS: {gc.SizeF | gc.LeftRead | RightRdwr, 0, 0, 0}, - x86.AIDIVB: {gc.SizeB | gc.LeftRead | gc.SetCarry, AX, AX, 0}, - x86.AIDIVL: {gc.SizeL | gc.LeftRead | gc.SetCarry, AX | DX, AX | DX, 0}, - x86.AIDIVQ: {gc.SizeQ | gc.LeftRead | gc.SetCarry, AX | DX, AX | DX, 0}, - x86.AIDIVW: {gc.SizeW | gc.LeftRead | gc.SetCarry, AX | DX, AX | DX, 0}, - x86.AIMULB: {gc.SizeB | gc.LeftRead | gc.SetCarry, AX, AX, 0}, - x86.AIMULL: {gc.SizeL | gc.LeftRead | gc.ImulAXDX | gc.SetCarry, 0, 0, 0}, - x86.AIMULQ: {gc.SizeQ | gc.LeftRead | gc.ImulAXDX | gc.SetCarry, 0, 0, 0}, - x86.AIMULW: {gc.SizeW | gc.LeftRead | gc.ImulAXDX | gc.SetCarry, 0, 0, 0}, - x86.AINCB: {gc.SizeB | RightRdwr, 0, 0, 0}, - x86.AINCL: {gc.SizeL | RightRdwr, 0, 0, 0}, - x86.AINCQ: {gc.SizeQ | RightRdwr, 0, 0, 0}, - x86.AINCW: {gc.SizeW | RightRdwr, 0, 0, 0}, - x86.AJCC: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - x86.AJCS: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - x86.AJEQ: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - x86.AJGE: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - x86.AJGT: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - x86.AJHI: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - x86.AJLE: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - x86.AJLS: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - x86.AJLT: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - x86.AJMI: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - x86.AJNE: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - x86.AJOC: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - x86.AJOS: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - x86.AJPC: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - x86.AJPL: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - x86.AJPS: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - obj.AJMP: {gc.Jump | gc.Break | gc.KillCarry, 0, 0, 0}, - x86.ALEAL: {gc.LeftAddr | gc.RightWrite, 0, 0, 0}, - x86.ALEAQ: {gc.LeftAddr | gc.RightWrite, 0, 0, 0}, - x86.AMOVBLSX: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.AMOVBLZX: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.AMOVBQSX: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.AMOVBQZX: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.AMOVBWSX: {gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.AMOVBWZX: {gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.AMOVLQSX: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.AMOVLQZX: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.AMOVWLSX: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.AMOVWLZX: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.AMOVWQSX: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.AMOVWQZX: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.AMOVQL: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.AMOVB: {gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, - x86.AMOVL: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, - x86.AMOVQ: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, - x86.AMOVW: {gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, - x86.AMOVSB: {gc.OK, DI | SI, DI | SI, 0}, - x86.AMOVSL: {gc.OK, DI | SI, DI | SI, 0}, - x86.AMOVSQ: {gc.OK, DI | SI, DI | SI, 0}, - x86.AMOVSW: {gc.OK, DI | SI, DI | SI, 0}, - obj.ADUFFCOPY: {gc.OK, DI | SI, DI | SI | CX, 0}, - x86.AMOVSD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, - x86.AMOVSS: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, - - // We use MOVAPD as a faster synonym for MOVSD. - x86.AMOVAPD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, - x86.AMULB: {gc.SizeB | gc.LeftRead | gc.SetCarry, AX, AX, 0}, - x86.AMULL: {gc.SizeL | gc.LeftRead | gc.SetCarry, AX, AX | DX, 0}, - x86.AMULQ: {gc.SizeQ | gc.LeftRead | gc.SetCarry, AX, AX | DX, 0}, - x86.AMULW: {gc.SizeW | gc.LeftRead | gc.SetCarry, AX, AX | DX, 0}, - x86.AMULSD: {gc.SizeD | gc.LeftRead | RightRdwr, 0, 0, 0}, - x86.AMULSS: {gc.SizeF | gc.LeftRead | RightRdwr, 0, 0, 0}, - x86.ANEGB: {gc.SizeB | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.ANEGL: {gc.SizeL | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.ANEGQ: {gc.SizeQ | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.ANEGW: {gc.SizeW | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.ANOTB: {gc.SizeB | RightRdwr, 0, 0, 0}, - x86.ANOTL: {gc.SizeL | RightRdwr, 0, 0, 0}, - x86.ANOTQ: {gc.SizeQ | RightRdwr, 0, 0, 0}, - x86.ANOTW: {gc.SizeW | RightRdwr, 0, 0, 0}, - x86.AORB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.AORL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.AORQ: {gc.SizeQ | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.AORW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.APOPQ: {gc.SizeQ | gc.RightWrite, 0, 0, 0}, - x86.APUSHQ: {gc.SizeQ | gc.LeftRead, 0, 0, 0}, - x86.ARCLB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry, 0, 0, 0}, - x86.ARCLL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry, 0, 0, 0}, - x86.ARCLQ: {gc.SizeQ | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry, 0, 0, 0}, - x86.ARCLW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry, 0, 0, 0}, - x86.ARCRB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry, 0, 0, 0}, - x86.ARCRL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry, 0, 0, 0}, - x86.ARCRQ: {gc.SizeQ | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry, 0, 0, 0}, - x86.ARCRW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry, 0, 0, 0}, - x86.AREP: {gc.OK, CX, CX, 0}, - x86.AREPN: {gc.OK, CX, CX, 0}, - obj.ARET: {gc.Break | gc.KillCarry, 0, 0, 0}, - x86.AROLB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.AROLL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.AROLQ: {gc.SizeQ | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.AROLW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ARORB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ARORL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ARORQ: {gc.SizeQ | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ARORW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ASALB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ASALL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ASALQ: {gc.SizeQ | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ASALW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ASARB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ASARL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ASARQ: {gc.SizeQ | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ASARW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ASBBB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.SetCarry | gc.UseCarry, 0, 0, 0}, - x86.ASBBL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry | gc.UseCarry, 0, 0, 0}, - x86.ASBBQ: {gc.SizeQ | gc.LeftRead | RightRdwr | gc.SetCarry | gc.UseCarry, 0, 0, 0}, - x86.ASBBW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry | gc.UseCarry, 0, 0, 0}, - x86.ASETCC: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, - x86.ASETCS: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, - x86.ASETEQ: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, - x86.ASETGE: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, - x86.ASETGT: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, - x86.ASETHI: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, - x86.ASETLE: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, - x86.ASETLS: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, - x86.ASETLT: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, - x86.ASETMI: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, - x86.ASETNE: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, - x86.ASETOC: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, - x86.ASETOS: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, - x86.ASETPC: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, - x86.ASETPL: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, - x86.ASETPS: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, - x86.ASHLB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ASHLL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ASHLQ: {gc.SizeQ | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ASHLW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ASHRB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ASHRL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ASHRQ: {gc.SizeQ | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ASHRW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ASQRTSD: {gc.SizeD | gc.LeftRead | RightRdwr, 0, 0, 0}, - x86.ASTOSB: {gc.OK, AX | DI, DI, 0}, - x86.ASTOSL: {gc.OK, AX | DI, DI, 0}, - x86.ASTOSQ: {gc.OK, AX | DI, DI, 0}, - x86.ASTOSW: {gc.OK, AX | DI, DI, 0}, - obj.ADUFFZERO: {gc.OK, AX | DI, DI, 0}, - x86.ASUBB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.ASUBL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.ASUBQ: {gc.SizeQ | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.ASUBW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.ASUBSD: {gc.SizeD | gc.LeftRead | RightRdwr, 0, 0, 0}, - x86.ASUBSS: {gc.SizeF | gc.LeftRead | RightRdwr, 0, 0, 0}, - x86.ATESTB: {gc.SizeB | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, - x86.ATESTL: {gc.SizeL | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, - x86.ATESTQ: {gc.SizeQ | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, - x86.ATESTW: {gc.SizeW | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, - x86.AUCOMISD: {gc.SizeD | gc.LeftRead | gc.RightRead, 0, 0, 0}, - x86.AUCOMISS: {gc.SizeF | gc.LeftRead | gc.RightRead, 0, 0, 0}, - x86.AXCHGB: {gc.SizeB | LeftRdwr | RightRdwr, 0, 0, 0}, - x86.AXCHGL: {gc.SizeL | LeftRdwr | RightRdwr, 0, 0, 0}, - x86.AXCHGQ: {gc.SizeQ | LeftRdwr | RightRdwr, 0, 0, 0}, - x86.AXCHGW: {gc.SizeW | LeftRdwr | RightRdwr, 0, 0, 0}, - x86.AXORB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.AXORL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.AXORQ: {gc.SizeQ | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.AXORW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, -} - -func progflags(p *obj.Prog) uint32 { - flags := progtable[p.As].Flags - if flags&gc.ImulAXDX != 0 && p.To.Type != obj.TYPE_NONE { - flags |= RightRdwr - } - return flags -} - -func progcarryflags(p *obj.Prog) uint32 { - return progtable[p.As].Flags -} - -func proginfo(p *obj.Prog) { - info := &p.Info - *info = progtable[p.As] - if info.Flags == 0 { - gc.Fatal("unknown instruction %v", p) - } - - if (info.Flags&gc.ShiftCX != 0) && p.From.Type != obj.TYPE_CONST { - info.Reguse |= CX - } - - if info.Flags&gc.ImulAXDX != 0 { - if p.To.Type == obj.TYPE_NONE { - info.Reguse |= AX - info.Regset |= AX | DX - } else { - info.Flags |= RightRdwr - } - } - - // Addressing makes some registers used. - if p.From.Type == obj.TYPE_MEM && p.From.Name == obj.NAME_NONE { - info.Regindex |= RtoB(int(p.From.Reg)) - } - if p.From.Index != x86.REG_NONE { - info.Regindex |= RtoB(int(p.From.Index)) - } - if p.To.Type == obj.TYPE_MEM && p.To.Name == obj.NAME_NONE { - info.Regindex |= RtoB(int(p.To.Reg)) - } - if p.To.Index != x86.REG_NONE { - info.Regindex |= RtoB(int(p.To.Index)) - } - if gc.Ctxt.Flag_dynlink { - // When -dynlink is passed, many operations on external names (and - // also calling duffzero/duffcopy) use R15 as a scratch register. - if p.As == x86.ALEAQ || info.Flags == gc.Pseudo || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP { - return - } - if p.As == obj.ADUFFZERO || p.As == obj.ADUFFCOPY || (p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local) || (p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local) { - info.Reguse |= R15 - info.Regset |= R15 - return - } - } -} diff --git a/src/cmd/6g/reg.go b/src/cmd/6g/reg.go deleted file mode 100644 index cab07b5b4e..0000000000 --- a/src/cmd/6g/reg.go +++ /dev/null @@ -1,154 +0,0 @@ -// Derived from Inferno utils/6c/reg.c -// http://code.google.com/p/inferno-os/source/browse/utils/6c/reg.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package main - -import ( - "cmd/internal/gc" - "cmd/internal/obj" - "cmd/internal/obj/x86" -) - -const ( - NREGVAR = 32 -) - -var reg [x86.MAXREG]uint8 - -var regname = []string{ - ".AX", - ".CX", - ".DX", - ".BX", - ".SP", - ".BP", - ".SI", - ".DI", - ".R8", - ".R9", - ".R10", - ".R11", - ".R12", - ".R13", - ".R14", - ".R15", - ".X0", - ".X1", - ".X2", - ".X3", - ".X4", - ".X5", - ".X6", - ".X7", - ".X8", - ".X9", - ".X10", - ".X11", - ".X12", - ".X13", - ".X14", - ".X15", -} - -func regnames(n *int) []string { - *n = NREGVAR - return regname -} - -func excludedregs() uint64 { - return RtoB(x86.REG_SP) -} - -func doregbits(r int) uint64 { - b := uint64(0) - if r >= x86.REG_AX && r <= x86.REG_R15 { - b |= RtoB(r) - } else if r >= x86.REG_AL && r <= x86.REG_R15B { - b |= RtoB(r - x86.REG_AL + x86.REG_AX) - } else if r >= x86.REG_AH && r <= x86.REG_BH { - b |= RtoB(r - x86.REG_AH + x86.REG_AX) - } else if r >= x86.REG_X0 && r <= x86.REG_X0+15 { - b |= FtoB(r) - } - return b -} - -// For ProgInfo. -const ( - AX = 1 << (x86.REG_AX - x86.REG_AX) - BX = 1 << (x86.REG_BX - x86.REG_AX) - CX = 1 << (x86.REG_CX - x86.REG_AX) - DX = 1 << (x86.REG_DX - x86.REG_AX) - DI = 1 << (x86.REG_DI - x86.REG_AX) - SI = 1 << (x86.REG_SI - x86.REG_AX) - R15 = 1 << (x86.REG_R15 - x86.REG_AX) -) - -func RtoB(r int) uint64 { - if r < x86.REG_AX || r > x86.REG_R15 { - return 0 - } - return 1 << uint(r-x86.REG_AX) -} - -func BtoR(b uint64) int { - b &= 0xffff - if gc.Nacl { - b &^= (1<<(x86.REG_BP-x86.REG_AX) | 1<<(x86.REG_R15-x86.REG_AX)) - } else if obj.Framepointer_enabled != 0 { - // BP is part of the calling convention if framepointer_enabled. - b &^= (1 << (x86.REG_BP - x86.REG_AX)) - } - if b == 0 { - return 0 - } - return gc.Bitno(b) + x86.REG_AX -} - -/* - * bit reg - * 16 X0 - * ... - * 31 X15 - */ -func FtoB(f int) uint64 { - if f < x86.REG_X0 || f > x86.REG_X15 { - return 0 - } - return 1 << uint(f-x86.REG_X0+16) -} - -func BtoF(b uint64) int { - b &= 0xFFFF0000 - if b == 0 { - return 0 - } - return gc.Bitno(b) - 16 + x86.REG_X0 -} diff --git a/src/cmd/6l/asm.go b/src/cmd/6l/asm.go deleted file mode 100644 index 02b4c7cdd2..0000000000 --- a/src/cmd/6l/asm.go +++ /dev/null @@ -1,817 +0,0 @@ -// Inferno utils/6l/asm.c -// http://code.google.com/p/inferno-os/source/browse/utils/6l/asm.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package main - -import ( - "cmd/internal/ld" - "cmd/internal/obj" - "debug/elf" - "fmt" - "log" -) - -func PADDR(x uint32) uint32 { - return x &^ 0x80000000 -} - -var zeroes string - -func Addcall(ctxt *ld.Link, s *ld.LSym, t *ld.LSym) int64 { - s.Reachable = true - i := s.Size - s.Size += 4 - ld.Symgrow(ctxt, s, s.Size) - r := ld.Addrel(s) - r.Sym = t - r.Off = int32(i) - r.Type = obj.R_CALL - r.Siz = 4 - return i + int64(r.Siz) -} - -func gentext() { - if !ld.DynlinkingGo() { - return - } - addmoduledata := ld.Linklookup(ld.Ctxt, "runtime.addmoduledata", 0) - if addmoduledata.Type == obj.STEXT { - // we're linking a module containing the runtime -> no need for - // an init function - return - } - addmoduledata.Reachable = true - initfunc := ld.Linklookup(ld.Ctxt, "go.link.addmoduledata", 0) - initfunc.Type = obj.STEXT - initfunc.Local = true - initfunc.Reachable = true - o := func(op ...uint8) { - for _, op1 := range op { - ld.Adduint8(ld.Ctxt, initfunc, op1) - } - } - // 0000000000000000 : - // 0: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 7 - // 3: R_X86_64_PC32 runtime.firstmoduledata-0x4 - o(0x48, 0x8d, 0x3d) - ld.Addpcrelplus(ld.Ctxt, initfunc, ld.Linklookup(ld.Ctxt, "runtime.firstmoduledata", 0), 0) - // 7: e8 00 00 00 00 callq c - // 8: R_X86_64_PLT32 runtime.addmoduledata-0x4 - o(0xe8) - Addcall(ld.Ctxt, initfunc, addmoduledata) - // c: c3 retq - o(0xc3) - if ld.Ctxt.Etextp != nil { - ld.Ctxt.Etextp.Next = initfunc - } else { - ld.Ctxt.Textp = initfunc - } - ld.Ctxt.Etextp = initfunc - initarray_entry := ld.Linklookup(ld.Ctxt, "go.link.addmoduledatainit", 0) - initarray_entry.Reachable = true - initarray_entry.Local = true - initarray_entry.Type = obj.SINITARR - ld.Addaddr(ld.Ctxt, initarray_entry, initfunc) -} - -func adddynrela(rela *ld.LSym, s *ld.LSym, r *ld.Reloc) { - ld.Addaddrplus(ld.Ctxt, rela, s, int64(r.Off)) - ld.Adduint64(ld.Ctxt, rela, ld.R_X86_64_RELATIVE) - ld.Addaddrplus(ld.Ctxt, rela, r.Sym, r.Add) // Addend -} - -func adddynrel(s *ld.LSym, r *ld.Reloc) { - targ := r.Sym - ld.Ctxt.Cursym = s - - switch r.Type { - default: - if r.Type >= 256 { - ld.Diag("unexpected relocation type %d", r.Type) - return - } - - // Handle relocations found in ELF object files. - case 256 + ld.R_X86_64_PC32: - if targ.Type == obj.SDYNIMPORT { - ld.Diag("unexpected R_X86_64_PC32 relocation for dynamic symbol %s", targ.Name) - } - if targ.Type == 0 || targ.Type == obj.SXREF { - ld.Diag("unknown symbol %s in pcrel", targ.Name) - } - r.Type = obj.R_PCREL - r.Add += 4 - return - - case 256 + ld.R_X86_64_PLT32: - r.Type = obj.R_PCREL - r.Add += 4 - if targ.Type == obj.SDYNIMPORT { - addpltsym(targ) - r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) - r.Add += int64(targ.Plt) - } - - return - - case 256 + ld.R_X86_64_GOTPCREL: - if targ.Type != obj.SDYNIMPORT { - // have symbol - if r.Off >= 2 && s.P[r.Off-2] == 0x8b { - // turn MOVQ of GOT entry into LEAQ of symbol itself - s.P[r.Off-2] = 0x8d - - r.Type = obj.R_PCREL - r.Add += 4 - return - } - } - - // fall back to using GOT and hope for the best (CMOV*) - // TODO: just needs relocation, no need to put in .dynsym - addgotsym(targ) - - r.Type = obj.R_PCREL - r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0) - r.Add += 4 - r.Add += int64(targ.Got) - return - - case 256 + ld.R_X86_64_64: - if targ.Type == obj.SDYNIMPORT { - ld.Diag("unexpected R_X86_64_64 relocation for dynamic symbol %s", targ.Name) - } - r.Type = obj.R_ADDR - return - - // Handle relocations found in Mach-O object files. - case 512 + ld.MACHO_X86_64_RELOC_UNSIGNED*2 + 0, - 512 + ld.MACHO_X86_64_RELOC_SIGNED*2 + 0, - 512 + ld.MACHO_X86_64_RELOC_BRANCH*2 + 0: - // TODO: What is the difference between all these? - r.Type = obj.R_ADDR - - if targ.Type == obj.SDYNIMPORT { - ld.Diag("unexpected reloc for dynamic symbol %s", targ.Name) - } - return - - case 512 + ld.MACHO_X86_64_RELOC_BRANCH*2 + 1: - if targ.Type == obj.SDYNIMPORT { - addpltsym(targ) - r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) - r.Add = int64(targ.Plt) - r.Type = obj.R_PCREL - return - } - fallthrough - - // fall through - case 512 + ld.MACHO_X86_64_RELOC_UNSIGNED*2 + 1, - 512 + ld.MACHO_X86_64_RELOC_SIGNED*2 + 1, - 512 + ld.MACHO_X86_64_RELOC_SIGNED_1*2 + 1, - 512 + ld.MACHO_X86_64_RELOC_SIGNED_2*2 + 1, - 512 + ld.MACHO_X86_64_RELOC_SIGNED_4*2 + 1: - r.Type = obj.R_PCREL - - if targ.Type == obj.SDYNIMPORT { - ld.Diag("unexpected pc-relative reloc for dynamic symbol %s", targ.Name) - } - return - - case 512 + ld.MACHO_X86_64_RELOC_GOT_LOAD*2 + 1: - if targ.Type != obj.SDYNIMPORT { - // have symbol - // turn MOVQ of GOT entry into LEAQ of symbol itself - if r.Off < 2 || s.P[r.Off-2] != 0x8b { - ld.Diag("unexpected GOT_LOAD reloc for non-dynamic symbol %s", targ.Name) - return - } - - s.P[r.Off-2] = 0x8d - r.Type = obj.R_PCREL - return - } - fallthrough - - // fall through - case 512 + ld.MACHO_X86_64_RELOC_GOT*2 + 1: - if targ.Type != obj.SDYNIMPORT { - ld.Diag("unexpected GOT reloc for non-dynamic symbol %s", targ.Name) - } - addgotsym(targ) - r.Type = obj.R_PCREL - r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0) - r.Add += int64(targ.Got) - return - } - - // Handle references to ELF symbols from our own object files. - if targ.Type != obj.SDYNIMPORT { - return - } - - switch r.Type { - case obj.R_CALL, - obj.R_PCREL: - if ld.HEADTYPE == obj.Hwindows { - // nothing to do, the relocation will be laid out in pereloc1 - return - } else { - // for both ELF and Mach-O - addpltsym(targ) - r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) - r.Add = int64(targ.Plt) - return - } - - case obj.R_ADDR: - if s.Type == obj.STEXT && ld.Iself { - if ld.HEADTYPE == obj.Hsolaris { - addpltsym(targ) - r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) - r.Add += int64(targ.Plt) - return - } - // The code is asking for the address of an external - // function. We provide it with the address of the - // correspondent GOT symbol. - addgotsym(targ) - - r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0) - r.Add += int64(targ.Got) - return - } - - if s.Type != obj.SDATA { - break - } - if ld.Iself { - 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 { - ld.Adduint64(ld.Ctxt, rela, ld.ELF64_R_INFO(uint32(targ.Dynid), ld.R_X86_64_64)) - } else { - ld.Adduint64(ld.Ctxt, rela, ld.ELF64_R_INFO(uint32(targ.Dynid), ld.R_X86_64_32)) - } - ld.Adduint64(ld.Ctxt, rela, uint64(r.Add)) - r.Type = 256 // ignore during relocsym - return - } - - if ld.HEADTYPE == obj.Hdarwin && s.Size == int64(ld.Thearch.Ptrsize) && r.Off == 0 { - // Mach-O relocations are a royal pain to lay out. - // They use a compact stateful bytecode representation - // that is too much bother to deal with. - // Instead, interpret the C declaration - // void *_Cvar_stderr = &stderr; - // as making _Cvar_stderr the name of a GOT entry - // for stderr. This is separate from the usual GOT entry, - // 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. - ld.Adddynsym(ld.Ctxt, targ) - - got := ld.Linklookup(ld.Ctxt, ".got", 0) - s.Type = got.Type | obj.SSUB - s.Outer = got - s.Sub = got.Sub - got.Sub = s - s.Value = got.Size - ld.Adduint64(ld.Ctxt, got, 0) - ld.Adduint32(ld.Ctxt, ld.Linklookup(ld.Ctxt, ".linkedit.got", 0), uint32(targ.Dynid)) - r.Type = 256 // ignore during relocsym - return - } - - if ld.HEADTYPE == obj.Hwindows { - // nothing to do, the relocation will be laid out in pereloc1 - return - } - } - - ld.Ctxt.Cursym = s - ld.Diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ.Name, r.Type, targ.Type) -} - -func elfreloc1(r *ld.Reloc, sectoff int64) int { - ld.Thearch.Vput(uint64(sectoff)) - - elfsym := r.Xsym.Elfsym - switch r.Type { - default: - return -1 - - case obj.R_ADDR: - if r.Siz == 4 { - ld.Thearch.Vput(ld.R_X86_64_32 | uint64(elfsym)<<32) - } else if r.Siz == 8 { - ld.Thearch.Vput(ld.R_X86_64_64 | uint64(elfsym)<<32) - } else { - return -1 - } - - case obj.R_TLS_LE: - if r.Siz == 4 { - ld.Thearch.Vput(ld.R_X86_64_TPOFF32 | uint64(elfsym)<<32) - } else { - return -1 - } - - case obj.R_TLS_IE: - if r.Siz == 4 { - ld.Thearch.Vput(ld.R_X86_64_GOTTPOFF | uint64(elfsym)<<32) - } else { - return -1 - } - - case obj.R_CALL: - if r.Siz == 4 { - if r.Xsym.Type == obj.SDYNIMPORT { - if ld.DynlinkingGo() { - ld.Thearch.Vput(ld.R_X86_64_PLT32 | uint64(elfsym)<<32) - } else { - ld.Thearch.Vput(ld.R_X86_64_GOTPCREL | uint64(elfsym)<<32) - } - } else { - ld.Thearch.Vput(ld.R_X86_64_PC32 | uint64(elfsym)<<32) - } - } else { - return -1 - } - - case obj.R_PCREL: - if r.Siz == 4 { - if r.Xsym.Type == obj.SDYNIMPORT && r.Xsym.ElfType == elf.STT_FUNC { - ld.Thearch.Vput(ld.R_X86_64_PLT32 | uint64(elfsym)<<32) - } else { - ld.Thearch.Vput(ld.R_X86_64_PC32 | uint64(elfsym)<<32) - } - } else { - return -1 - } - - case obj.R_GOTPCREL: - if r.Siz == 4 { - ld.Thearch.Vput(ld.R_X86_64_GOTPCREL | uint64(elfsym)<<32) - } else { - return -1 - } - } - - ld.Thearch.Vput(uint64(r.Xadd)) - return 0 -} - -func machoreloc1(r *ld.Reloc, sectoff int64) int { - var v uint32 - - rs := r.Xsym - - if rs.Type == obj.SHOSTOBJ || r.Type == obj.R_PCREL { - if rs.Dynid < 0 { - ld.Diag("reloc %d to non-macho symbol %s type=%d", r.Type, rs.Name, rs.Type) - return -1 - } - - v = uint32(rs.Dynid) - v |= 1 << 27 // external relocation - } else { - v = uint32((rs.Sect.(*ld.Section)).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) - return -1 - } - } - - switch r.Type { - default: - return -1 - - case obj.R_ADDR: - v |= ld.MACHO_X86_64_RELOC_UNSIGNED << 28 - - case obj.R_CALL: - v |= 1 << 24 // pc-relative bit - v |= ld.MACHO_X86_64_RELOC_BRANCH << 28 - - // NOTE: Only works with 'external' relocation. Forced above. - case obj.R_PCREL: - v |= 1 << 24 // pc-relative bit - v |= ld.MACHO_X86_64_RELOC_SIGNED << 28 - } - - switch r.Siz { - default: - return -1 - - case 1: - v |= 0 << 25 - - case 2: - v |= 1 << 25 - - case 4: - v |= 2 << 25 - - case 8: - v |= 3 << 25 - } - - ld.Thearch.Lput(uint32(sectoff)) - ld.Thearch.Lput(v) - return 0 -} - -func pereloc1(r *ld.Reloc, sectoff int64) bool { - var v uint32 - - rs := r.Xsym - - if rs.Dynid < 0 { - ld.Diag("reloc %d to non-coff symbol %s type=%d", r.Type, rs.Name, rs.Type) - return false - } - - ld.Thearch.Lput(uint32(sectoff)) - ld.Thearch.Lput(uint32(rs.Dynid)) - - switch r.Type { - default: - return false - - case obj.R_ADDR: - if r.Siz == 8 { - v = ld.IMAGE_REL_AMD64_ADDR64 - } else { - v = ld.IMAGE_REL_AMD64_ADDR32 - } - - case obj.R_CALL, - obj.R_PCREL: - v = ld.IMAGE_REL_AMD64_REL32 - } - - ld.Thearch.Wput(uint16(v)) - - return true -} - -func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int { - return -1 -} - -func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 { - log.Fatalf("unexpected relocation variant") - return t -} - -func elfsetupplt() { - plt := ld.Linklookup(ld.Ctxt, ".plt", 0) - got := ld.Linklookup(ld.Ctxt, ".got.plt", 0) - if plt.Size == 0 { - // pushq got+8(IP) - ld.Adduint8(ld.Ctxt, plt, 0xff) - - ld.Adduint8(ld.Ctxt, plt, 0x35) - ld.Addpcrelplus(ld.Ctxt, plt, got, 8) - - // jmpq got+16(IP) - ld.Adduint8(ld.Ctxt, plt, 0xff) - - ld.Adduint8(ld.Ctxt, plt, 0x25) - ld.Addpcrelplus(ld.Ctxt, plt, got, 16) - - // nopl 0(AX) - ld.Adduint32(ld.Ctxt, plt, 0x00401f0f) - - // assume got->size == 0 too - ld.Addaddrplus(ld.Ctxt, got, ld.Linklookup(ld.Ctxt, ".dynamic", 0), 0) - - ld.Adduint64(ld.Ctxt, got, 0) - ld.Adduint64(ld.Ctxt, got, 0) - } -} - -func addpltsym(s *ld.LSym) { - if s.Plt >= 0 { - return - } - - ld.Adddynsym(ld.Ctxt, s) - - if ld.Iself { - plt := ld.Linklookup(ld.Ctxt, ".plt", 0) - got := ld.Linklookup(ld.Ctxt, ".got.plt", 0) - rela := ld.Linklookup(ld.Ctxt, ".rela.plt", 0) - if plt.Size == 0 { - elfsetupplt() - } - - // jmpq *got+size(IP) - ld.Adduint8(ld.Ctxt, plt, 0xff) - - ld.Adduint8(ld.Ctxt, plt, 0x25) - ld.Addpcrelplus(ld.Ctxt, plt, got, got.Size) - - // add to got: pointer to current pos in plt - ld.Addaddrplus(ld.Ctxt, got, plt, plt.Size) - - // pushq $x - ld.Adduint8(ld.Ctxt, plt, 0x68) - - ld.Adduint32(ld.Ctxt, plt, uint32((got.Size-24-8)/8)) - - // jmpq .plt - ld.Adduint8(ld.Ctxt, plt, 0xe9) - - ld.Adduint32(ld.Ctxt, plt, uint32(-(plt.Size + 4))) - - // rela - ld.Addaddrplus(ld.Ctxt, rela, got, got.Size-8) - - ld.Adduint64(ld.Ctxt, rela, ld.ELF64_R_INFO(uint32(s.Dynid), ld.R_X86_64_JMP_SLOT)) - ld.Adduint64(ld.Ctxt, rela, 0) - - s.Plt = int32(plt.Size - 16) - } else if ld.HEADTYPE == obj.Hdarwin { - // To do lazy symbol lookup right, we're supposed - // to tell the dynamic loader which library each - // symbol comes from and format the link info - // section just so. I'm too lazy (ha!) to do that - // so for now we'll just use non-lazy pointers, - // which don't need to be told which library to use. - // - // http://networkpx.blogspot.com/2009/09/about-lcdyldinfoonly-command.html - // has details about what we're avoiding. - - addgotsym(s) - plt := ld.Linklookup(ld.Ctxt, ".plt", 0) - - ld.Adduint32(ld.Ctxt, ld.Linklookup(ld.Ctxt, ".linkedit.plt", 0), uint32(s.Dynid)) - - // jmpq *got+size(IP) - s.Plt = int32(plt.Size) - - ld.Adduint8(ld.Ctxt, plt, 0xff) - ld.Adduint8(ld.Ctxt, plt, 0x25) - ld.Addpcrelplus(ld.Ctxt, plt, ld.Linklookup(ld.Ctxt, ".got", 0), int64(s.Got)) - } else { - ld.Diag("addpltsym: unsupported binary format") - } -} - -func addgotsym(s *ld.LSym) { - if s.Got >= 0 { - return - } - - ld.Adddynsym(ld.Ctxt, s) - got := ld.Linklookup(ld.Ctxt, ".got", 0) - s.Got = int32(got.Size) - ld.Adduint64(ld.Ctxt, got, 0) - - if ld.Iself { - rela := ld.Linklookup(ld.Ctxt, ".rela", 0) - ld.Addaddrplus(ld.Ctxt, rela, got, int64(s.Got)) - ld.Adduint64(ld.Ctxt, rela, ld.ELF64_R_INFO(uint32(s.Dynid), ld.R_X86_64_GLOB_DAT)) - ld.Adduint64(ld.Ctxt, rela, 0) - } else if ld.HEADTYPE == obj.Hdarwin { - ld.Adduint32(ld.Ctxt, ld.Linklookup(ld.Ctxt, ".linkedit.got", 0), uint32(s.Dynid)) - } else { - ld.Diag("addgotsym: unsupported binary format") - } -} - -func asmb() { - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime()) - } - ld.Bso.Flush() - - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f codeblk\n", obj.Cputime()) - } - ld.Bso.Flush() - - if ld.Iself { - ld.Asmbelfsetup() - } - - sect := ld.Segtext.Sect - ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) - ld.Codeblk(int64(sect.Vaddr), int64(sect.Length)) - for sect = sect.Next; sect != nil; sect = sect.Next { - ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) - ld.Datblk(int64(sect.Vaddr), int64(sect.Length)) - } - - if ld.Segrodata.Filelen > 0 { - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) - } - ld.Bso.Flush() - - ld.Cseek(int64(ld.Segrodata.Fileoff)) - ld.Datblk(int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) - } - - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f datblk\n", obj.Cputime()) - } - ld.Bso.Flush() - - ld.Cseek(int64(ld.Segdata.Fileoff)) - ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) - - machlink := int64(0) - if ld.HEADTYPE == obj.Hdarwin { - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) - } - - dwarfoff := ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Length), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND)) - ld.Cseek(dwarfoff) - - ld.Segdwarf.Fileoff = uint64(ld.Cpos()) - ld.Dwarfemitdebugsections() - ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff - - machlink = ld.Domacholink() - } - - switch ld.HEADTYPE { - default: - ld.Diag("unknown header type %d", ld.HEADTYPE) - fallthrough - - case obj.Hplan9, - obj.Helf: - break - - case obj.Hdarwin: - ld.Debug['8'] = 1 /* 64-bit addresses */ - - case obj.Hlinux, - obj.Hfreebsd, - obj.Hnetbsd, - obj.Hopenbsd, - obj.Hdragonfly, - obj.Hsolaris: - ld.Debug['8'] = 1 /* 64-bit addresses */ - - case obj.Hnacl, - obj.Hwindows: - break - } - - ld.Symsize = 0 - ld.Spsize = 0 - ld.Lcsize = 0 - symo := int64(0) - if ld.Debug['s'] == 0 { - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime()) - } - ld.Bso.Flush() - switch ld.HEADTYPE { - default: - case obj.Hplan9, - obj.Helf: - ld.Debug['s'] = 1 - 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)) - - case obj.Hlinux, - obj.Hfreebsd, - obj.Hnetbsd, - obj.Hopenbsd, - obj.Hdragonfly, - obj.Hsolaris, - obj.Hnacl: - symo = int64(ld.Segdata.Fileoff + ld.Segdata.Filelen) - symo = ld.Rnd(symo, int64(ld.INITRND)) - - case obj.Hwindows: - symo = int64(ld.Segdata.Fileoff + ld.Segdata.Filelen) - symo = ld.Rnd(symo, ld.PEFILEALIGN) - } - - ld.Cseek(symo) - switch ld.HEADTYPE { - default: - if ld.Iself { - ld.Cseek(symo) - ld.Asmelfsym() - ld.Cflush() - ld.Cwrite(ld.Elfstrdat) - - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) - } - - ld.Dwarfemitdebugsections() - - if ld.Linkmode == ld.LinkExternal { - ld.Elfemitreloc() - } - } - - case obj.Hplan9: - ld.Asmplan9sym() - ld.Cflush() - - sym := ld.Linklookup(ld.Ctxt, "pclntab", 0) - if sym != nil { - ld.Lcsize = int32(len(sym.P)) - for i := 0; int32(i) < ld.Lcsize; i++ { - ld.Cput(uint8(sym.P[i])) - } - - ld.Cflush() - } - - case obj.Hwindows: - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) - } - - ld.Dwarfemitdebugsections() - - case obj.Hdarwin: - if ld.Linkmode == ld.LinkExternal { - ld.Machoemitreloc() - } - } - } - - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f headr\n", obj.Cputime()) - } - ld.Bso.Flush() - ld.Cseek(0) - switch ld.HEADTYPE { - default: - case obj.Hplan9: /* plan9 */ - magic := int32(4*26*26 + 7) - - magic |= 0x00008000 /* fat header */ - ld.Lputb(uint32(magic)) /* magic */ - ld.Lputb(uint32(ld.Segtext.Filelen)) /* sizes */ - ld.Lputb(uint32(ld.Segdata.Filelen)) - ld.Lputb(uint32(ld.Segdata.Length - ld.Segdata.Filelen)) - ld.Lputb(uint32(ld.Symsize)) /* nsyms */ - vl := ld.Entryvalue() - ld.Lputb(PADDR(uint32(vl))) /* va of entry */ - ld.Lputb(uint32(ld.Spsize)) /* sp offsets */ - ld.Lputb(uint32(ld.Lcsize)) /* line offsets */ - ld.Vputb(uint64(vl)) /* va of entry */ - - case obj.Hdarwin: - ld.Asmbmacho() - - case obj.Hlinux, - obj.Hfreebsd, - obj.Hnetbsd, - obj.Hopenbsd, - obj.Hdragonfly, - obj.Hsolaris, - obj.Hnacl: - ld.Asmbelf(symo) - - case obj.Hwindows: - ld.Asmbpe() - } - - ld.Cflush() -} diff --git a/src/cmd/6l/l.go b/src/cmd/6l/l.go deleted file mode 100644 index 64466d126a..0000000000 --- a/src/cmd/6l/l.go +++ /dev/null @@ -1,47 +0,0 @@ -// Inferno utils/6l/l.h -// http://code.google.com/p/inferno-os/source/browse/utils/6l/l.h -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package main - -const ( - thechar = '6' - MaxAlign = 32 // max data alignment - FuncAlign = 16 -) - -const ( - MINLC = 1 -) - -/* 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 deleted file mode 100644 index 1dc9e02a8b..0000000000 --- a/src/cmd/6l/obj.go +++ /dev/null @@ -1,222 +0,0 @@ -// Inferno utils/6l/obj.c -// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package main - -import ( - "cmd/internal/ld" - "cmd/internal/obj" - "fmt" - "log" -) - -// Reading object files. - -func main() { - linkarchinit() - ld.Ldmain() -} - -func linkarchinit() { - ld.Thestring = "amd64" - ld.Thelinkarch = &ld.Linkamd64 - if obj.Getgoarch() == "amd64p32" { - ld.Thelinkarch = &ld.Linkamd64p32 - } - - ld.Thearch.Thechar = thechar - ld.Thearch.Ptrsize = ld.Thelinkarch.Ptrsize - ld.Thearch.Intsize = ld.Thelinkarch.Ptrsize - ld.Thearch.Regsize = ld.Thelinkarch.Regsize - ld.Thearch.Funcalign = FuncAlign - ld.Thearch.Maxalign = MaxAlign - ld.Thearch.Minlc = MINLC - ld.Thearch.Dwarfregsp = DWARFREGSP - ld.Thearch.Dwarfreglr = DWARFREGLR - - ld.Thearch.Adddynrel = adddynrel - ld.Thearch.Archinit = archinit - ld.Thearch.Archreloc = archreloc - ld.Thearch.Archrelocvariant = archrelocvariant - ld.Thearch.Asmb = asmb - ld.Thearch.Elfreloc1 = elfreloc1 - ld.Thearch.Elfsetupplt = elfsetupplt - ld.Thearch.Gentext = gentext - ld.Thearch.Machoreloc1 = machoreloc1 - ld.Thearch.PEreloc1 = pereloc1 - ld.Thearch.Lput = ld.Lputl - ld.Thearch.Wput = ld.Wputl - ld.Thearch.Vput = ld.Vputl - - ld.Thearch.Linuxdynld = "/lib64/ld-linux-x86-64.so.2" - ld.Thearch.Freebsddynld = "/libexec/ld-elf.so.1" - ld.Thearch.Openbsddynld = "/usr/libexec/ld.so" - ld.Thearch.Netbsddynld = "/libexec/ld.elf_so" - ld.Thearch.Dragonflydynld = "/usr/libexec/ld-elf.so.2" - ld.Thearch.Solarisdynld = "/lib/amd64/ld.so.1" -} - -func archinit() { - // getgoextlinkenabled is based on GO_EXTLINK_ENABLED when - // Go was built; see ../../make.bash. - if ld.Linkmode == ld.LinkAuto && obj.Getgoextlinkenabled() == "0" { - ld.Linkmode = ld.LinkInternal - } - - if ld.Buildmode == ld.BuildmodeCShared || ld.DynlinkingGo() { - ld.Linkmode = ld.LinkExternal - } - - switch ld.HEADTYPE { - default: - if ld.Linkmode == ld.LinkAuto { - ld.Linkmode = ld.LinkInternal - } - if ld.Linkmode == ld.LinkExternal && obj.Getgoextlinkenabled() != "1" { - log.Fatalf("cannot use -linkmode=external with -H %s", ld.Headstr(int(ld.HEADTYPE))) - } - - case obj.Hdarwin, - obj.Hdragonfly, - obj.Hfreebsd, - obj.Hlinux, - obj.Hnacl, - obj.Hnetbsd, - obj.Hopenbsd, - obj.Hsolaris, - obj.Hwindows: - break - } - - switch ld.HEADTYPE { - default: - ld.Exitf("unknown -H option: %v", ld.HEADTYPE) - - case obj.Hplan9: /* plan 9 */ - ld.HEADR = 32 + 8 - - if ld.INITTEXT == -1 { - ld.INITTEXT = 0x200000 + int64(ld.HEADR) - } - if ld.INITDAT == -1 { - ld.INITDAT = 0 - } - if ld.INITRND == -1 { - ld.INITRND = 0x200000 - } - - case obj.Helf: /* elf32 executable */ - ld.HEADR = int32(ld.Rnd(52+3*32, 16)) - - if ld.INITTEXT == -1 { - ld.INITTEXT = 0x80110000 - } - if ld.INITDAT == -1 { - ld.INITDAT = 0 - } - if ld.INITRND == -1 { - ld.INITRND = 4096 - } - - case obj.Hdarwin: /* apple MACH */ - ld.Machoinit() - - ld.HEADR = ld.INITIAL_MACHO_HEADR - if ld.INITRND == -1 { - ld.INITRND = 4096 - } - if ld.INITTEXT == -1 { - ld.INITTEXT = 4096 + int64(ld.HEADR) - } - if ld.INITDAT == -1 { - ld.INITDAT = 0 - } - - case obj.Hlinux, /* elf64 executable */ - obj.Hfreebsd, /* freebsd */ - obj.Hnetbsd, /* netbsd */ - obj.Hopenbsd, /* openbsd */ - obj.Hdragonfly, /* dragonfly */ - obj.Hsolaris: /* solaris */ - 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) - } - if ld.INITDAT == -1 { - ld.INITDAT = 0 - } - if ld.INITRND == -1 { - ld.INITRND = 4096 - } - - case obj.Hnacl: - ld.Elfinit() - ld.Debug['w']++ // disable dwarf, which gets confused and is useless anyway - ld.HEADR = 0x10000 - ld.Funcalign = 32 - if ld.INITTEXT == -1 { - ld.INITTEXT = 0x20000 - } - if ld.INITDAT == -1 { - ld.INITDAT = 0 - } - if ld.INITRND == -1 { - ld.INITRND = 0x10000 - } - - case obj.Hwindows: /* PE executable */ - ld.Peinit() - - ld.HEADR = ld.PEFILEHEADR - if ld.INITTEXT == -1 { - ld.INITTEXT = ld.PEBASE + int64(ld.PESECTHEADR) - } - if ld.INITDAT == -1 { - ld.INITDAT = 0 - } - if ld.INITRND == -1 { - ld.INITRND = ld.PESECTALIGN - } - } - - if ld.INITDAT != 0 && ld.INITRND != 0 { - fmt.Printf("warning: -D0x%x is ignored because of -R0x%x\n", uint64(ld.INITDAT), uint32(ld.INITRND)) - } -} 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/7g/cgen.go b/src/cmd/7g/cgen.go deleted file mode 100644 index 6f268b4185..0000000000 --- a/src/cmd/7g/cgen.go +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2009 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/gc" - "cmd/internal/obj" - "cmd/internal/obj/arm64" -) - -func blockcopy(n, res *gc.Node, osrc, odst, w int64) { - // determine alignment. - // want to avoid unaligned access, so have to use - // smaller operations for less aligned types. - // for example moving [4]byte must use 4 MOVB not 1 MOVW. - align := int(n.Type.Align) - - var op int - switch align { - default: - gc.Fatal("sgen: invalid alignment %d for %v", align, n.Type) - - case 1: - op = arm64.AMOVB - - case 2: - op = arm64.AMOVH - - case 4: - op = arm64.AMOVW - - case 8: - op = arm64.AMOVD - } - - if w%int64(align) != 0 { - gc.Fatal("sgen: unaligned size %d (align=%d) for %v", w, align, n.Type) - } - c := int32(w / int64(align)) - - if osrc%int64(align) != 0 || odst%int64(align) != 0 { - gc.Fatal("sgen: unaligned offset src %d or dst %d (align %d)", osrc, odst, align) - } - - // if we are copying forward on the stack and - // the src and dst overlap, then reverse direction - dir := align - - if osrc < odst && int64(odst) < int64(osrc)+w { - dir = -dir - } - - var dst gc.Node - var src gc.Node - if n.Ullman >= res.Ullman { - gc.Agenr(n, &dst, res) // temporarily use dst - gc.Regalloc(&src, gc.Types[gc.Tptr], nil) - gins(arm64.AMOVD, &dst, &src) - if res.Op == gc.ONAME { - gc.Gvardef(res) - } - gc.Agen(res, &dst) - } else { - if res.Op == gc.ONAME { - gc.Gvardef(res) - } - gc.Agenr(res, &dst, res) - gc.Agenr(n, &src, nil) - } - - var tmp gc.Node - gc.Regalloc(&tmp, gc.Types[gc.Tptr], nil) - - // set up end marker - var nend gc.Node - - // move src and dest to the end of block if necessary - if dir < 0 { - if c >= 4 { - gc.Regalloc(&nend, gc.Types[gc.Tptr], nil) - gins(arm64.AMOVD, &src, &nend) - } - - p := gins(arm64.AADD, nil, &src) - p.From.Type = obj.TYPE_CONST - p.From.Offset = w - - p = gins(arm64.AADD, nil, &dst) - p.From.Type = obj.TYPE_CONST - p.From.Offset = w - } else { - p := gins(arm64.AADD, nil, &src) - p.From.Type = obj.TYPE_CONST - p.From.Offset = int64(-dir) - - p = gins(arm64.AADD, nil, &dst) - p.From.Type = obj.TYPE_CONST - p.From.Offset = int64(-dir) - - if c >= 4 { - gc.Regalloc(&nend, gc.Types[gc.Tptr], nil) - p := gins(arm64.AMOVD, &src, &nend) - p.From.Type = obj.TYPE_ADDR - p.From.Offset = w - } - } - - // move - // TODO: enable duffcopy for larger copies. - if c >= 4 { - p := gins(op, &src, &tmp) - p.From.Type = obj.TYPE_MEM - p.From.Offset = int64(dir) - p.Scond = arm64.C_XPRE - ploop := p - - p = gins(op, &tmp, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = int64(dir) - p.Scond = arm64.C_XPRE - - p = gcmp(arm64.ACMP, &src, &nend) - - gc.Patch(gc.Gbranch(arm64.ABNE, nil, 0), ploop) - gc.Regfree(&nend) - } else { - // TODO(austin): Instead of generating ADD $-8,R8; ADD - // $-8,R7; n*(MOVDU 8(R8),R9; MOVDU R9,8(R7);) just - // generate the offsets directly and eliminate the - // ADDs. That will produce shorter, more - // pipeline-able code. - var p *obj.Prog - for { - tmp14 := c - c-- - if tmp14 <= 0 { - break - } - - p = gins(op, &src, &tmp) - p.From.Type = obj.TYPE_MEM - p.From.Offset = int64(dir) - p.Scond = arm64.C_XPRE - - p = gins(op, &tmp, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = int64(dir) - p.Scond = arm64.C_XPRE - } - } - - gc.Regfree(&dst) - gc.Regfree(&src) - gc.Regfree(&tmp) -} diff --git a/src/cmd/7g/galign.go b/src/cmd/7g/galign.go deleted file mode 100644 index 8a6184efd0..0000000000 --- a/src/cmd/7g/galign.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2009 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/gc" - "cmd/internal/obj" - "cmd/internal/obj/arm64" -) - -var thechar int = '7' - -var thestring string = "arm64" - -var thelinkarch *obj.LinkArch = &arm64.Linkarm64 - -func linkarchinit() { -} - -var MAXWIDTH int64 = 1 << 50 - -/* - * go declares several platform-specific type aliases: - * int, uint, and uintptr - */ -var typedefs = []gc.Typedef{ - gc.Typedef{"int", gc.TINT, gc.TINT64}, - gc.Typedef{"uint", gc.TUINT, gc.TUINT64}, - gc.Typedef{"uintptr", gc.TUINTPTR, gc.TUINT64}, -} - -func betypeinit() { - gc.Widthptr = 8 - gc.Widthint = 8 - gc.Widthreg = 8 -} - -func main() { - gc.Thearch.Thechar = thechar - gc.Thearch.Thestring = thestring - gc.Thearch.Thelinkarch = thelinkarch - gc.Thearch.Typedefs = typedefs - gc.Thearch.REGSP = arm64.REGSP - gc.Thearch.REGCTXT = arm64.REGCTXT - gc.Thearch.REGCALLX = arm64.REGRT1 - gc.Thearch.REGCALLX2 = arm64.REGRT2 - gc.Thearch.REGRETURN = arm64.REG_R0 - gc.Thearch.REGMIN = arm64.REG_R0 - gc.Thearch.REGMAX = arm64.REG_R31 - gc.Thearch.REGZERO = arm64.REGZERO - gc.Thearch.FREGMIN = arm64.REG_F0 - gc.Thearch.FREGMAX = arm64.REG_F31 - gc.Thearch.MAXWIDTH = MAXWIDTH - gc.Thearch.ReservedRegs = resvd - - gc.Thearch.Betypeinit = betypeinit - gc.Thearch.Cgen_hmul = cgen_hmul - gc.Thearch.Cgen_shift = cgen_shift - gc.Thearch.Clearfat = clearfat - gc.Thearch.Defframe = defframe - gc.Thearch.Dodiv = dodiv - gc.Thearch.Excise = excise - 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 - gc.Thearch.Linkarchinit = linkarchinit - gc.Thearch.Peep = peep - gc.Thearch.Proginfo = proginfo - gc.Thearch.Regtyp = regtyp - gc.Thearch.Sameaddr = sameaddr - gc.Thearch.Smallindir = smallindir - gc.Thearch.Stackaddr = stackaddr - gc.Thearch.Blockcopy = blockcopy - gc.Thearch.Sudoaddable = sudoaddable - gc.Thearch.Sudoclean = sudoclean - gc.Thearch.Excludedregs = excludedregs - gc.Thearch.RtoB = RtoB - gc.Thearch.FtoB = RtoB - gc.Thearch.BtoR = BtoR - gc.Thearch.BtoF = BtoF - gc.Thearch.Optoas = optoas - gc.Thearch.Doregbits = doregbits - gc.Thearch.Regnames = regnames - - gc.Main() - gc.Exit(0) -} diff --git a/src/cmd/7g/ggen.go b/src/cmd/7g/ggen.go deleted file mode 100644 index ec2eb09e38..0000000000 --- a/src/cmd/7g/ggen.go +++ /dev/null @@ -1,532 +0,0 @@ -// Copyright 2009 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/gc" - "cmd/internal/obj" - "cmd/internal/obj/arm64" - "fmt" -) - -func defframe(ptxt *obj.Prog) { - var n *gc.Node - - // fill in argument size, stack size - ptxt.To.Type = obj.TYPE_TEXTSIZE - - ptxt.To.Val = int32(gc.Rnd(gc.Curfn.Type.Argwid, int64(gc.Widthptr))) - frame := uint32(gc.Rnd(gc.Stksize+gc.Maxarg, int64(gc.Widthreg))) - ptxt.To.Offset = int64(frame) - - // insert code to zero ambiguously live variables - // so that the garbage collector only sees initialized values - // when it looks for pointers. - p := ptxt - - hi := int64(0) - lo := hi - - // 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.Name.Needzero { - continue - } - if n.Class != gc.PAUTO { - gc.Fatal("needzero class %d", n.Class) - } - if n.Type.Width%int64(gc.Widthptr) != 0 || n.Xoffset%int64(gc.Widthptr) != 0 || n.Type.Width == 0 { - gc.Fatal("var %v has size %d offset %d", gc.Nconv(n, obj.FmtLong), int(n.Type.Width), int(n.Xoffset)) - } - - if lo != hi && n.Xoffset+n.Type.Width >= lo-int64(2*gc.Widthreg) { - // merge with range we already have - lo = n.Xoffset - - continue - } - - // zero old range - p = zerorange(p, int64(frame), lo, hi) - - // set new range - hi = n.Xoffset + n.Type.Width - - lo = n.Xoffset - } - - // zero final range - zerorange(p, int64(frame), lo, hi) -} - -var darwin = obj.Getgoos() == "darwin" - -func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog { - cnt := hi - lo - if cnt == 0 { - return p - } - if cnt < int64(4*gc.Widthptr) { - for i := int64(0); i < cnt; i += int64(gc.Widthptr) { - p = appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGSP, 8+frame+lo+i) - } - } else if cnt <= int64(128*gc.Widthptr) && !darwin { // darwin ld64 cannot handle BR26 reloc with non-zero addend - p = appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGSP, 0, obj.TYPE_REG, arm64.REGRT1, 0) - p = appendpp(p, arm64.AADD, obj.TYPE_CONST, 0, 8+frame+lo-8, obj.TYPE_REG, arm64.REGRT1, 0) - p.Reg = arm64.REGRT1 - p = appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0) - f := gc.Sysfunc("duffzero") - gc.Naddr(&p.To, f) - gc.Afunclit(&p.To, f) - p.To.Offset = 4 * (128 - cnt/int64(gc.Widthptr)) - } else { - p = appendpp(p, arm64.AMOVD, obj.TYPE_CONST, 0, 8+frame+lo-8, obj.TYPE_REG, arm64.REGTMP, 0) - p = appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGSP, 0, obj.TYPE_REG, arm64.REGRT1, 0) - p = appendpp(p, arm64.AADD, obj.TYPE_REG, arm64.REGTMP, 0, obj.TYPE_REG, arm64.REGRT1, 0) - p.Reg = arm64.REGRT1 - p = appendpp(p, arm64.AMOVD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, arm64.REGTMP, 0) - p = appendpp(p, arm64.AADD, obj.TYPE_REG, arm64.REGTMP, 0, obj.TYPE_REG, arm64.REGRT2, 0) - p.Reg = arm64.REGRT1 - p = appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGRT1, int64(gc.Widthptr)) - p.Scond = arm64.C_XPRE - p1 := p - p = appendpp(p, arm64.ACMP, obj.TYPE_REG, arm64.REGRT1, 0, obj.TYPE_NONE, 0, 0) - p.Reg = arm64.REGRT2 - p = appendpp(p, arm64.ABNE, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0) - gc.Patch(p, p1) - } - - return p -} - -func appendpp(p *obj.Prog, as int, ftype int, freg int, foffset int64, ttype int, treg int, toffset int64) *obj.Prog { - q := gc.Ctxt.NewProg() - gc.Clearp(q) - q.As = int16(as) - q.Lineno = p.Lineno - q.From.Type = int16(ftype) - q.From.Reg = int16(freg) - q.From.Offset = foffset - q.To.Type = int16(ttype) - q.To.Reg = int16(treg) - q.To.Offset = toffset - q.Link = p.Link - p.Link = q - return q -} - -func ginsnop() { - var con gc.Node - gc.Nodconst(&con, gc.Types[gc.TINT], 0) - gins(arm64.AHINT, &con, nil) -} - -var panicdiv *gc.Node - -/* - * generate division. - * generates one of: - * res = nl / nr - * res = nl % nr - * according to op. - */ -func dodiv(op int, nl *gc.Node, nr *gc.Node, res *gc.Node) { - // Have to be careful about handling - // most negative int divided by -1 correctly. - // The hardware will generate undefined result. - // Also need to explicitly trap on division on zero, - // the hardware will silently generate undefined result. - // DIVW will leave unpredicable result in higher 32-bit, - // so always use DIVD/DIVDU. - t := nl.Type - - t0 := t - check := 0 - if gc.Issigned[t.Etype] { - check = 1 - if gc.Isconst(nl, gc.CTINT) && nl.Int() != -(1<= nr.Ullman { - gc.Cgen(nl, &tl) - gc.Cgen(nr, &tr) - } else { - gc.Cgen(nr, &tr) - gc.Cgen(nl, &tl) - } - - if t != t0 { - // Convert - tl2 := tl - - tr2 := tr - tl.Type = t - tr.Type = t - gmove(&tl2, &tl) - gmove(&tr2, &tr) - } - - // Handle divide-by-zero panic. - p1 := gins(optoas(gc.OCMP, t), &tr, nil) - p1.Reg = arm64.REGZERO - p1 = gc.Gbranch(optoas(gc.ONE, t), nil, +1) - if panicdiv == nil { - panicdiv = gc.Sysfunc("panicdivide") - } - gc.Ginscall(panicdiv, -1) - gc.Patch(p1, gc.Pc) - - var p2 *obj.Prog - if check != 0 { - var nm1 gc.Node - gc.Nodconst(&nm1, t, -1) - gcmp(optoas(gc.OCMP, t), &tr, &nm1) - p1 := gc.Gbranch(optoas(gc.ONE, t), nil, +1) - if op == gc.ODIV { - // a / (-1) is -a. - gins(optoas(gc.OMINUS, t), &tl, &tl) - - gmove(&tl, res) - } else { - // a % (-1) is 0. - var nz gc.Node - gc.Nodconst(&nz, t, 0) - - gmove(&nz, res) - } - - p2 = gc.Gbranch(obj.AJMP, nil, 0) - gc.Patch(p1, gc.Pc) - } - - p1 = gins(a, &tr, &tl) - if op == gc.ODIV { - gc.Regfree(&tr) - gmove(&tl, res) - } else { - // A%B = A-(A/B*B) - var tm gc.Node - gc.Regalloc(&tm, t, nil) - - // patch div to use the 3 register form - // TODO(minux): add gins3? - p1.Reg = p1.To.Reg - - p1.To.Reg = tm.Reg - gins(optoas(gc.OMUL, t), &tr, &tm) - gc.Regfree(&tr) - gins(optoas(gc.OSUB, t), &tm, &tl) - gc.Regfree(&tm) - gmove(&tl, res) - } - - gc.Regfree(&tl) - if check != 0 { - gc.Patch(p2, gc.Pc) - } -} - -/* - * generate high multiply: - * res = (nl*nr) >> width - */ -func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) { - // largest ullman on left. - if nl.Ullman < nr.Ullman { - tmp := (*gc.Node)(nl) - nl = nr - nr = tmp - } - - t := (*gc.Type)(nl.Type) - w := int(int(t.Width * 8)) - var n1 gc.Node - gc.Cgenr(nl, &n1, res) - var n2 gc.Node - gc.Cgenr(nr, &n2, nil) - switch gc.Simtype[t.Etype] { - case gc.TINT8, - gc.TINT16, - gc.TINT32: - gins(optoas(gc.OMUL, t), &n2, &n1) - p := (*obj.Prog)(gins(arm64.AASR, nil, &n1)) - p.From.Type = obj.TYPE_CONST - p.From.Offset = int64(w) - - case gc.TUINT8, - gc.TUINT16, - gc.TUINT32: - gins(optoas(gc.OMUL, t), &n2, &n1) - p := (*obj.Prog)(gins(arm64.ALSR, nil, &n1)) - p.From.Type = obj.TYPE_CONST - p.From.Offset = int64(w) - - case gc.TINT64, - gc.TUINT64: - if gc.Issigned[t.Etype] { - gins(arm64.ASMULH, &n2, &n1) - } else { - gins(arm64.AUMULH, &n2, &n1) - } - - default: - gc.Fatal("cgen_hmul %v", t) - } - - gc.Cgen(&n1, res) - gc.Regfree(&n1) - gc.Regfree(&n2) -} - -/* - * generate shift according to op, one of: - * res = nl << nr - * res = nl >> nr - */ -func cgen_shift(op int, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) { - a := int(optoas(op, nl.Type)) - - if nr.Op == gc.OLITERAL { - var n1 gc.Node - gc.Regalloc(&n1, nl.Type, res) - gc.Cgen(nl, &n1) - sc := uint64(nr.Int()) - if sc >= uint64(nl.Type.Width*8) { - // large shift gets 2 shifts by width-1 - var n3 gc.Node - gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1) - - gins(a, &n3, &n1) - gins(a, &n3, &n1) - } else { - gins(a, nr, &n1) - } - gmove(&n1, res) - gc.Regfree(&n1) - return - } - - if nl.Ullman >= gc.UINF { - var n4 gc.Node - gc.Tempname(&n4, nl.Type) - gc.Cgen(nl, &n4) - nl = &n4 - } - - if nr.Ullman >= gc.UINF { - var n5 gc.Node - gc.Tempname(&n5, nr.Type) - gc.Cgen(nr, &n5) - nr = &n5 - } - - // Allow either uint32 or uint64 as shift type, - // to avoid unnecessary conversion from uint32 to uint64 - // just to do the comparison. - tcount := gc.Types[gc.Simtype[nr.Type.Etype]] - - if tcount.Etype < gc.TUINT32 { - tcount = gc.Types[gc.TUINT32] - } - - var n1 gc.Node - gc.Regalloc(&n1, nr.Type, nil) // to hold the shift type in CX - var n3 gc.Node - gc.Regalloc(&n3, tcount, &n1) // to clear high bits of CX - - var n2 gc.Node - gc.Regalloc(&n2, nl.Type, res) - - if nl.Ullman >= nr.Ullman { - gc.Cgen(nl, &n2) - gc.Cgen(nr, &n1) - gmove(&n1, &n3) - } else { - gc.Cgen(nr, &n1) - gmove(&n1, &n3) - gc.Cgen(nl, &n2) - } - - gc.Regfree(&n3) - - // test and fix up large shifts - if !bounded { - gc.Nodconst(&n3, tcount, nl.Type.Width*8) - gcmp(optoas(gc.OCMP, tcount), &n1, &n3) - p1 := (*obj.Prog)(gc.Gbranch(optoas(gc.OLT, tcount), nil, +1)) - if op == gc.ORSH && gc.Issigned[nl.Type.Etype] { - gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1) - gins(a, &n3, &n2) - } else { - gc.Nodconst(&n3, nl.Type, 0) - gmove(&n3, &n2) - } - - gc.Patch(p1, gc.Pc) - } - - gins(a, &n1, &n2) - - gmove(&n2, res) - - gc.Regfree(&n1) - gc.Regfree(&n2) -} - -func clearfat(nl *gc.Node) { - /* clear a fat object */ - if gc.Debug['g'] != 0 { - fmt.Printf("clearfat %v (%v, size: %d)\n", nl, nl.Type, nl.Type.Width) - } - - w := uint64(uint64(nl.Type.Width)) - - // Avoid taking the address for simple enough types. - if gc.Componentgen(nil, nl) { - return - } - - c := uint64(w % 8) // bytes - q := uint64(w / 8) // dwords - - if reg[arm64.REGRT1-arm64.REG_R0] > 0 { - gc.Fatal("R%d in use during clearfat", arm64.REGRT1-arm64.REG_R0) - } - - var r0 gc.Node - gc.Nodreg(&r0, gc.Types[gc.TUINT64], arm64.REGZERO) - var dst gc.Node - gc.Nodreg(&dst, gc.Types[gc.Tptr], arm64.REGRT1) - reg[arm64.REGRT1-arm64.REG_R0]++ - gc.Agen(nl, &dst) - - var boff uint64 - if q > 128 { - p := gins(arm64.ASUB, nil, &dst) - p.From.Type = obj.TYPE_CONST - p.From.Offset = 8 - - var end gc.Node - gc.Regalloc(&end, gc.Types[gc.Tptr], nil) - p = gins(arm64.AMOVD, &dst, &end) - p.From.Type = obj.TYPE_ADDR - p.From.Offset = int64(q * 8) - - p = gins(arm64.AMOVD, &r0, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = 8 - p.Scond = arm64.C_XPRE - pl := (*obj.Prog)(p) - - p = gcmp(arm64.ACMP, &dst, &end) - gc.Patch(gc.Gbranch(arm64.ABNE, nil, 0), pl) - - gc.Regfree(&end) - - // The loop leaves R16 on the last zeroed dword - boff = 8 - } else if q >= 4 && !darwin { // darwin ld64 cannot handle BR26 reloc with non-zero addend - p := gins(arm64.ASUB, nil, &dst) - p.From.Type = obj.TYPE_CONST - p.From.Offset = 8 - f := (*gc.Node)(gc.Sysfunc("duffzero")) - p = gins(obj.ADUFFZERO, nil, f) - gc.Afunclit(&p.To, f) - - // 4 and 128 = magic constants: see ../../runtime/asm_arm64x.s - p.To.Offset = int64(4 * (128 - q)) - - // duffzero leaves R16 on the last zeroed dword - boff = 8 - } else { - var p *obj.Prog - for t := uint64(0); t < q; t++ { - p = gins(arm64.AMOVD, &r0, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = int64(8 * t) - } - - boff = 8 * q - } - - var p *obj.Prog - for t := uint64(0); t < c; t++ { - p = gins(arm64.AMOVB, &r0, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = int64(t + boff) - } - - reg[arm64.REGRT1-arm64.REG_R0]-- -} - -// Called after regopt and peep have run. -// Expand CHECKNIL pseudo-op into actual nil pointer check. -func expandchecks(firstp *obj.Prog) { - var p1 *obj.Prog - - for p := (*obj.Prog)(firstp); p != nil; p = p.Link { - if gc.Debug_checknil != 0 && gc.Ctxt.Debugvlog != 0 { - fmt.Printf("expandchecks: %v\n", p) - } - if p.As != obj.ACHECKNIL { - continue - } - if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers - gc.Warnl(int(p.Lineno), "generated nil check") - } - if p.From.Type != obj.TYPE_REG { - gc.Fatal("invalid nil check %v\n", p) - } - - // check is - // CBNZ arg, 2(PC) - // MOVD ZR, 0(arg) - p1 = gc.Ctxt.NewProg() - gc.Clearp(p1) - p1.Link = p.Link - p.Link = p1 - p1.Lineno = p.Lineno - p1.Pc = 9999 - - p.As = arm64.ACBNZ - p.To.Type = obj.TYPE_BRANCH - p.To.Val = p1.Link - - // crash by write to memory address 0. - p1.As = arm64.AMOVD - p1.From.Type = obj.TYPE_REG - p1.From.Reg = arm64.REGZERO - p1.To.Type = obj.TYPE_MEM - p1.To.Reg = p.From.Reg - p1.To.Offset = 0 - } -} - -// res = runtime.getg() -func getg(res *gc.Node) { - var n1 gc.Node - gc.Nodreg(&n1, res.Type, arm64.REGG) - gmove(&n1, res) -} diff --git a/src/cmd/7g/gsubr.go b/src/cmd/7g/gsubr.go deleted file mode 100644 index 0f617079ad..0000000000 --- a/src/cmd/7g/gsubr.go +++ /dev/null @@ -1,983 +0,0 @@ -// Derived from Inferno utils/6c/txt.c -// http://code.google.com/p/inferno-os/source/browse/utils/6c/txt.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package main - -import ( - "cmd/internal/gc" - "cmd/internal/obj" - "cmd/internal/obj/arm64" - "fmt" -) - -var resvd = []int{ - arm64.REGTMP, - arm64.REGG, - arm64.REGRT1, - arm64.REGRT2, - arm64.REG_R31, // REGZERO and REGSP - arm64.FREGZERO, - arm64.FREGHALF, - arm64.FREGONE, - arm64.FREGTWO, -} - -/* - * generate - * as $c, n - */ -func ginscon(as int, c int64, n2 *gc.Node) { - var n1 gc.Node - - gc.Nodconst(&n1, gc.Types[gc.TINT64], c) - - if as != arm64.AMOVD && (c < -arm64.BIG || c > arm64.BIG) || as == arm64.AMUL || n2 != nil && n2.Op != gc.OREGISTER { - // cannot have more than 16-bit of immediate in ADD, etc. - // instead, MOV into register first. - var ntmp gc.Node - gc.Regalloc(&ntmp, gc.Types[gc.TINT64], nil) - - gins(arm64.AMOVD, &n1, &ntmp) - gins(as, &ntmp, n2) - gc.Regfree(&ntmp) - return - } - - rawgins(as, &n1, n2) -} - -/* - * generate - * as n, $c (CMP) - */ -func ginscon2(as int, n2 *gc.Node, c int64) { - var n1 gc.Node - - gc.Nodconst(&n1, gc.Types[gc.TINT64], c) - - switch as { - default: - gc.Fatal("ginscon2") - - case arm64.ACMP: - if -arm64.BIG <= c && c <= arm64.BIG { - gcmp(as, n2, &n1) - return - } - } - - // MOV n1 into register first - var ntmp gc.Node - gc.Regalloc(&ntmp, gc.Types[gc.TINT64], nil) - - rawgins(arm64.AMOVD, &n1, &ntmp) - gcmp(as, n2, &ntmp) - 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, n2.Int()) - } 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 - * hard part is conversions. - */ -func gmove(f *gc.Node, t *gc.Node) { - if gc.Debug['M'] != 0 { - fmt.Printf("gmove %v -> %v\n", gc.Nconv(f, obj.FmtLong), gc.Nconv(t, obj.FmtLong)) - } - - ft := int(gc.Simsimtype(f.Type)) - tt := int(gc.Simsimtype(t.Type)) - cvt := (*gc.Type)(t.Type) - - if gc.Iscomplex[ft] || gc.Iscomplex[tt] { - gc.Complexmove(f, t) - return - } - - // cannot have two memory operands - var r1 gc.Node - var a int - if gc.Ismem(f) && gc.Ismem(t) { - goto hard - } - - // convert constant to desired type - if f.Op == gc.OLITERAL { - var con gc.Node - switch tt { - default: - f.Convconst(&con, t.Type) - - case gc.TINT32, - gc.TINT16, - gc.TINT8: - var con gc.Node - f.Convconst(&con, gc.Types[gc.TINT64]) - var r1 gc.Node - gc.Regalloc(&r1, con.Type, t) - gins(arm64.AMOVD, &con, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - - case gc.TUINT32, - gc.TUINT16, - gc.TUINT8: - var con gc.Node - f.Convconst(&con, gc.Types[gc.TUINT64]) - var r1 gc.Node - gc.Regalloc(&r1, con.Type, t) - gins(arm64.AMOVD, &con, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - } - - f = &con - ft = tt // so big switch will choose a simple mov - - // constants can't move directly to memory. - if gc.Ismem(t) { - goto hard - } - } - - // value -> value copy, first operand in memory. - // any floating point operand requires register - // src, so goto hard to copy to register first. - if gc.Ismem(f) && ft != tt && (gc.Isfloat[ft] || gc.Isfloat[tt]) { - cvt = gc.Types[ft] - goto hard - } - - // value -> value copy, only one memory operand. - // figure out the instruction to use. - // break out of switch for one-instruction gins. - // goto rdst for "destination must be register". - // goto hard for "convert to cvt type first". - // otherwise handle and return. - - switch uint32(ft)<<16 | uint32(tt) { - default: - gc.Fatal("gmove %v -> %v", gc.Tconv(f.Type, obj.FmtLong), gc.Tconv(t.Type, obj.FmtLong)) - - /* - * integer copy and truncate - */ - case gc.TINT8<<16 | gc.TINT8, // same size - gc.TUINT8<<16 | gc.TINT8, - gc.TINT16<<16 | gc.TINT8, - // truncate - gc.TUINT16<<16 | gc.TINT8, - gc.TINT32<<16 | gc.TINT8, - gc.TUINT32<<16 | gc.TINT8, - gc.TINT64<<16 | gc.TINT8, - gc.TUINT64<<16 | gc.TINT8: - a = arm64.AMOVB - - case gc.TINT8<<16 | gc.TUINT8, // same size - gc.TUINT8<<16 | gc.TUINT8, - gc.TINT16<<16 | gc.TUINT8, - // truncate - gc.TUINT16<<16 | gc.TUINT8, - gc.TINT32<<16 | gc.TUINT8, - gc.TUINT32<<16 | gc.TUINT8, - gc.TINT64<<16 | gc.TUINT8, - gc.TUINT64<<16 | gc.TUINT8: - a = arm64.AMOVBU - - case gc.TINT16<<16 | gc.TINT16, // same size - gc.TUINT16<<16 | gc.TINT16, - gc.TINT32<<16 | gc.TINT16, - // truncate - gc.TUINT32<<16 | gc.TINT16, - gc.TINT64<<16 | gc.TINT16, - gc.TUINT64<<16 | gc.TINT16: - a = arm64.AMOVH - - case gc.TINT16<<16 | gc.TUINT16, // same size - gc.TUINT16<<16 | gc.TUINT16, - gc.TINT32<<16 | gc.TUINT16, - // truncate - gc.TUINT32<<16 | gc.TUINT16, - gc.TINT64<<16 | gc.TUINT16, - gc.TUINT64<<16 | gc.TUINT16: - a = arm64.AMOVHU - - case gc.TINT32<<16 | gc.TINT32, // same size - gc.TUINT32<<16 | gc.TINT32, - gc.TINT64<<16 | gc.TINT32, - // truncate - gc.TUINT64<<16 | gc.TINT32: - a = arm64.AMOVW - - case gc.TINT32<<16 | gc.TUINT32, // same size - gc.TUINT32<<16 | gc.TUINT32, - gc.TINT64<<16 | gc.TUINT32, - gc.TUINT64<<16 | gc.TUINT32: - a = arm64.AMOVWU - - case gc.TINT64<<16 | gc.TINT64, // same size - gc.TINT64<<16 | gc.TUINT64, - gc.TUINT64<<16 | gc.TINT64, - gc.TUINT64<<16 | gc.TUINT64: - a = arm64.AMOVD - - /* - * integer up-conversions - */ - case gc.TINT8<<16 | gc.TINT16, // sign extend int8 - gc.TINT8<<16 | gc.TUINT16, - gc.TINT8<<16 | gc.TINT32, - gc.TINT8<<16 | gc.TUINT32, - gc.TINT8<<16 | gc.TINT64, - gc.TINT8<<16 | gc.TUINT64: - a = arm64.AMOVB - - goto rdst - - case gc.TUINT8<<16 | gc.TINT16, // zero extend uint8 - gc.TUINT8<<16 | gc.TUINT16, - gc.TUINT8<<16 | gc.TINT32, - gc.TUINT8<<16 | gc.TUINT32, - gc.TUINT8<<16 | gc.TINT64, - gc.TUINT8<<16 | gc.TUINT64: - a = arm64.AMOVBU - - goto rdst - - case gc.TINT16<<16 | gc.TINT32, // sign extend int16 - gc.TINT16<<16 | gc.TUINT32, - gc.TINT16<<16 | gc.TINT64, - gc.TINT16<<16 | gc.TUINT64: - a = arm64.AMOVH - - goto rdst - - case gc.TUINT16<<16 | gc.TINT32, // zero extend uint16 - gc.TUINT16<<16 | gc.TUINT32, - gc.TUINT16<<16 | gc.TINT64, - gc.TUINT16<<16 | gc.TUINT64: - a = arm64.AMOVHU - - goto rdst - - case gc.TINT32<<16 | gc.TINT64, // sign extend int32 - gc.TINT32<<16 | gc.TUINT64: - a = arm64.AMOVW - - goto rdst - - case gc.TUINT32<<16 | gc.TINT64, // zero extend uint32 - gc.TUINT32<<16 | gc.TUINT64: - a = arm64.AMOVWU - - goto rdst - - /* - * float to integer - */ - case gc.TFLOAT32<<16 | gc.TINT32: - a = arm64.AFCVTZSSW - goto rdst - - case gc.TFLOAT64<<16 | gc.TINT32: - a = arm64.AFCVTZSDW - goto rdst - - case gc.TFLOAT32<<16 | gc.TINT64: - a = arm64.AFCVTZSS - goto rdst - - case gc.TFLOAT64<<16 | gc.TINT64: - a = arm64.AFCVTZSD - goto rdst - - case gc.TFLOAT32<<16 | gc.TUINT32: - a = arm64.AFCVTZUSW - goto rdst - - case gc.TFLOAT64<<16 | gc.TUINT32: - a = arm64.AFCVTZUDW - goto rdst - - case gc.TFLOAT32<<16 | gc.TUINT64: - a = arm64.AFCVTZUS - goto rdst - - case gc.TFLOAT64<<16 | gc.TUINT64: - a = arm64.AFCVTZUD - goto rdst - - case gc.TFLOAT32<<16 | gc.TINT16, - gc.TFLOAT32<<16 | gc.TINT8, - gc.TFLOAT64<<16 | gc.TINT16, - gc.TFLOAT64<<16 | gc.TINT8: - cvt = gc.Types[gc.TINT32] - - goto hard - - case gc.TFLOAT32<<16 | gc.TUINT16, - gc.TFLOAT32<<16 | gc.TUINT8, - gc.TFLOAT64<<16 | gc.TUINT16, - gc.TFLOAT64<<16 | gc.TUINT8: - cvt = gc.Types[gc.TUINT32] - - goto hard - - /* - * integer to float - */ - case gc.TINT8<<16 | gc.TFLOAT32, - gc.TINT16<<16 | gc.TFLOAT32, - gc.TINT32<<16 | gc.TFLOAT32: - a = arm64.ASCVTFWS - - goto rdst - - case gc.TINT8<<16 | gc.TFLOAT64, - gc.TINT16<<16 | gc.TFLOAT64, - gc.TINT32<<16 | gc.TFLOAT64: - a = arm64.ASCVTFWD - - goto rdst - - case gc.TINT64<<16 | gc.TFLOAT32: - a = arm64.ASCVTFS - goto rdst - - case gc.TINT64<<16 | gc.TFLOAT64: - a = arm64.ASCVTFD - goto rdst - - case gc.TUINT8<<16 | gc.TFLOAT32, - gc.TUINT16<<16 | gc.TFLOAT32, - gc.TUINT32<<16 | gc.TFLOAT32: - a = arm64.AUCVTFWS - - goto rdst - - case gc.TUINT8<<16 | gc.TFLOAT64, - gc.TUINT16<<16 | gc.TFLOAT64, - gc.TUINT32<<16 | gc.TFLOAT64: - a = arm64.AUCVTFWD - - goto rdst - - case gc.TUINT64<<16 | gc.TFLOAT32: - a = arm64.AUCVTFS - goto rdst - - case gc.TUINT64<<16 | gc.TFLOAT64: - a = arm64.AUCVTFD - goto rdst - - /* - * float to float - */ - case gc.TFLOAT32<<16 | gc.TFLOAT32: - a = arm64.AFMOVS - - case gc.TFLOAT64<<16 | gc.TFLOAT64: - a = arm64.AFMOVD - - case gc.TFLOAT32<<16 | gc.TFLOAT64: - a = arm64.AFCVTSD - goto rdst - - case gc.TFLOAT64<<16 | gc.TFLOAT32: - a = arm64.AFCVTDS - goto rdst - } - - gins(a, f, t) - return - - // requires register destination -rdst: - gc.Regalloc(&r1, t.Type, t) - - gins(a, f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - - // requires register intermediate -hard: - gc.Regalloc(&r1, cvt, t) - - gmove(f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return -} - -func intLiteral(n *gc.Node) (x int64, ok bool) { - switch { - case n == nil: - return - case gc.Isconst(n, gc.CTINT): - return n.Int(), true - case gc.Isconst(n, gc.CTBOOL): - return int64(obj.Bool2int(n.Bool())), true - } - return -} - -// gins is called by the front end. -// It synthesizes some multiple-instruction sequences -// so the front end can stay simpler. -func gins(as int, f, t *gc.Node) *obj.Prog { - if as >= obj.A_ARCHSPECIFIC { - if x, ok := intLiteral(f); ok { - ginscon(as, x, t) - return nil // caller must not use - } - } - if as == arm64.ACMP { - if x, ok := intLiteral(t); ok { - ginscon2(as, f, x) - return nil // caller must not use - } - } - return rawgins(as, f, t) -} - -/* - * generate one instruction: - * as f, t - */ -func rawgins(as int, f *gc.Node, t *gc.Node) *obj.Prog { - // TODO(austin): Add self-move test like in 6g (but be careful - // of truncation moves) - - p := gc.Prog(as) - gc.Naddr(&p.From, f) - gc.Naddr(&p.To, t) - - switch as { - case arm64.ACMP, arm64.AFCMPS, arm64.AFCMPD: - if t != nil { - if f.Op != gc.OREGISTER { - gc.Fatal("bad operands to gcmp") - } - p.From = p.To - p.To = obj.Addr{} - raddr(f, p) - } - } - - // Bad things the front end has done to us. Crash to find call stack. - switch as { - case arm64.AAND, arm64.AMUL: - if p.From.Type == obj.TYPE_CONST { - gc.Debug['h'] = 1 - gc.Fatal("bad inst: %v", p) - } - case arm64.ACMP: - if p.From.Type == obj.TYPE_MEM || p.To.Type == obj.TYPE_MEM { - gc.Debug['h'] = 1 - gc.Fatal("bad inst: %v", p) - } - } - - if gc.Debug['g'] != 0 { - fmt.Printf("%v\n", p) - } - - w := int32(0) - switch as { - case arm64.AMOVB, - arm64.AMOVBU: - w = 1 - - case arm64.AMOVH, - arm64.AMOVHU: - w = 2 - - case arm64.AMOVW, - arm64.AMOVWU: - w = 4 - - case arm64.AMOVD: - if p.From.Type == obj.TYPE_CONST || p.From.Type == obj.TYPE_ADDR { - break - } - w = 8 - } - - if w != 0 && ((f != nil && p.From.Width < int64(w)) || (t != nil && p.To.Type != obj.TYPE_REG && p.To.Width > int64(w))) { - gc.Dump("f", f) - gc.Dump("t", t) - gc.Fatal("bad width: %v (%d, %d)\n", p, p.From.Width, p.To.Width) - } - - return p -} - -/* - * insert n into reg slot of p - */ -func raddr(n *gc.Node, p *obj.Prog) { - var a obj.Addr - - gc.Naddr(&a, n) - if a.Type != obj.TYPE_REG { - if n != nil { - gc.Fatal("bad in raddr: %v", gc.Oconv(int(n.Op), 0)) - } else { - gc.Fatal("bad in raddr: ") - } - p.Reg = 0 - } else { - p.Reg = a.Reg - } -} - -func gcmp(as int, lhs *gc.Node, rhs *gc.Node) *obj.Prog { - if lhs.Op != gc.OREGISTER { - gc.Fatal("bad operands to gcmp: %v %v", gc.Oconv(int(lhs.Op), 0), gc.Oconv(int(rhs.Op), 0)) - } - - p := rawgins(as, rhs, nil) - raddr(lhs, p) - return p -} - -/* - * return Axxx for Oxxx on type t. - */ -func optoas(op int, t *gc.Type) int { - if t == nil { - gc.Fatal("optoas: t is nil") - } - - a := int(obj.AXXX) - switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) { - default: - gc.Fatal("optoas: no entry for op=%v type=%v", gc.Oconv(int(op), 0), t) - - case gc.OEQ<<16 | gc.TBOOL, - gc.OEQ<<16 | gc.TINT8, - gc.OEQ<<16 | gc.TUINT8, - gc.OEQ<<16 | gc.TINT16, - gc.OEQ<<16 | gc.TUINT16, - gc.OEQ<<16 | gc.TINT32, - gc.OEQ<<16 | gc.TUINT32, - gc.OEQ<<16 | gc.TINT64, - gc.OEQ<<16 | gc.TUINT64, - gc.OEQ<<16 | gc.TPTR32, - gc.OEQ<<16 | gc.TPTR64, - gc.OEQ<<16 | gc.TFLOAT32, - gc.OEQ<<16 | gc.TFLOAT64: - a = arm64.ABEQ - - case gc.ONE<<16 | gc.TBOOL, - gc.ONE<<16 | gc.TINT8, - gc.ONE<<16 | gc.TUINT8, - gc.ONE<<16 | gc.TINT16, - gc.ONE<<16 | gc.TUINT16, - gc.ONE<<16 | gc.TINT32, - gc.ONE<<16 | gc.TUINT32, - gc.ONE<<16 | gc.TINT64, - gc.ONE<<16 | gc.TUINT64, - gc.ONE<<16 | gc.TPTR32, - gc.ONE<<16 | gc.TPTR64, - gc.ONE<<16 | gc.TFLOAT32, - gc.ONE<<16 | gc.TFLOAT64: - a = arm64.ABNE - - case gc.OLT<<16 | gc.TINT8, - gc.OLT<<16 | gc.TINT16, - gc.OLT<<16 | gc.TINT32, - gc.OLT<<16 | gc.TINT64: - a = arm64.ABLT - - case gc.OLT<<16 | gc.TUINT8, - gc.OLT<<16 | gc.TUINT16, - gc.OLT<<16 | gc.TUINT32, - gc.OLT<<16 | gc.TUINT64, - gc.OLT<<16 | gc.TFLOAT32, - gc.OLT<<16 | gc.TFLOAT64: - a = arm64.ABLO - - case gc.OLE<<16 | gc.TINT8, - gc.OLE<<16 | gc.TINT16, - gc.OLE<<16 | gc.TINT32, - gc.OLE<<16 | gc.TINT64: - a = arm64.ABLE - - case gc.OLE<<16 | gc.TUINT8, - gc.OLE<<16 | gc.TUINT16, - gc.OLE<<16 | gc.TUINT32, - gc.OLE<<16 | gc.TUINT64, - gc.OLE<<16 | gc.TFLOAT32, - gc.OLE<<16 | gc.TFLOAT64: - a = arm64.ABLS - - case gc.OGT<<16 | gc.TINT8, - gc.OGT<<16 | gc.TINT16, - gc.OGT<<16 | gc.TINT32, - gc.OGT<<16 | gc.TINT64, - gc.OGT<<16 | gc.TFLOAT32, - gc.OGT<<16 | gc.TFLOAT64: - a = arm64.ABGT - - case gc.OGT<<16 | gc.TUINT8, - gc.OGT<<16 | gc.TUINT16, - gc.OGT<<16 | gc.TUINT32, - gc.OGT<<16 | gc.TUINT64: - a = arm64.ABHI - - case gc.OGE<<16 | gc.TINT8, - gc.OGE<<16 | gc.TINT16, - gc.OGE<<16 | gc.TINT32, - gc.OGE<<16 | gc.TINT64, - gc.OGE<<16 | gc.TFLOAT32, - gc.OGE<<16 | gc.TFLOAT64: - a = arm64.ABGE - - case gc.OGE<<16 | gc.TUINT8, - gc.OGE<<16 | gc.TUINT16, - gc.OGE<<16 | gc.TUINT32, - gc.OGE<<16 | gc.TUINT64: - a = arm64.ABHS - - case gc.OCMP<<16 | gc.TBOOL, - gc.OCMP<<16 | gc.TINT8, - gc.OCMP<<16 | gc.TINT16, - gc.OCMP<<16 | gc.TINT32, - gc.OCMP<<16 | gc.TPTR32, - gc.OCMP<<16 | gc.TINT64, - gc.OCMP<<16 | gc.TUINT8, - gc.OCMP<<16 | gc.TUINT16, - gc.OCMP<<16 | gc.TUINT32, - gc.OCMP<<16 | gc.TUINT64, - gc.OCMP<<16 | gc.TPTR64: - a = arm64.ACMP - - case gc.OCMP<<16 | gc.TFLOAT32: - a = arm64.AFCMPS - - case gc.OCMP<<16 | gc.TFLOAT64: - a = arm64.AFCMPD - - case gc.OAS<<16 | gc.TBOOL, - gc.OAS<<16 | gc.TINT8: - a = arm64.AMOVB - - case gc.OAS<<16 | gc.TUINT8: - a = arm64.AMOVBU - - case gc.OAS<<16 | gc.TINT16: - a = arm64.AMOVH - - case gc.OAS<<16 | gc.TUINT16: - a = arm64.AMOVHU - - case gc.OAS<<16 | gc.TINT32: - a = arm64.AMOVW - - case gc.OAS<<16 | gc.TUINT32, - gc.OAS<<16 | gc.TPTR32: - a = arm64.AMOVWU - - case gc.OAS<<16 | gc.TINT64, - gc.OAS<<16 | gc.TUINT64, - gc.OAS<<16 | gc.TPTR64: - a = arm64.AMOVD - - case gc.OAS<<16 | gc.TFLOAT32: - a = arm64.AFMOVS - - case gc.OAS<<16 | gc.TFLOAT64: - a = arm64.AFMOVD - - case gc.OADD<<16 | gc.TINT8, - gc.OADD<<16 | gc.TUINT8, - gc.OADD<<16 | gc.TINT16, - gc.OADD<<16 | gc.TUINT16, - gc.OADD<<16 | gc.TINT32, - gc.OADD<<16 | gc.TUINT32, - gc.OADD<<16 | gc.TPTR32, - gc.OADD<<16 | gc.TINT64, - gc.OADD<<16 | gc.TUINT64, - gc.OADD<<16 | gc.TPTR64: - a = arm64.AADD - - case gc.OADD<<16 | gc.TFLOAT32: - a = arm64.AFADDS - - case gc.OADD<<16 | gc.TFLOAT64: - a = arm64.AFADDD - - case gc.OSUB<<16 | gc.TINT8, - gc.OSUB<<16 | gc.TUINT8, - gc.OSUB<<16 | gc.TINT16, - gc.OSUB<<16 | gc.TUINT16, - gc.OSUB<<16 | gc.TINT32, - gc.OSUB<<16 | gc.TUINT32, - gc.OSUB<<16 | gc.TPTR32, - gc.OSUB<<16 | gc.TINT64, - gc.OSUB<<16 | gc.TUINT64, - gc.OSUB<<16 | gc.TPTR64: - a = arm64.ASUB - - case gc.OSUB<<16 | gc.TFLOAT32: - a = arm64.AFSUBS - - case gc.OSUB<<16 | gc.TFLOAT64: - a = arm64.AFSUBD - - case gc.OMINUS<<16 | gc.TINT8, - gc.OMINUS<<16 | gc.TUINT8, - gc.OMINUS<<16 | gc.TINT16, - gc.OMINUS<<16 | gc.TUINT16, - gc.OMINUS<<16 | gc.TINT32, - gc.OMINUS<<16 | gc.TUINT32, - gc.OMINUS<<16 | gc.TPTR32, - gc.OMINUS<<16 | gc.TINT64, - gc.OMINUS<<16 | gc.TUINT64, - gc.OMINUS<<16 | gc.TPTR64: - a = arm64.ANEG - - case gc.OMINUS<<16 | gc.TFLOAT32: - a = arm64.AFNEGS - - case gc.OMINUS<<16 | gc.TFLOAT64: - a = arm64.AFNEGD - - case gc.OAND<<16 | gc.TINT8, - gc.OAND<<16 | gc.TUINT8, - gc.OAND<<16 | gc.TINT16, - gc.OAND<<16 | gc.TUINT16, - gc.OAND<<16 | gc.TINT32, - gc.OAND<<16 | gc.TUINT32, - gc.OAND<<16 | gc.TPTR32, - gc.OAND<<16 | gc.TINT64, - gc.OAND<<16 | gc.TUINT64, - gc.OAND<<16 | gc.TPTR64: - a = arm64.AAND - - case gc.OOR<<16 | gc.TINT8, - gc.OOR<<16 | gc.TUINT8, - gc.OOR<<16 | gc.TINT16, - gc.OOR<<16 | gc.TUINT16, - gc.OOR<<16 | gc.TINT32, - gc.OOR<<16 | gc.TUINT32, - gc.OOR<<16 | gc.TPTR32, - gc.OOR<<16 | gc.TINT64, - gc.OOR<<16 | gc.TUINT64, - gc.OOR<<16 | gc.TPTR64: - a = arm64.AORR - - case gc.OXOR<<16 | gc.TINT8, - gc.OXOR<<16 | gc.TUINT8, - gc.OXOR<<16 | gc.TINT16, - gc.OXOR<<16 | gc.TUINT16, - gc.OXOR<<16 | gc.TINT32, - gc.OXOR<<16 | gc.TUINT32, - gc.OXOR<<16 | gc.TPTR32, - gc.OXOR<<16 | gc.TINT64, - gc.OXOR<<16 | gc.TUINT64, - gc.OXOR<<16 | gc.TPTR64: - a = arm64.AEOR - - // TODO(minux): handle rotates - //case CASE(OLROT, TINT8): - //case CASE(OLROT, TUINT8): - //case CASE(OLROT, TINT16): - //case CASE(OLROT, TUINT16): - //case CASE(OLROT, TINT32): - //case CASE(OLROT, TUINT32): - //case CASE(OLROT, TPTR32): - //case CASE(OLROT, TINT64): - //case CASE(OLROT, TUINT64): - //case CASE(OLROT, TPTR64): - // a = 0//???; RLDC? - // break; - - case gc.OLSH<<16 | gc.TINT8, - gc.OLSH<<16 | gc.TUINT8, - gc.OLSH<<16 | gc.TINT16, - gc.OLSH<<16 | gc.TUINT16, - gc.OLSH<<16 | gc.TINT32, - gc.OLSH<<16 | gc.TUINT32, - gc.OLSH<<16 | gc.TPTR32, - gc.OLSH<<16 | gc.TINT64, - gc.OLSH<<16 | gc.TUINT64, - gc.OLSH<<16 | gc.TPTR64: - a = arm64.ALSL - - case gc.ORSH<<16 | gc.TUINT8, - gc.ORSH<<16 | gc.TUINT16, - gc.ORSH<<16 | gc.TUINT32, - gc.ORSH<<16 | gc.TPTR32, - gc.ORSH<<16 | gc.TUINT64, - gc.ORSH<<16 | gc.TPTR64: - a = arm64.ALSR - - case gc.ORSH<<16 | gc.TINT8, - gc.ORSH<<16 | gc.TINT16, - gc.ORSH<<16 | gc.TINT32, - gc.ORSH<<16 | gc.TINT64: - a = arm64.AASR - - // TODO(minux): handle rotates - //case CASE(ORROTC, TINT8): - //case CASE(ORROTC, TUINT8): - //case CASE(ORROTC, TINT16): - //case CASE(ORROTC, TUINT16): - //case CASE(ORROTC, TINT32): - //case CASE(ORROTC, TUINT32): - //case CASE(ORROTC, TINT64): - //case CASE(ORROTC, TUINT64): - // a = 0//??? RLDC?? - // break; - - case gc.OHMUL<<16 | gc.TINT64: - a = arm64.ASMULH - - case gc.OHMUL<<16 | gc.TUINT64, - gc.OHMUL<<16 | gc.TPTR64: - a = arm64.AUMULH - - case gc.OMUL<<16 | gc.TINT8, - gc.OMUL<<16 | gc.TINT16, - gc.OMUL<<16 | gc.TINT32: - a = arm64.ASMULL - - case gc.OMUL<<16 | gc.TINT64: - a = arm64.AMUL - - case gc.OMUL<<16 | gc.TUINT8, - gc.OMUL<<16 | gc.TUINT16, - gc.OMUL<<16 | gc.TUINT32, - gc.OMUL<<16 | gc.TPTR32: - // don't use word multiply, the high 32-bit are undefined. - a = arm64.AUMULL - - case gc.OMUL<<16 | gc.TUINT64, - gc.OMUL<<16 | gc.TPTR64: - a = arm64.AMUL // for 64-bit multiplies, signedness doesn't matter. - - case gc.OMUL<<16 | gc.TFLOAT32: - a = arm64.AFMULS - - case gc.OMUL<<16 | gc.TFLOAT64: - a = arm64.AFMULD - - case gc.ODIV<<16 | gc.TINT8, - gc.ODIV<<16 | gc.TINT16, - gc.ODIV<<16 | gc.TINT32, - gc.ODIV<<16 | gc.TINT64: - a = arm64.ASDIV - - case gc.ODIV<<16 | gc.TUINT8, - gc.ODIV<<16 | gc.TUINT16, - gc.ODIV<<16 | gc.TUINT32, - gc.ODIV<<16 | gc.TPTR32, - gc.ODIV<<16 | gc.TUINT64, - gc.ODIV<<16 | gc.TPTR64: - a = arm64.AUDIV - - case gc.ODIV<<16 | gc.TFLOAT32: - a = arm64.AFDIVS - - case gc.ODIV<<16 | gc.TFLOAT64: - a = arm64.AFDIVD - - case gc.OSQRT<<16 | gc.TFLOAT64: - a = arm64.AFSQRTD - } - - return a -} - -const ( - ODynam = 1 << 0 - OAddable = 1 << 1 -) - -func xgen(n *gc.Node, a *gc.Node, o int) bool { - // TODO(minux) - - return -1 != 0 /*TypeKind(100016)*/ -} - -func sudoclean() { - return -} - -/* - * generate code to compute address of n, - * a reference to a (perhaps nested) field inside - * an array or struct. - * return 0 on failure, 1 on success. - * on success, leaves usable address in a. - * - * caller is responsible for calling sudoclean - * after successful sudoaddable, - * to release the register used for a. - */ -func sudoaddable(as int, n *gc.Node, a *obj.Addr) bool { - // TODO(minux) - - *a = obj.Addr{} - return false -} diff --git a/src/cmd/7g/peep.go b/src/cmd/7g/peep.go deleted file mode 100644 index 49bc69b132..0000000000 --- a/src/cmd/7g/peep.go +++ /dev/null @@ -1,809 +0,0 @@ -// Derived from Inferno utils/6c/peep.c -// http://code.google.com/p/inferno-os/source/browse/utils/6c/peep.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package main - -import ( - "cmd/internal/gc" - "cmd/internal/obj" - "cmd/internal/obj/arm64" - "fmt" -) - -var gactive uint32 - -func peep(firstp *obj.Prog) { - g := (*gc.Graph)(gc.Flowstart(firstp, nil)) - if g == nil { - return - } - gactive = 0 - - var p *obj.Prog - var r *gc.Flow - var t int -loop1: - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - gc.Dumpit("loop1", g.Start, 0) - } - - t = 0 - for r = g.Start; r != nil; r = r.Link { - p = r.Prog - - // TODO(minux) Handle smaller moves. arm and amd64 - // distinguish between moves that *must* sign/zero - // extend and moves that don't care so they - // can eliminate moves that don't care without - // breaking moves that do care. This might let us - // simplify or remove the next peep loop, too. - if p.As == arm64.AMOVD || p.As == arm64.AFMOVD { - if regtyp(&p.To) { - // Try to eliminate reg->reg moves - if regtyp(&p.From) { - if p.From.Type == p.To.Type { - if copyprop(r) { - excise(r) - t++ - } else if subprop(r) && copyprop(r) { - excise(r) - t++ - } - } - } - } - } - } - - if t != 0 { - goto loop1 - } - - /* - * look for MOVB x,R; MOVB R,R (for small MOVs not handled above) - */ - var p1 *obj.Prog - var r1 *gc.Flow - for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { - p = r.Prog - switch p.As { - default: - continue - - case arm64.AMOVH, - arm64.AMOVHU, - arm64.AMOVB, - arm64.AMOVBU, - arm64.AMOVW, - arm64.AMOVWU: - if p.To.Type != obj.TYPE_REG { - continue - } - } - - r1 = r.Link - if r1 == nil { - continue - } - p1 = r1.Prog - if p1.As != p.As { - continue - } - if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg { - continue - } - if p1.To.Type != obj.TYPE_REG || p1.To.Reg != p.To.Reg { - continue - } - excise(r1) - } - - if gc.Debug['D'] > 1 { - goto ret /* allow following code improvement to be suppressed */ - } - - // MOVD $c, R'; ADD R', R (R' unused) -> ADD $c, R - for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { - p = r.Prog - switch p.As { - default: - continue - - case arm64.AMOVD: - if p.To.Type != obj.TYPE_REG { - continue - } - if p.From.Type != obj.TYPE_CONST { - continue - } - if p.From.Offset < 0 || 4096 <= p.From.Offset { - continue - } - } - r1 = r.Link - if r1 == nil { - continue - } - p1 = r1.Prog - if p1.As != arm64.AADD && p1.As != arm64.ASUB { // TODO(aram): also logical after we have bimm. - continue - } - if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg { - continue - } - if p1.To.Type != obj.TYPE_REG { - continue - } - if gc.Debug['P'] != 0 { - fmt.Printf("encoding $%d directly into %v in:\n%v\n%v\n", p.From.Offset, obj.Aconv(int(p1.As)), p, p1) - } - p1.From.Type = obj.TYPE_CONST - p1.From = p.From - excise(r) - } - - /* TODO(minux): - * look for OP x,y,R; CMP R, $0 -> OP.S x,y,R - * when OP can set condition codes correctly - */ - -ret: - gc.Flowend(g) -} - -func excise(r *gc.Flow) { - p := (*obj.Prog)(r.Prog) - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("%v ===delete===\n", p) - } - obj.Nopout(p) - gc.Ostats.Ndelmov++ -} - -func regtyp(a *obj.Addr) bool { - // TODO(rsc): Floating point register exclusions? - return a.Type == obj.TYPE_REG && arm64.REG_R0 <= a.Reg && a.Reg <= arm64.REG_F31 && a.Reg != arm64.REGZERO -} - -/* - * the idea is to substitute - * one register for another - * from one MOV to another - * MOV a, R1 - * ADD b, R1 / no use of R2 - * MOV R1, R2 - * would be converted to - * MOV a, R2 - * ADD b, R2 - * MOV R2, R1 - * hopefully, then the former or latter MOV - * will be eliminated by copy propagation. - * - * r0 (the argument, not the register) is the MOV at the end of the - * above sequences. This returns 1 if it modified any instructions. - */ -func subprop(r0 *gc.Flow) bool { - p := (*obj.Prog)(r0.Prog) - v1 := (*obj.Addr)(&p.From) - if !regtyp(v1) { - return false - } - v2 := (*obj.Addr)(&p.To) - if !regtyp(v2) { - return false - } - for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { - if gc.Uniqs(r) == nil { - break - } - p = r.Prog - if p.As == obj.AVARDEF || p.As == obj.AVARKILL { - continue - } - if p.Info.Flags&gc.Call != 0 { - return false - } - - if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightWrite { - if p.To.Type == v1.Type { - if p.To.Reg == v1.Reg { - copysub(&p.To, v1, v2, 1) - if gc.Debug['P'] != 0 { - fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog) - if p.From.Type == v2.Type { - fmt.Printf(" excise") - } - fmt.Printf("\n") - } - - for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { - p = r.Prog - copysub(&p.From, v1, v2, 1) - copysub1(p, v1, v2, 1) - copysub(&p.To, v1, v2, 1) - if gc.Debug['P'] != 0 { - fmt.Printf("%v\n", r.Prog) - } - } - - t := int(int(v1.Reg)) - v1.Reg = v2.Reg - v2.Reg = int16(t) - if gc.Debug['P'] != 0 { - fmt.Printf("%v last\n", r.Prog) - } - return true - } - } - } - - if copyau(&p.From, v2) || copyau1(p, v2) || copyau(&p.To, v2) { - break - } - if copysub(&p.From, v1, v2, 0) != 0 || copysub1(p, v1, v2, 0) != 0 || copysub(&p.To, v1, v2, 0) != 0 { - break - } - } - - return false -} - -/* - * The idea is to remove redundant copies. - * v1->v2 F=0 - * (use v2 s/v2/v1/)* - * set v1 F=1 - * use v2 return fail (v1->v2 move must remain) - * ----------------- - * v1->v2 F=0 - * (use v2 s/v2/v1/)* - * set v1 F=1 - * set v2 return success (caller can remove v1->v2 move) - */ -func copyprop(r0 *gc.Flow) bool { - p := (*obj.Prog)(r0.Prog) - v1 := (*obj.Addr)(&p.From) - v2 := (*obj.Addr)(&p.To) - if copyas(v1, v2) { - if gc.Debug['P'] != 0 { - fmt.Printf("eliminating self-move: %v\n", r0.Prog) - } - return true - } - - gactive++ - if gc.Debug['P'] != 0 { - fmt.Printf("trying to eliminate %v->%v move from:\n%v\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r0.Prog) - } - return copy1(v1, v2, r0.S1, 0) -} - -// copy1 replaces uses of v2 with v1 starting at r and returns 1 if -// all uses were rewritten. -func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f int) bool { - if uint32(r.Active) == gactive { - if gc.Debug['P'] != 0 { - fmt.Printf("act set; return 1\n") - } - return true - } - - r.Active = int32(gactive) - if gc.Debug['P'] != 0 { - fmt.Printf("copy1 replace %v with %v f=%d\n", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), f) - } - var t int - var p *obj.Prog - for ; r != nil; r = r.S1 { - p = r.Prog - if gc.Debug['P'] != 0 { - fmt.Printf("%v", p) - } - if f == 0 && gc.Uniqp(r) == nil { - // Multiple predecessors; conservatively - // assume v1 was set on other path - f = 1 - - if gc.Debug['P'] != 0 { - fmt.Printf("; merge; f=%d", f) - } - } - - t = copyu(p, v2, nil) - switch t { - case 2: /* rar, can't split */ - if gc.Debug['P'] != 0 { - fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2)) - } - return false - - case 3: /* set */ - if gc.Debug['P'] != 0 { - fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2)) - } - return true - - case 1, /* used, substitute */ - 4: /* use and set */ - if f != 0 { - if gc.Debug['P'] == 0 { - return false - } - if t == 4 { - fmt.Printf("; %v used+set and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f) - } else { - fmt.Printf("; %v used and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f) - } - return false - } - - if copyu(p, v2, v1) != 0 { - if gc.Debug['P'] != 0 { - fmt.Printf("; sub fail; return 0\n") - } - return false - } - - if gc.Debug['P'] != 0 { - fmt.Printf("; sub %v->%v\n => %v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), p) - } - if t == 4 { - if gc.Debug['P'] != 0 { - fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2)) - } - return true - } - } - - if f == 0 { - t = copyu(p, v1, nil) - if f == 0 && (t == 2 || t == 3 || t == 4) { - f = 1 - if gc.Debug['P'] != 0 { - fmt.Printf("; %v set and !f; f=%d", gc.Ctxt.Dconv(v1), f) - } - } - } - - if gc.Debug['P'] != 0 { - fmt.Printf("\n") - } - if r.S2 != nil { - if !copy1(v1, v2, r.S2, f) { - return false - } - } - } - - return true -} - -// If s==nil, copyu returns the set/use of v in p; otherwise, it -// modifies p to replace reads of v with reads of s and returns 0 for -// success or non-zero for failure. -// -// If s==nil, copy returns one of the following values: -// 1 if v only used -// 2 if v is set and used in one address (read-alter-rewrite; -// can't substitute) -// 3 if v is only set -// 4 if v is set in one address and used in another (so addresses -// can be rewritten independently) -// 0 otherwise (not touched) -func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int { - if p.From3.Type != obj.TYPE_NONE { - // 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 { - // 7g never generates a to2 - fmt.Printf("copyu: to2 (%v) not implemented\n", gc.Ctxt.Dconv(&p.To2)) - } - - switch p.As { - default: - fmt.Printf("copyu: can't find %v\n", obj.Aconv(int(p.As))) - return 2 - - case obj.ANOP, /* read p->from, write p->to */ - arm64.ANEG, - arm64.AFNEGD, - arm64.AFNEGS, - arm64.AFSQRTD, - arm64.AFCVTZSD, - arm64.AFCVTZSS, - arm64.AFCVTZSDW, - arm64.AFCVTZSSW, - arm64.AFCVTZUD, - arm64.AFCVTZUS, - arm64.AFCVTZUDW, - arm64.AFCVTZUSW, - arm64.AFCVTSD, - arm64.AFCVTDS, - arm64.ASCVTFD, - arm64.ASCVTFS, - arm64.ASCVTFWD, - arm64.ASCVTFWS, - arm64.AUCVTFD, - arm64.AUCVTFS, - arm64.AUCVTFWD, - arm64.AUCVTFWS, - arm64.AMOVB, - arm64.AMOVBU, - arm64.AMOVH, - arm64.AMOVHU, - arm64.AMOVW, - arm64.AMOVWU, - arm64.AMOVD, - arm64.AFMOVS, - arm64.AFMOVD: - if p.Scond == 0 { - if s != nil { - if copysub(&p.From, v, s, 1) != 0 { - return 1 - } - - // Update only indirect uses of v in p->to - if !copyas(&p.To, v) { - if copysub(&p.To, v, s, 1) != 0 { - return 1 - } - } - return 0 - } - - if copyas(&p.To, v) { - // Fix up implicit from - if p.From.Type == obj.TYPE_NONE { - p.From = p.To - } - if copyau(&p.From, v) { - return 4 - } - return 3 - } - - if copyau(&p.From, v) { - return 1 - } - if copyau(&p.To, v) { - // p->to only indirectly uses v - return 1 - } - - return 0 - } - - /* rar p->from, write p->to or read p->from, rar p->to */ - if p.From.Type == obj.TYPE_MEM { - if copyas(&p.From, v) { - // No s!=nil check; need to fail - // anyway in that case - return 2 - } - - if s != nil { - if copysub(&p.To, v, s, 1) != 0 { - return 1 - } - return 0 - } - - if copyas(&p.To, v) { - return 3 - } - } else if p.To.Type == obj.TYPE_MEM { - if copyas(&p.To, v) { - return 2 - } - if s != nil { - if copysub(&p.From, v, s, 1) != 0 { - return 1 - } - return 0 - } - - if copyau(&p.From, v) { - return 1 - } - } else { - fmt.Printf("copyu: bad %v\n", p) - } - - return 0 - - case arm64.AADD, /* read p->from, read p->reg, write p->to */ - arm64.ASUB, - arm64.AAND, - arm64.AORR, - arm64.AEOR, - arm64.AMUL, - arm64.ASMULL, - arm64.AUMULL, - arm64.ASMULH, - arm64.AUMULH, - arm64.ASDIV, - arm64.AUDIV, - arm64.ALSL, - arm64.ALSR, - arm64.AASR, - arm64.AFADDD, - arm64.AFADDS, - arm64.AFSUBD, - arm64.AFSUBS, - arm64.AFMULD, - arm64.AFMULS, - arm64.AFDIVD, - arm64.AFDIVS: - if s != nil { - if copysub(&p.From, v, s, 1) != 0 { - return 1 - } - if copysub1(p, v, s, 1) != 0 { - return 1 - } - - // Update only indirect uses of v in p->to - if !copyas(&p.To, v) { - if copysub(&p.To, v, s, 1) != 0 { - return 1 - } - } - return 0 - } - - if copyas(&p.To, v) { - if p.Reg == 0 { - // Fix up implicit reg (e.g., ADD - // R3,R4 -> ADD R3,R4,R4) so we can - // update reg and to separately. - p.Reg = p.To.Reg - } - - if copyau(&p.From, v) { - return 4 - } - if copyau1(p, v) { - return 4 - } - return 3 - } - - if copyau(&p.From, v) { - return 1 - } - if copyau1(p, v) { - return 1 - } - if copyau(&p.To, v) { - return 1 - } - return 0 - - case arm64.ABEQ, - arm64.ABNE, - arm64.ABGE, - arm64.ABLT, - arm64.ABGT, - arm64.ABLE, - arm64.ABLO, - arm64.ABLS, - arm64.ABHI, - arm64.ABHS: - return 0 - - case obj.ACHECKNIL, /* read p->from */ - arm64.ACMP, /* read p->from, read p->reg */ - arm64.AFCMPD, - arm64.AFCMPS: - if s != nil { - if copysub(&p.From, v, s, 1) != 0 { - return 1 - } - return copysub1(p, v, s, 1) - } - - if copyau(&p.From, v) { - return 1 - } - if copyau1(p, v) { - return 1 - } - return 0 - - case arm64.AB: /* read p->to */ - if s != nil { - if copysub(&p.To, v, s, 1) != 0 { - return 1 - } - return 0 - } - - if copyau(&p.To, v) { - return 1 - } - return 0 - - case obj.ARET: /* funny */ - if s != nil { - return 0 - } - - // All registers die at this point, so claim - // everything is set (and not used). - return 3 - - case arm64.ABL: /* funny */ - if p.From.Type == obj.TYPE_REG && v.Type == obj.TYPE_REG && p.From.Reg == v.Reg { - return 2 - } - - if s != nil { - if copysub(&p.To, v, s, 1) != 0 { - return 1 - } - return 0 - } - - if copyau(&p.To, v) { - return 4 - } - return 3 - - // R31 is zero, used by DUFFZERO, cannot be substituted. - // R16 is ptr to memory, used and set, cannot be substituted. - case obj.ADUFFZERO: - if v.Type == obj.TYPE_REG { - if v.Reg == 31 { - return 1 - } - if v.Reg == 16 { - return 2 - } - } - - return 0 - - // R16, R17 are ptr to src, dst, used and set, cannot be substituted. - // R27 is scratch, set by DUFFCOPY, cannot be substituted. - case obj.ADUFFCOPY: - if v.Type == obj.TYPE_REG { - if v.Reg == 16 || v.Reg == 17 { - return 2 - } - if v.Reg == 27 { - return 3 - } - } - - return 0 - - case arm64.AHINT, - obj.ATEXT, - obj.APCDATA, - obj.AFUNCDATA, - obj.AVARDEF, - obj.AVARKILL: - return 0 - } -} - -// copyas returns 1 if a and v address the same register. -// -// If a is the from operand, this means this operation reads the -// register in v. If a is the to operand, this means this operation -// writes the register in v. -func copyas(a *obj.Addr, v *obj.Addr) bool { - if regtyp(v) { - if a.Type == v.Type { - if a.Reg == v.Reg { - return true - } - } - } - return false -} - -// copyau returns 1 if a either directly or indirectly addresses the -// same register as v. -// -// If a is the from operand, this means this operation reads the -// register in v. If a is the to operand, this means the operation -// either reads or writes the register in v (if !copyas(a, v), then -// the operation reads the register in v). -func copyau(a *obj.Addr, v *obj.Addr) bool { - if copyas(a, v) { - return true - } - if v.Type == obj.TYPE_REG { - if a.Type == obj.TYPE_MEM || (a.Type == obj.TYPE_ADDR && a.Reg != 0) { - if v.Reg == a.Reg { - return true - } - } - } - return false -} - -// copyau1 returns 1 if p->reg references the same register as v and v -// is a direct reference. -func copyau1(p *obj.Prog, v *obj.Addr) bool { - if regtyp(v) && v.Reg != 0 { - if p.Reg == v.Reg { - return true - } - } - return false -} - -// copysub replaces v with s in a if f!=0 or indicates it if could if f==0. -// Returns 1 on failure to substitute (it always succeeds on arm64). -func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f int) int { - if f != 0 { - if copyau(a, v) { - a.Reg = s.Reg - } - } - return 0 -} - -// copysub1 replaces v with s in p1->reg if f!=0 or indicates if it could if f==0. -// Returns 1 on failure to substitute (it always succeeds on arm64). -func copysub1(p1 *obj.Prog, v *obj.Addr, s *obj.Addr, f int) int { - if f != 0 { - if copyau1(p1, v) { - p1.Reg = s.Reg - } - } - return 0 -} - -func sameaddr(a *obj.Addr, v *obj.Addr) bool { - if a.Type != v.Type { - return false - } - if regtyp(v) && a.Reg == v.Reg { - return true - } - if v.Type == obj.NAME_AUTO || v.Type == obj.NAME_PARAM { - if v.Offset == a.Offset { - return true - } - } - return false -} - -func smallindir(a *obj.Addr, reg *obj.Addr) bool { - return reg.Type == obj.TYPE_REG && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && 0 <= a.Offset && a.Offset < 4096 -} - -func stackaddr(a *obj.Addr) bool { - return a.Type == obj.TYPE_REG && a.Reg == arm64.REGSP -} diff --git a/src/cmd/7g/prog.go b/src/cmd/7g/prog.go deleted file mode 100644 index 023f302e14..0000000000 --- a/src/cmd/7g/prog.go +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright 2014 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/gc" - "cmd/internal/obj" - "cmd/internal/obj/arm64" -) - -const ( - LeftRdwr uint32 = gc.LeftRead | gc.LeftWrite - RightRdwr uint32 = gc.RightRead | gc.RightWrite -) - -// This table gives the basic information about instruction -// generated by the compiler and processed in the optimizer. -// See opt.h for bit definitions. -// -// Instructions not generated need not be listed. -// As an exception to that rule, we typically write down all the -// size variants of an operation even if we just use a subset. -// -// The table is formatted for 8-space tabs. -var progtable = [arm64.ALAST]obj.ProgInfo{ - obj.ATYPE: {gc.Pseudo | gc.Skip, 0, 0, 0}, - obj.ATEXT: {gc.Pseudo, 0, 0, 0}, - obj.AFUNCDATA: {gc.Pseudo, 0, 0, 0}, - obj.APCDATA: {gc.Pseudo, 0, 0, 0}, - obj.AUNDEF: {gc.Break, 0, 0, 0}, - obj.AUSEFIELD: {gc.OK, 0, 0, 0}, - obj.ACHECKNIL: {gc.LeftRead, 0, 0, 0}, - obj.AVARDEF: {gc.Pseudo | gc.RightWrite, 0, 0, 0}, - obj.AVARKILL: {gc.Pseudo | gc.RightWrite, 0, 0, 0}, - - // NOP is an internal no-op that also stands - // for USED and SET annotations, not the Power opcode. - obj.ANOP: {gc.LeftRead | gc.RightWrite, 0, 0, 0}, - arm64.AHINT: {gc.OK, 0, 0, 0}, - - // Integer - arm64.AADD: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm64.ASUB: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm64.ANEG: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm64.AAND: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm64.AORR: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm64.AEOR: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm64.AMUL: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm64.ASMULL: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm64.AUMULL: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm64.ASMULH: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm64.AUMULH: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm64.ASDIV: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm64.AUDIV: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm64.ALSL: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm64.ALSR: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm64.AASR: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm64.ACMP: {gc.SizeQ | gc.LeftRead | gc.RegRead, 0, 0, 0}, - - // Floating point. - arm64.AFADDD: {gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm64.AFADDS: {gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm64.AFSUBD: {gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm64.AFSUBS: {gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm64.AFNEGD: {gc.SizeD | gc.LeftRead | gc.RightWrite, 0, 0, 0}, - arm64.AFNEGS: {gc.SizeF | gc.LeftRead | gc.RightWrite, 0, 0, 0}, - arm64.AFSQRTD: {gc.SizeD | gc.LeftRead | gc.RightWrite, 0, 0, 0}, - arm64.AFMULD: {gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm64.AFMULS: {gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm64.AFDIVD: {gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm64.AFDIVS: {gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - arm64.AFCMPD: {gc.SizeD | gc.LeftRead | gc.RegRead, 0, 0, 0}, - arm64.AFCMPS: {gc.SizeF | gc.LeftRead | gc.RegRead, 0, 0, 0}, - - // float -> integer - arm64.AFCVTZSD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - arm64.AFCVTZSS: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - arm64.AFCVTZSDW: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - arm64.AFCVTZSSW: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - arm64.AFCVTZUD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - arm64.AFCVTZUS: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - arm64.AFCVTZUDW: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - arm64.AFCVTZUSW: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - - // float -> float - arm64.AFCVTSD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - arm64.AFCVTDS: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - - // integer -> float - arm64.ASCVTFD: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - arm64.ASCVTFS: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - arm64.ASCVTFWD: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - arm64.ASCVTFWS: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - arm64.AUCVTFD: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - arm64.AUCVTFS: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - arm64.AUCVTFWD: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - arm64.AUCVTFWS: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - - // Moves - arm64.AMOVB: {gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv, 0, 0, 0}, - arm64.AMOVBU: {gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv, 0, 0, 0}, - arm64.AMOVH: {gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv, 0, 0, 0}, - arm64.AMOVHU: {gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv, 0, 0, 0}, - arm64.AMOVW: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv, 0, 0, 0}, - arm64.AMOVWU: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv, 0, 0, 0}, - arm64.AMOVD: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, - arm64.AFMOVS: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv, 0, 0, 0}, - arm64.AFMOVD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, - - // Jumps - arm64.AB: {gc.Jump | gc.Break, 0, 0, 0}, - arm64.ABL: {gc.Call, 0, 0, 0}, - arm64.ABEQ: {gc.Cjmp, 0, 0, 0}, - arm64.ABNE: {gc.Cjmp, 0, 0, 0}, - arm64.ABGE: {gc.Cjmp, 0, 0, 0}, - arm64.ABLT: {gc.Cjmp, 0, 0, 0}, - arm64.ABGT: {gc.Cjmp, 0, 0, 0}, - arm64.ABLE: {gc.Cjmp, 0, 0, 0}, - arm64.ABLO: {gc.Cjmp, 0, 0, 0}, - arm64.ABLS: {gc.Cjmp, 0, 0, 0}, - arm64.ABHI: {gc.Cjmp, 0, 0, 0}, - arm64.ABHS: {gc.Cjmp, 0, 0, 0}, - arm64.ACBZ: {gc.Cjmp, 0, 0, 0}, - arm64.ACBNZ: {gc.Cjmp, 0, 0, 0}, - obj.ARET: {gc.Break, 0, 0, 0}, - obj.ADUFFZERO: {gc.Call, 0, 0, 0}, - obj.ADUFFCOPY: {gc.Call, 0, 0, 0}, -} - -func proginfo(p *obj.Prog) { - info := &p.Info - *info = progtable[p.As] - if info.Flags == 0 { - gc.Fatal("proginfo: unknown instruction %v", p) - } - - if (info.Flags&gc.RegRead != 0) && p.Reg == 0 { - info.Flags &^= gc.RegRead - info.Flags |= gc.RightRead /*CanRegRead |*/ - } - - if (p.From.Type == obj.TYPE_MEM || p.From.Type == obj.TYPE_ADDR) && p.From.Reg != 0 { - info.Regindex |= RtoB(int(p.From.Reg)) - if p.Scond != 0 { - info.Regset |= RtoB(int(p.From.Reg)) - } - } - - if (p.To.Type == obj.TYPE_MEM || p.To.Type == obj.TYPE_ADDR) && p.To.Reg != 0 { - info.Regindex |= RtoB(int(p.To.Reg)) - if p.Scond != 0 { - info.Regset |= RtoB(int(p.To.Reg)) - } - } - - if p.From.Type == obj.TYPE_ADDR && p.From.Sym != nil && (info.Flags&gc.LeftRead != 0) { - info.Flags &^= gc.LeftRead - info.Flags |= gc.LeftAddr - } - - if p.As == obj.ADUFFZERO { - info.Reguse |= RtoB(arm64.REGRT1) - info.Regset |= RtoB(arm64.REGRT1) - } - - if p.As == obj.ADUFFCOPY { - // TODO(austin) Revisit when duffcopy is implemented - info.Reguse |= RtoB(arm64.REGRT1) | RtoB(arm64.REGRT2) | RtoB(arm64.REG_R5) - - info.Regset |= RtoB(arm64.REGRT1) | RtoB(arm64.REGRT2) - } -} diff --git a/src/cmd/7g/reg.go b/src/cmd/7g/reg.go deleted file mode 100644 index 0e5ac73499..0000000000 --- a/src/cmd/7g/reg.go +++ /dev/null @@ -1,171 +0,0 @@ -// Derived from Inferno utils/6c/reg.c -// http://code.google.com/p/inferno-os/source/browse/utils/6c/reg.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package main - -import ( - "cmd/internal/gc" - "cmd/internal/obj/arm64" -) - -const ( - NREGVAR = 64 /* 32 general + 32 floating */ -) - -var reg [arm64.NREG + arm64.NFREG]uint8 - -var regname = []string{ - ".R0", - ".R1", - ".R2", - ".R3", - ".R4", - ".R5", - ".R6", - ".R7", - ".R8", - ".R9", - ".R10", - ".R11", - ".R12", - ".R13", - ".R14", - ".R15", - ".R16", - ".R17", - ".R18", - ".R19", - ".R20", - ".R21", - ".R22", - ".R23", - ".R24", - ".R25", - ".R26", - ".R27", - ".R28", - ".R29", - ".R30", - ".R31", - ".F0", - ".F1", - ".F2", - ".F3", - ".F4", - ".F5", - ".F6", - ".F7", - ".F8", - ".F9", - ".F10", - ".F11", - ".F12", - ".F13", - ".F14", - ".F15", - ".F16", - ".F17", - ".F18", - ".F19", - ".F20", - ".F21", - ".F22", - ".F23", - ".F24", - ".F25", - ".F26", - ".F27", - ".F28", - ".F29", - ".F30", - ".F31", -} - -func regnames(n *int) []string { - *n = NREGVAR - return regname -} - -func excludedregs() uint64 { - // Exclude registers with fixed functions - regbits := uint64(RtoB(arm64.REGRT1) | RtoB(arm64.REGRT2) | RtoB(arm64.REGPR)) - - // Exclude R26 - R31. - for r := arm64.REGMAX + 1; r <= arm64.REGZERO; r++ { - regbits |= RtoB(r) - } - - // Also exclude floating point registers with fixed constants - regbits |= RtoB(arm64.REG_F27) | RtoB(arm64.REG_F28) | RtoB(arm64.REG_F29) | RtoB(arm64.REG_F30) | RtoB(arm64.REG_F31) - - return regbits -} - -func doregbits(r int) uint64 { - return 0 -} - -/* - * track register variables including external registers: - * bit reg - * 0 R0 - * 1 R1 - * ... ... - * 31 R31 - * 32+0 F0 - * 32+1 F1 - * ... ... - * 32+31 F31 - */ -func RtoB(r int) uint64 { - if r >= arm64.REG_R0 && r <= arm64.REG_R31 { - return 1 << uint(r-arm64.REG_R0) - } - if r >= arm64.REG_F0 && r <= arm64.REG_F31 { - return 1 << uint(32+r-arm64.REG_F0) - } - return 0 -} - -func BtoR(b uint64) int { - b &= 0xffffffff - if b == 0 { - return 0 - } - return gc.Bitno(b) + arm64.REG_R0 -} - -func BtoF(b uint64) int { - b >>= 32 - if b == 0 { - return 0 - } - return gc.Bitno(b) + arm64.REG_F0 -} diff --git a/src/cmd/7l/asm.go b/src/cmd/7l/asm.go deleted file mode 100644 index 064ff56283..0000000000 --- a/src/cmd/7l/asm.go +++ /dev/null @@ -1,435 +0,0 @@ -// Inferno utils/5l/asm.c -// http://code.google.com/p/inferno-os/source/browse/utils/5l/asm.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package main - -import ( - "cmd/internal/ld" - "cmd/internal/obj" - "encoding/binary" - "fmt" - "log" -) - -func gentext() {} - -func adddynrela(rel *ld.LSym, s *ld.LSym, r *ld.Reloc) { - log.Fatalf("adddynrela not implemented") -} - -func adddynrel(s *ld.LSym, r *ld.Reloc) { - log.Fatalf("adddynrel not implemented") -} - -func elfreloc1(r *ld.Reloc, sectoff int64) int { - ld.Thearch.Vput(uint64(sectoff)) - - elfsym := r.Xsym.Elfsym - switch r.Type { - default: - return -1 - - case obj.R_ADDR: - switch r.Siz { - case 4: - ld.Thearch.Vput(ld.R_AARCH64_ABS32 | uint64(elfsym)<<32) - case 8: - ld.Thearch.Vput(ld.R_AARCH64_ABS64 | uint64(elfsym)<<32) - default: - return -1 - } - - case obj.R_ADDRARM64: - // two relocations: R_AARCH64_ADR_PREL_PG_HI21 and R_AARCH64_ADD_ABS_LO12_NC - ld.Thearch.Vput(ld.R_AARCH64_ADR_PREL_PG_HI21 | uint64(elfsym)<<32) - ld.Thearch.Vput(uint64(r.Xadd)) - ld.Thearch.Vput(uint64(sectoff + 4)) - ld.Thearch.Vput(ld.R_AARCH64_ADD_ABS_LO12_NC | uint64(elfsym)<<32) - - case obj.R_CALLARM64: - if r.Siz != 4 { - return -1 - } - ld.Thearch.Vput(ld.R_AARCH64_CALL26 | uint64(elfsym)<<32) - - } - ld.Thearch.Vput(uint64(r.Xadd)) - - return 0 -} - -func elfsetupplt() { - // TODO(aram) - return -} - -func machoreloc1(r *ld.Reloc, sectoff int64) int { - var v uint32 - - rs := r.Xsym - - // ld64 has a bug handling MACHO_ARM64_RELOC_UNSIGNED with !extern relocation. - // see cmd/internal/ld/data.go for details. The workarond is that don't use !extern - // UNSIGNED relocation at all. - if rs.Type == obj.SHOSTOBJ || r.Type == obj.R_CALLARM64 || r.Type == obj.R_ADDRARM64 || r.Type == obj.R_ADDR { - if rs.Dynid < 0 { - ld.Diag("reloc %d to non-macho symbol %s type=%d", r.Type, rs.Name, rs.Type) - return -1 - } - - v = uint32(rs.Dynid) - v |= 1 << 27 // external relocation - } else { - v = uint32((rs.Sect.(*ld.Section)).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) - return -1 - } - } - - switch r.Type { - default: - return -1 - - case obj.R_ADDR: - v |= ld.MACHO_ARM64_RELOC_UNSIGNED << 28 - - case obj.R_CALLARM64: - if r.Xadd != 0 { - ld.Diag("ld64 doesn't allow BR26 reloc with non-zero addend: %s+%d", rs.Name, r.Xadd) - } - - v |= 1 << 24 // pc-relative bit - v |= ld.MACHO_ARM64_RELOC_BRANCH26 << 28 - - case obj.R_ADDRARM64: - r.Siz = 4 - // Two relocation entries: MACHO_ARM64_RELOC_PAGEOFF12 MACHO_ARM64_RELOC_PAGE21 - // if r.Xadd is non-zero, add two MACHO_ARM64_RELOC_ADDEND. - if r.Xadd != 0 { - ld.Thearch.Lput(uint32(sectoff + 4)) - ld.Thearch.Lput((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(r.Xadd&0xffffff)) - } - ld.Thearch.Lput(uint32(sectoff + 4)) - ld.Thearch.Lput(v | (ld.MACHO_ARM64_RELOC_PAGEOFF12 << 28) | (2 << 25)) - if r.Xadd != 0 { - ld.Thearch.Lput(uint32(sectoff)) - ld.Thearch.Lput((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(r.Xadd&0xffffff)) - } - v |= 1 << 24 // pc-relative bit - v |= ld.MACHO_ARM64_RELOC_PAGE21 << 28 - } - - switch r.Siz { - default: - return -1 - - case 1: - v |= 0 << 25 - - case 2: - v |= 1 << 25 - - case 4: - v |= 2 << 25 - - case 8: - v |= 3 << 25 - } - - ld.Thearch.Lput(uint32(sectoff)) - ld.Thearch.Lput(v) - return 0 -} - -func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int { - if ld.Linkmode == ld.LinkExternal { - switch r.Type { - default: - return -1 - - case obj.R_ADDRARM64: - r.Done = 0 - - // set up addend for eventual relocation via outer symbol. - rs := r.Sym - r.Xadd = r.Add - for rs.Outer != nil { - r.Xadd += ld.Symaddr(rs) - ld.Symaddr(rs.Outer) - rs = rs.Outer - } - - if rs.Type != obj.SHOSTOBJ && rs.Sect == nil { - ld.Diag("missing section for %s", rs.Name) - } - r.Xsym = rs - - // the first instruction is always at the lower address, this is endian neutral; - // but note that o0 and o1 should still use the target endian. - o0 := ld.Thelinkarch.ByteOrder.Uint32(s.P[r.Off : r.Off+4]) - o1 := ld.Thelinkarch.ByteOrder.Uint32(s.P[r.Off+4 : r.Off+8]) - - // Note: ld64 currently has a bug that any non-zero addend for BR26 relocation - // will make the linking fail because it thinks the code is not PIC even though - // the BR26 relocation should be fully resolved at link time. - // That is the reason why the next if block is disabled. When the bug in ld64 - // is fixed, we can enable this block and also enable duff's device in cmd/7g. - if false && ld.HEADTYPE == obj.Hdarwin { - // Mach-O wants the addend to be encoded in the instruction - // Note that although Mach-O supports ARM64_RELOC_ADDEND, it - // can only encode 24-bit of signed addend, but the instructions - // supports 33-bit of signed addend, so we always encode the - // addend in place. - o0 |= (uint32((r.Xadd>>12)&3) << 29) | (uint32((r.Xadd>>12>>2)&0x7ffff) << 5) - o1 |= uint32(r.Xadd&0xfff) << 10 - r.Xadd = 0 - } - - // when laid out, the instruction order must always be o1, o2. - if ld.Ctxt.Arch.ByteOrder == binary.BigEndian { - *val = int64(o0)<<32 | int64(o1) - } else { - *val = int64(o1)<<32 | int64(o0) - } - - return 0 - - case obj.R_CALLARM64: - r.Done = 0 - r.Xsym = r.Sym - *val = int64(0xfc000000 & uint32(r.Add)) - r.Xadd = int64((uint32(r.Add) &^ 0xfc000000) * 4) - r.Add = 0 - return 0 - } - } - - switch r.Type { - case obj.R_CONST: - *val = r.Add - return 0 - - case obj.R_GOTOFF: - *val = ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ld.Linklookup(ld.Ctxt, ".got", 0)) - return 0 - - case obj.R_ADDRARM64: - t := ld.Symaddr(r.Sym) + r.Add - ((s.Value + int64(r.Off)) &^ 0xfff) - if t >= 1<<32 || t < -1<<32 { - ld.Diag("program too large, address relocation distance = %d", t) - } - - // the first instruction is always at the lower address, this is endian neutral; - // but note that o0 and o1 should still use the target endian. - o0 := ld.Thelinkarch.ByteOrder.Uint32(s.P[r.Off : r.Off+4]) - o1 := ld.Thelinkarch.ByteOrder.Uint32(s.P[r.Off+4 : r.Off+8]) - - o0 |= (uint32((t>>12)&3) << 29) | (uint32((t>>12>>2)&0x7ffff) << 5) - o1 |= uint32(t&0xfff) << 10 - - // when laid out, the instruction order must always be o1, o2. - if ld.Ctxt.Arch.ByteOrder == binary.BigEndian { - *val = int64(o0)<<32 | int64(o1) - } else { - *val = int64(o1)<<32 | int64(o0) - } - return 0 - - case obj.R_CALLARM64: - *val = int64((0xfc000000 & uint32(r.Add)) | uint32((ld.Symaddr(r.Sym)+r.Add*4-(s.Value+int64(r.Off)))/4)) - return 0 - } - - return -1 -} - -func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 { - log.Fatalf("unexpected relocation variant") - return -1 -} - -func asmb() { - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime()) - } - ld.Bso.Flush() - - if ld.Iself { - ld.Asmbelfsetup() - } - - sect := ld.Segtext.Sect - ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) - ld.Codeblk(int64(sect.Vaddr), int64(sect.Length)) - for sect = sect.Next; sect != nil; sect = sect.Next { - ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) - ld.Datblk(int64(sect.Vaddr), int64(sect.Length)) - } - - if ld.Segrodata.Filelen > 0 { - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) - } - ld.Bso.Flush() - - ld.Cseek(int64(ld.Segrodata.Fileoff)) - ld.Datblk(int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) - } - - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f datblk\n", obj.Cputime()) - } - ld.Bso.Flush() - - ld.Cseek(int64(ld.Segdata.Fileoff)) - ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) - - machlink := uint32(0) - if ld.HEADTYPE == obj.Hdarwin { - if ld.Debug['v'] != 0 { - 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)) - - ld.Segdwarf.Fileoff = uint64(ld.Cpos()) - ld.Dwarfemitdebugsections() - ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff - - machlink = uint32(ld.Domacholink()) - } - - /* output symbol table */ - ld.Symsize = 0 - - ld.Lcsize = 0 - symo := uint32(0) - if ld.Debug['s'] == 0 { - // TODO: rationalize - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime()) - } - ld.Bso.Flush() - switch ld.HEADTYPE { - default: - if ld.Iself { - symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) - symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND))) - } - - case obj.Hplan9: - 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)) - } - - ld.Cseek(int64(symo)) - switch ld.HEADTYPE { - default: - if ld.Iself { - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f elfsym\n", obj.Cputime()) - } - ld.Asmelfsym() - ld.Cflush() - ld.Cwrite(ld.Elfstrdat) - - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) - } - ld.Dwarfemitdebugsections() - - if ld.Linkmode == ld.LinkExternal { - ld.Elfemitreloc() - } - } - - case obj.Hplan9: - ld.Asmplan9sym() - ld.Cflush() - - sym := ld.Linklookup(ld.Ctxt, "pclntab", 0) - if sym != nil { - ld.Lcsize = int32(len(sym.P)) - for i := 0; int32(i) < ld.Lcsize; i++ { - ld.Cput(uint8(sym.P[i])) - } - - ld.Cflush() - } - - case obj.Hdarwin: - if ld.Linkmode == ld.LinkExternal { - ld.Machoemitreloc() - } - } - } - - ld.Ctxt.Cursym = nil - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f header\n", obj.Cputime()) - } - ld.Bso.Flush() - ld.Cseek(0) - switch ld.HEADTYPE { - default: - case obj.Hplan9: /* plan 9 */ - ld.Thearch.Lput(0x647) /* magic */ - ld.Thearch.Lput(uint32(ld.Segtext.Filelen)) /* sizes */ - ld.Thearch.Lput(uint32(ld.Segdata.Filelen)) - ld.Thearch.Lput(uint32(ld.Segdata.Length - ld.Segdata.Filelen)) - ld.Thearch.Lput(uint32(ld.Symsize)) /* nsyms */ - ld.Thearch.Lput(uint32(ld.Entryvalue())) /* va of entry */ - ld.Thearch.Lput(0) - ld.Thearch.Lput(uint32(ld.Lcsize)) - - case obj.Hlinux, - obj.Hfreebsd, - obj.Hnetbsd, - obj.Hopenbsd, - obj.Hnacl: - ld.Asmbelf(int64(symo)) - - case obj.Hdarwin: - ld.Asmbmacho() - } - - ld.Cflush() - if ld.Debug['c'] != 0 { - fmt.Printf("textsize=%d\n", ld.Segtext.Filelen) - fmt.Printf("datsize=%d\n", ld.Segdata.Filelen) - fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen) - fmt.Printf("symsize=%d\n", ld.Symsize) - fmt.Printf("lcsize=%d\n", ld.Lcsize) - fmt.Printf("total=%d\n", ld.Segtext.Filelen+ld.Segdata.Length+uint64(ld.Symsize)+uint64(ld.Lcsize)) - } -} diff --git a/src/cmd/7l/l.go b/src/cmd/7l/l.go deleted file mode 100644 index 7227cc430f..0000000000 --- a/src/cmd/7l/l.go +++ /dev/null @@ -1,78 +0,0 @@ -// Inferno utils/5l/asm.c -// http://code.google.com/p/inferno-os/source/browse/utils/5l/asm.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package main - -// Writing object files. - -// cmd/9l/l.h from Vita Nuova. -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -const ( - thechar = '7' - PtrSize = 8 - IntSize = 8 - RegSize = 8 - MaxAlign = 32 // max data alignment - FuncAlign = 8 - MINLC = 4 -) - -/* 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 deleted file mode 100644 index f88584b938..0000000000 --- a/src/cmd/7l/obj.go +++ /dev/null @@ -1,169 +0,0 @@ -// Inferno utils/5l/obj.c -// http://code.google.com/p/inferno-os/source/browse/utils/5l/obj.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package main - -import ( - "cmd/internal/ld" - "cmd/internal/obj" - "fmt" - "log" -) - -// Reading object files. - -func main() { - linkarchinit() - ld.Ldmain() -} - -func linkarchinit() { - ld.Thestring = obj.Getgoarch() - ld.Thelinkarch = &ld.Linkarm64 - - ld.Thearch.Thechar = thechar - ld.Thearch.Ptrsize = ld.Thelinkarch.Ptrsize - ld.Thearch.Intsize = ld.Thelinkarch.Ptrsize - ld.Thearch.Regsize = ld.Thelinkarch.Regsize - ld.Thearch.Funcalign = FuncAlign - ld.Thearch.Maxalign = MaxAlign - ld.Thearch.Minlc = MINLC - ld.Thearch.Dwarfregsp = DWARFREGSP - ld.Thearch.Dwarfreglr = DWARFREGLR - - ld.Thearch.Adddynrel = adddynrel - ld.Thearch.Archinit = archinit - ld.Thearch.Archreloc = archreloc - ld.Thearch.Archrelocvariant = archrelocvariant - ld.Thearch.Asmb = asmb - ld.Thearch.Elfreloc1 = elfreloc1 - ld.Thearch.Elfsetupplt = elfsetupplt - ld.Thearch.Gentext = gentext - ld.Thearch.Machoreloc1 = machoreloc1 - ld.Thearch.Lput = ld.Lputl - ld.Thearch.Wput = ld.Wputl - ld.Thearch.Vput = ld.Vputl - - ld.Thearch.Linuxdynld = "/lib/ld-linux-aarch64.so.1" - - ld.Thearch.Freebsddynld = "XXX" - ld.Thearch.Openbsddynld = "XXX" - ld.Thearch.Netbsddynld = "XXX" - ld.Thearch.Dragonflydynld = "XXX" - ld.Thearch.Solarisdynld = "XXX" -} - -func archinit() { - // getgoextlinkenabled is based on GO_EXTLINK_ENABLED when - // Go was built; see ../../make.bash. - if ld.Linkmode == ld.LinkAuto && obj.Getgoextlinkenabled() == "0" { - ld.Linkmode = ld.LinkInternal - } - - // Darwin/arm64 only supports external linking - if ld.HEADTYPE == obj.Hdarwin { - ld.Linkmode = ld.LinkExternal - } - - switch ld.HEADTYPE { - default: - if ld.Linkmode == ld.LinkAuto { - ld.Linkmode = ld.LinkInternal - } - if ld.Linkmode == ld.LinkExternal && obj.Getgoextlinkenabled() != "1" { - log.Fatalf("cannot use -linkmode=external with -H %s", ld.Headstr(int(ld.HEADTYPE))) - } - case obj.Hlinux, obj.Hdarwin: - break - } - - switch ld.HEADTYPE { - default: - ld.Exitf("unknown -H option: %v", ld.HEADTYPE) - - case obj.Hplan9: /* plan 9 */ - ld.HEADR = 32 - - if ld.INITTEXT == -1 { - ld.INITTEXT = 4128 - } - if ld.INITDAT == -1 { - ld.INITDAT = 0 - } - if ld.INITRND == -1 { - ld.INITRND = 4096 - } - - case obj.Hlinux: /* arm64 elf */ - ld.Elfinit() - ld.HEADR = ld.ELFRESERVE - if ld.INITTEXT == -1 { - ld.INITTEXT = 0x10000 + int64(ld.HEADR) - } - if ld.INITDAT == -1 { - ld.INITDAT = 0 - } - if ld.INITRND == -1 { - ld.INITRND = 0x10000 - } - - case obj.Hdarwin: /* apple MACH */ - ld.Debug['w'] = 1 // disable DWARF generation - ld.Machoinit() - ld.HEADR = ld.INITIAL_MACHO_HEADR - if ld.INITTEXT == -1 { - ld.INITTEXT = 4096 + int64(ld.HEADR) - } - if ld.INITDAT == -1 { - ld.INITDAT = 0 - } - if ld.INITRND == -1 { - ld.INITRND = 4096 - } - - case obj.Hnacl: - ld.Elfinit() - ld.HEADR = 0x10000 - ld.Funcalign = 16 - if ld.INITTEXT == -1 { - ld.INITTEXT = 0x20000 - } - if ld.INITDAT == -1 { - ld.INITDAT = 0 - } - if ld.INITRND == -1 { - ld.INITRND = 0x10000 - } - } - - if ld.INITDAT != 0 && ld.INITRND != 0 { - fmt.Printf("warning: -D0x%x is ignored because of -R0x%x\n", uint64(ld.INITDAT), uint32(ld.INITRND)) - } -} diff --git a/src/cmd/8g/cgen.go b/src/cmd/8g/cgen.go deleted file mode 100644 index 48d9e9867a..0000000000 --- a/src/cmd/8g/cgen.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2009 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/gc" - "cmd/internal/obj" - "cmd/internal/obj/x86" -) - -/* - * generate an addressable node in res, containing the value of n. - * n is an array index, and might be any size; res width is <= 32-bit. - * returns Prog* to patch to panic call. - */ -func igenindex(n *gc.Node, res *gc.Node, bounded bool) *obj.Prog { - if !gc.Is64(n.Type) { - if n.Addable && (gc.Simtype[n.Etype] == gc.TUINT32 || gc.Simtype[n.Etype] == gc.TINT32) { - // nothing to do. - *res = *n - } else { - gc.Tempname(res, gc.Types[gc.TUINT32]) - gc.Cgen(n, res) - } - - return nil - } - - var tmp gc.Node - gc.Tempname(&tmp, gc.Types[gc.TINT64]) - gc.Cgen(n, &tmp) - var lo gc.Node - var hi gc.Node - split64(&tmp, &lo, &hi) - gc.Tempname(res, gc.Types[gc.TUINT32]) - gmove(&lo, res) - if bounded { - splitclean() - return nil - } - - var zero gc.Node - gc.Nodconst(&zero, gc.Types[gc.TINT32], 0) - gins(x86.ACMPL, &hi, &zero) - splitclean() - return gc.Gbranch(x86.AJNE, nil, +1) -} - -func blockcopy(n, res *gc.Node, osrc, odst, w int64) { - var dst gc.Node - gc.Nodreg(&dst, gc.Types[gc.Tptr], x86.REG_DI) - var src gc.Node - gc.Nodreg(&src, gc.Types[gc.Tptr], x86.REG_SI) - - var tsrc gc.Node - gc.Tempname(&tsrc, gc.Types[gc.Tptr]) - var tdst gc.Node - gc.Tempname(&tdst, gc.Types[gc.Tptr]) - if !n.Addable { - gc.Agen(n, &tsrc) - } - if !res.Addable { - gc.Agen(res, &tdst) - } - if n.Addable { - gc.Agen(n, &src) - } else { - gmove(&tsrc, &src) - } - - if res.Op == gc.ONAME { - gc.Gvardef(res) - } - - if res.Addable { - gc.Agen(res, &dst) - } else { - gmove(&tdst, &dst) - } - - c := int32(w % 4) // bytes - q := int32(w / 4) // doublewords - - // if we are copying forward on the stack and - // the src and dst overlap, then reverse direction - if osrc < odst && int64(odst) < int64(osrc)+w { - // reverse direction - gins(x86.ASTD, nil, nil) // set direction flag - if c > 0 { - gconreg(x86.AADDL, w-1, x86.REG_SI) - gconreg(x86.AADDL, w-1, x86.REG_DI) - - gconreg(x86.AMOVL, int64(c), x86.REG_CX) - gins(x86.AREP, nil, nil) // repeat - gins(x86.AMOVSB, nil, nil) // MOVB *(SI)-,*(DI)- - } - - if q > 0 { - if c > 0 { - gconreg(x86.AADDL, -3, x86.REG_SI) - gconreg(x86.AADDL, -3, x86.REG_DI) - } else { - gconreg(x86.AADDL, w-4, x86.REG_SI) - gconreg(x86.AADDL, w-4, x86.REG_DI) - } - - gconreg(x86.AMOVL, int64(q), x86.REG_CX) - gins(x86.AREP, nil, nil) // repeat - gins(x86.AMOVSL, nil, nil) // MOVL *(SI)-,*(DI)- - } - - // we leave with the flag clear - gins(x86.ACLD, nil, nil) - } else { - gins(x86.ACLD, nil, nil) // paranoia. TODO(rsc): remove? - - // normal direction - if q > 128 || (q >= 4 && gc.Nacl) { - gconreg(x86.AMOVL, int64(q), x86.REG_CX) - gins(x86.AREP, nil, nil) // repeat - gins(x86.AMOVSL, nil, nil) // MOVL *(SI)+,*(DI)+ - } else if q >= 4 { - p := gins(obj.ADUFFCOPY, nil, nil) - p.To.Type = obj.TYPE_ADDR - p.To.Sym = gc.Linksym(gc.Pkglookup("duffcopy", gc.Runtimepkg)) - - // 10 and 128 = magic constants: see ../../runtime/asm_386.s - p.To.Offset = 10 * (128 - int64(q)) - } else if !gc.Nacl && c == 0 { - var cx gc.Node - gc.Nodreg(&cx, gc.Types[gc.TINT32], x86.REG_CX) - - // We don't need the MOVSL side-effect of updating SI and DI, - // and issuing a sequence of MOVLs directly is faster. - src.Op = gc.OINDREG - - dst.Op = gc.OINDREG - for q > 0 { - gmove(&src, &cx) // MOVL x+(SI),CX - gmove(&cx, &dst) // MOVL CX,x+(DI) - src.Xoffset += 4 - dst.Xoffset += 4 - q-- - } - } else { - for q > 0 { - gins(x86.AMOVSL, nil, nil) // MOVL *(SI)+,*(DI)+ - q-- - } - } - - for c > 0 { - gins(x86.AMOVSB, nil, nil) // MOVB *(SI)+,*(DI)+ - c-- - } - } -} diff --git a/src/cmd/8g/cgen64.go b/src/cmd/8g/cgen64.go deleted file mode 100644 index 80a9642f75..0000000000 --- a/src/cmd/8g/cgen64.go +++ /dev/null @@ -1,598 +0,0 @@ -// Copyright 2009 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/gc" - "cmd/internal/obj" - "cmd/internal/obj/x86" -) - -/* - * attempt to generate 64-bit - * res = n - * return 1 on success, 0 if op not handled. - */ -func cgen64(n *gc.Node, res *gc.Node) { - if res.Op != gc.OINDREG && res.Op != gc.ONAME { - gc.Dump("n", n) - gc.Dump("res", res) - gc.Fatal("cgen64 %v of %v", gc.Oconv(int(n.Op), 0), gc.Oconv(int(res.Op), 0)) - } - - switch n.Op { - default: - gc.Fatal("cgen64 %v", gc.Oconv(int(n.Op), 0)) - - case gc.OMINUS: - gc.Cgen(n.Left, res) - var hi1 gc.Node - var lo1 gc.Node - split64(res, &lo1, &hi1) - gins(x86.ANEGL, nil, &lo1) - gins(x86.AADCL, ncon(0), &hi1) - gins(x86.ANEGL, nil, &hi1) - splitclean() - return - - case gc.OCOM: - gc.Cgen(n.Left, res) - var lo1 gc.Node - var hi1 gc.Node - split64(res, &lo1, &hi1) - gins(x86.ANOTL, nil, &lo1) - gins(x86.ANOTL, nil, &hi1) - splitclean() - return - - // binary operators. - // common setup below. - case gc.OADD, - gc.OSUB, - gc.OMUL, - gc.OLROT, - gc.OLSH, - gc.ORSH, - gc.OAND, - gc.OOR, - gc.OXOR: - break - } - - l := n.Left - r := n.Right - if !l.Addable { - var t1 gc.Node - gc.Tempname(&t1, l.Type) - gc.Cgen(l, &t1) - l = &t1 - } - - if r != nil && !r.Addable { - var t2 gc.Node - gc.Tempname(&t2, r.Type) - gc.Cgen(r, &t2) - r = &t2 - } - - var ax gc.Node - gc.Nodreg(&ax, gc.Types[gc.TINT32], x86.REG_AX) - var cx gc.Node - gc.Nodreg(&cx, gc.Types[gc.TINT32], x86.REG_CX) - var dx gc.Node - gc.Nodreg(&dx, gc.Types[gc.TINT32], x86.REG_DX) - - // Setup for binary operation. - var hi1 gc.Node - var lo1 gc.Node - split64(l, &lo1, &hi1) - - var lo2 gc.Node - var hi2 gc.Node - if gc.Is64(r.Type) { - split64(r, &lo2, &hi2) - } - - // Do op. Leave result in DX:AX. - switch n.Op { - // TODO: Constants - case gc.OADD: - gins(x86.AMOVL, &lo1, &ax) - - gins(x86.AMOVL, &hi1, &dx) - gins(x86.AADDL, &lo2, &ax) - gins(x86.AADCL, &hi2, &dx) - - // TODO: Constants. - case gc.OSUB: - gins(x86.AMOVL, &lo1, &ax) - - gins(x86.AMOVL, &hi1, &dx) - gins(x86.ASUBL, &lo2, &ax) - gins(x86.ASBBL, &hi2, &dx) - - // let's call the next two EX and FX. - case gc.OMUL: - var ex gc.Node - gc.Regalloc(&ex, gc.Types[gc.TPTR32], nil) - - var fx gc.Node - gc.Regalloc(&fx, gc.Types[gc.TPTR32], nil) - - // load args into DX:AX and EX:CX. - gins(x86.AMOVL, &lo1, &ax) - - gins(x86.AMOVL, &hi1, &dx) - gins(x86.AMOVL, &lo2, &cx) - gins(x86.AMOVL, &hi2, &ex) - - // if DX and EX are zero, use 32 x 32 -> 64 unsigned multiply. - gins(x86.AMOVL, &dx, &fx) - - gins(x86.AORL, &ex, &fx) - p1 := gc.Gbranch(x86.AJNE, nil, 0) - gins(x86.AMULL, &cx, nil) // implicit &ax - p2 := gc.Gbranch(obj.AJMP, nil, 0) - gc.Patch(p1, gc.Pc) - - // full 64x64 -> 64, from 32x32 -> 64. - gins(x86.AIMULL, &cx, &dx) - - gins(x86.AMOVL, &ax, &fx) - gins(x86.AIMULL, &ex, &fx) - gins(x86.AADDL, &dx, &fx) - gins(x86.AMOVL, &cx, &dx) - gins(x86.AMULL, &dx, nil) // implicit &ax - gins(x86.AADDL, &fx, &dx) - gc.Patch(p2, gc.Pc) - - gc.Regfree(&ex) - gc.Regfree(&fx) - - // We only rotate by a constant c in [0,64). - // if c >= 32: - // lo, hi = hi, lo - // c -= 32 - // if c == 0: - // no-op - // else: - // t = hi - // shld hi:lo, c - // shld lo:t, c - case gc.OLROT: - v := uint64(r.Int()) - - if v >= 32 { - // reverse during load to do the first 32 bits of rotate - v -= 32 - - gins(x86.AMOVL, &lo1, &dx) - gins(x86.AMOVL, &hi1, &ax) - } else { - gins(x86.AMOVL, &lo1, &ax) - gins(x86.AMOVL, &hi1, &dx) - } - - if v == 0 { - } else // done - { - gins(x86.AMOVL, &dx, &cx) - p1 := gins(x86.ASHLL, ncon(uint32(v)), &dx) - p1.From.Index = x86.REG_AX // double-width shift - p1.From.Scale = 0 - p1 = gins(x86.ASHLL, ncon(uint32(v)), &ax) - p1.From.Index = x86.REG_CX // double-width shift - p1.From.Scale = 0 - } - - case gc.OLSH: - if r.Op == gc.OLITERAL { - v := uint64(r.Int()) - if v >= 64 { - if gc.Is64(r.Type) { - splitclean() - } - splitclean() - split64(res, &lo2, &hi2) - gins(x86.AMOVL, ncon(0), &lo2) - gins(x86.AMOVL, ncon(0), &hi2) - splitclean() - return - } - - if v >= 32 { - if gc.Is64(r.Type) { - splitclean() - } - split64(res, &lo2, &hi2) - gmove(&lo1, &hi2) - if v > 32 { - gins(x86.ASHLL, ncon(uint32(v-32)), &hi2) - } - - gins(x86.AMOVL, ncon(0), &lo2) - splitclean() - splitclean() - return - } - - // general shift - gins(x86.AMOVL, &lo1, &ax) - - gins(x86.AMOVL, &hi1, &dx) - p1 := gins(x86.ASHLL, ncon(uint32(v)), &dx) - p1.From.Index = x86.REG_AX // double-width shift - p1.From.Scale = 0 - gins(x86.ASHLL, ncon(uint32(v)), &ax) - break - } - - // load value into DX:AX. - gins(x86.AMOVL, &lo1, &ax) - - gins(x86.AMOVL, &hi1, &dx) - - // load shift value into register. - // if high bits are set, zero value. - var p1 *obj.Prog - - if gc.Is64(r.Type) { - gins(x86.ACMPL, &hi2, ncon(0)) - p1 = gc.Gbranch(x86.AJNE, nil, +1) - gins(x86.AMOVL, &lo2, &cx) - } else { - cx.Type = gc.Types[gc.TUINT32] - gmove(r, &cx) - } - - // if shift count is >=64, zero value - gins(x86.ACMPL, &cx, ncon(64)) - - p2 := gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1) - if p1 != nil { - gc.Patch(p1, gc.Pc) - } - gins(x86.AXORL, &dx, &dx) - gins(x86.AXORL, &ax, &ax) - gc.Patch(p2, gc.Pc) - - // if shift count is >= 32, zero low. - gins(x86.ACMPL, &cx, ncon(32)) - - p1 = gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1) - gins(x86.AMOVL, &ax, &dx) - gins(x86.ASHLL, &cx, &dx) // SHLL only uses bottom 5 bits of count - gins(x86.AXORL, &ax, &ax) - p2 = gc.Gbranch(obj.AJMP, nil, 0) - gc.Patch(p1, gc.Pc) - - // general shift - p1 = gins(x86.ASHLL, &cx, &dx) - - p1.From.Index = x86.REG_AX // double-width shift - p1.From.Scale = 0 - gins(x86.ASHLL, &cx, &ax) - gc.Patch(p2, gc.Pc) - - case gc.ORSH: - if r.Op == gc.OLITERAL { - v := uint64(r.Int()) - if v >= 64 { - if gc.Is64(r.Type) { - splitclean() - } - splitclean() - split64(res, &lo2, &hi2) - if hi1.Type.Etype == gc.TINT32 { - gmove(&hi1, &lo2) - gins(x86.ASARL, ncon(31), &lo2) - gmove(&hi1, &hi2) - gins(x86.ASARL, ncon(31), &hi2) - } else { - gins(x86.AMOVL, ncon(0), &lo2) - gins(x86.AMOVL, ncon(0), &hi2) - } - - splitclean() - return - } - - if v >= 32 { - if gc.Is64(r.Type) { - splitclean() - } - split64(res, &lo2, &hi2) - gmove(&hi1, &lo2) - if v > 32 { - gins(optoas(gc.ORSH, hi1.Type), ncon(uint32(v-32)), &lo2) - } - if hi1.Type.Etype == gc.TINT32 { - gmove(&hi1, &hi2) - gins(x86.ASARL, ncon(31), &hi2) - } else { - gins(x86.AMOVL, ncon(0), &hi2) - } - splitclean() - splitclean() - return - } - - // general shift - gins(x86.AMOVL, &lo1, &ax) - - gins(x86.AMOVL, &hi1, &dx) - p1 := gins(x86.ASHRL, ncon(uint32(v)), &ax) - p1.From.Index = x86.REG_DX // double-width shift - p1.From.Scale = 0 - gins(optoas(gc.ORSH, hi1.Type), ncon(uint32(v)), &dx) - break - } - - // load value into DX:AX. - gins(x86.AMOVL, &lo1, &ax) - - gins(x86.AMOVL, &hi1, &dx) - - // load shift value into register. - // if high bits are set, zero value. - var p1 *obj.Prog - - if gc.Is64(r.Type) { - gins(x86.ACMPL, &hi2, ncon(0)) - p1 = gc.Gbranch(x86.AJNE, nil, +1) - gins(x86.AMOVL, &lo2, &cx) - } else { - cx.Type = gc.Types[gc.TUINT32] - gmove(r, &cx) - } - - // if shift count is >=64, zero or sign-extend value - gins(x86.ACMPL, &cx, ncon(64)) - - p2 := gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1) - if p1 != nil { - gc.Patch(p1, gc.Pc) - } - if hi1.Type.Etype == gc.TINT32 { - gins(x86.ASARL, ncon(31), &dx) - gins(x86.AMOVL, &dx, &ax) - } else { - gins(x86.AXORL, &dx, &dx) - gins(x86.AXORL, &ax, &ax) - } - - gc.Patch(p2, gc.Pc) - - // if shift count is >= 32, sign-extend hi. - gins(x86.ACMPL, &cx, ncon(32)) - - p1 = gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1) - gins(x86.AMOVL, &dx, &ax) - if hi1.Type.Etype == gc.TINT32 { - gins(x86.ASARL, &cx, &ax) // SARL only uses bottom 5 bits of count - gins(x86.ASARL, ncon(31), &dx) - } else { - gins(x86.ASHRL, &cx, &ax) - gins(x86.AXORL, &dx, &dx) - } - - p2 = gc.Gbranch(obj.AJMP, nil, 0) - gc.Patch(p1, gc.Pc) - - // general shift - p1 = gins(x86.ASHRL, &cx, &ax) - - p1.From.Index = x86.REG_DX // double-width shift - p1.From.Scale = 0 - gins(optoas(gc.ORSH, hi1.Type), &cx, &dx) - gc.Patch(p2, gc.Pc) - - // make constant the right side (it usually is anyway). - case gc.OXOR, - gc.OAND, - gc.OOR: - if lo1.Op == gc.OLITERAL { - nswap(&lo1, &lo2) - nswap(&hi1, &hi2) - } - - if lo2.Op == gc.OLITERAL { - // special cases for constants. - lv := uint32(lo2.Int()) - hv := uint32(hi2.Int()) - splitclean() // right side - split64(res, &lo2, &hi2) - switch n.Op { - case gc.OXOR: - gmove(&lo1, &lo2) - gmove(&hi1, &hi2) - switch lv { - case 0: - break - - case 0xffffffff: - gins(x86.ANOTL, nil, &lo2) - - default: - gins(x86.AXORL, ncon(lv), &lo2) - } - - switch hv { - case 0: - break - - case 0xffffffff: - gins(x86.ANOTL, nil, &hi2) - - default: - gins(x86.AXORL, ncon(hv), &hi2) - } - - case gc.OAND: - switch lv { - case 0: - gins(x86.AMOVL, ncon(0), &lo2) - - default: - gmove(&lo1, &lo2) - if lv != 0xffffffff { - gins(x86.AANDL, ncon(lv), &lo2) - } - } - - switch hv { - case 0: - gins(x86.AMOVL, ncon(0), &hi2) - - default: - gmove(&hi1, &hi2) - if hv != 0xffffffff { - gins(x86.AANDL, ncon(hv), &hi2) - } - } - - case gc.OOR: - switch lv { - case 0: - gmove(&lo1, &lo2) - - case 0xffffffff: - gins(x86.AMOVL, ncon(0xffffffff), &lo2) - - default: - gmove(&lo1, &lo2) - gins(x86.AORL, ncon(lv), &lo2) - } - - switch hv { - case 0: - gmove(&hi1, &hi2) - - case 0xffffffff: - gins(x86.AMOVL, ncon(0xffffffff), &hi2) - - default: - gmove(&hi1, &hi2) - gins(x86.AORL, ncon(hv), &hi2) - } - } - - splitclean() - splitclean() - return - } - - gins(x86.AMOVL, &lo1, &ax) - gins(x86.AMOVL, &hi1, &dx) - gins(optoas(int(n.Op), lo1.Type), &lo2, &ax) - gins(optoas(int(n.Op), lo1.Type), &hi2, &dx) - } - - if gc.Is64(r.Type) { - splitclean() - } - splitclean() - - split64(res, &lo1, &hi1) - gins(x86.AMOVL, &ax, &lo1) - gins(x86.AMOVL, &dx, &hi1) - splitclean() -} - -/* - * generate comparison of nl, nr, both 64-bit. - * nl is memory; nr is constant or memory. - */ -func cmp64(nl *gc.Node, nr *gc.Node, op int, likely int, to *obj.Prog) { - var lo1 gc.Node - var hi1 gc.Node - var lo2 gc.Node - var hi2 gc.Node - var rr gc.Node - - split64(nl, &lo1, &hi1) - split64(nr, &lo2, &hi2) - - // compare most significant word; - // if they differ, we're done. - t := hi1.Type - - if nl.Op == gc.OLITERAL || nr.Op == gc.OLITERAL { - gins(x86.ACMPL, &hi1, &hi2) - } else { - gc.Regalloc(&rr, gc.Types[gc.TINT32], nil) - gins(x86.AMOVL, &hi1, &rr) - gins(x86.ACMPL, &rr, &hi2) - gc.Regfree(&rr) - } - - var br *obj.Prog - switch op { - default: - gc.Fatal("cmp64 %v %v", gc.Oconv(int(op), 0), t) - - // cmp hi - // jne L - // cmp lo - // jeq to - // L: - case gc.OEQ: - br = gc.Gbranch(x86.AJNE, nil, -likely) - - // cmp hi - // jne to - // cmp lo - // jne to - case gc.ONE: - gc.Patch(gc.Gbranch(x86.AJNE, nil, likely), to) - - // cmp hi - // jgt to - // jlt L - // cmp lo - // jge to (or jgt to) - // L: - case gc.OGE, - gc.OGT: - gc.Patch(gc.Gbranch(optoas(gc.OGT, t), nil, likely), to) - - br = gc.Gbranch(optoas(gc.OLT, t), nil, -likely) - - // cmp hi - // jlt to - // jgt L - // cmp lo - // jle to (or jlt to) - // L: - case gc.OLE, - gc.OLT: - gc.Patch(gc.Gbranch(optoas(gc.OLT, t), nil, likely), to) - - br = gc.Gbranch(optoas(gc.OGT, t), nil, -likely) - } - - // compare least significant word - t = lo1.Type - - if nl.Op == gc.OLITERAL || nr.Op == gc.OLITERAL { - gins(x86.ACMPL, &lo1, &lo2) - } else { - gc.Regalloc(&rr, gc.Types[gc.TINT32], nil) - gins(x86.AMOVL, &lo1, &rr) - gins(x86.ACMPL, &rr, &lo2) - gc.Regfree(&rr) - } - - // jump again - gc.Patch(gc.Gbranch(optoas(op, t), nil, likely), to) - - // point first branch down here if appropriate - if br != nil { - gc.Patch(br, gc.Pc) - } - - splitclean() - splitclean() -} diff --git a/src/cmd/8g/galign.go b/src/cmd/8g/galign.go deleted file mode 100644 index 3651f509c9..0000000000 --- a/src/cmd/8g/galign.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2009 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/gc" - "cmd/internal/obj" - "cmd/internal/obj/x86" - "fmt" - "os" -) - -var thechar int = '8' - -var thestring string = "386" - -var thelinkarch *obj.LinkArch = &x86.Link386 - -func linkarchinit() { -} - -var MAXWIDTH int64 = (1 << 32) - 1 - -/* - * go declares several platform-specific type aliases: - * int, uint, and uintptr - */ -var typedefs = []gc.Typedef{ - gc.Typedef{"int", gc.TINT, gc.TINT32}, - gc.Typedef{"uint", gc.TUINT, gc.TUINT32}, - gc.Typedef{"uintptr", gc.TUINTPTR, gc.TUINT32}, -} - -func betypeinit() { - gc.Widthptr = 4 - gc.Widthint = 4 - gc.Widthreg = 4 -} - -func main() { - gc.Thearch.Thechar = thechar - gc.Thearch.Thestring = thestring - gc.Thearch.Thelinkarch = thelinkarch - gc.Thearch.Typedefs = typedefs - gc.Thearch.REGSP = x86.REGSP - gc.Thearch.REGCTXT = x86.REGCTXT - gc.Thearch.REGCALLX = x86.REG_BX - gc.Thearch.REGCALLX2 = x86.REG_AX - gc.Thearch.REGRETURN = x86.REG_AX - gc.Thearch.REGMIN = x86.REG_AX - gc.Thearch.REGMAX = x86.REG_DI - switch v := obj.Getgo386(); v { - case "387": - gc.Thearch.FREGMIN = x86.REG_F0 - gc.Thearch.FREGMAX = x86.REG_F7 - gc.Thearch.Use387 = true - case "sse2": - gc.Thearch.FREGMIN = x86.REG_X0 - gc.Thearch.FREGMAX = x86.REG_X7 - default: - fmt.Fprintf(os.Stderr, "unsupported setting GO386=%s\n", v) - gc.Exit(1) - } - gc.Thearch.MAXWIDTH = MAXWIDTH - gc.Thearch.ReservedRegs = resvd - - gc.Thearch.Betypeinit = betypeinit - gc.Thearch.Bgen_float = bgen_float - gc.Thearch.Cgen64 = cgen64 - gc.Thearch.Cgen_bmul = cgen_bmul - gc.Thearch.Cgen_float = cgen_float - gc.Thearch.Cgen_hmul = cgen_hmul - gc.Thearch.Cgen_shift = cgen_shift - gc.Thearch.Clearfat = clearfat - gc.Thearch.Cmp64 = cmp64 - gc.Thearch.Defframe = defframe - gc.Thearch.Dodiv = cgen_div - gc.Thearch.Excise = excise - 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 - gc.Thearch.Igenindex = igenindex - gc.Thearch.Linkarchinit = linkarchinit - gc.Thearch.Peep = peep - gc.Thearch.Proginfo = proginfo - gc.Thearch.Regtyp = regtyp - gc.Thearch.Sameaddr = sameaddr - gc.Thearch.Smallindir = smallindir - gc.Thearch.Stackaddr = stackaddr - gc.Thearch.Blockcopy = blockcopy - gc.Thearch.Sudoaddable = sudoaddable - gc.Thearch.Sudoclean = sudoclean - gc.Thearch.Excludedregs = excludedregs - gc.Thearch.RtoB = RtoB - gc.Thearch.FtoB = FtoB - gc.Thearch.BtoR = BtoR - gc.Thearch.BtoF = BtoF - gc.Thearch.Optoas = optoas - gc.Thearch.Doregbits = doregbits - gc.Thearch.Regnames = regnames - - gc.Main() - gc.Exit(0) -} diff --git a/src/cmd/8g/ggen.go b/src/cmd/8g/ggen.go deleted file mode 100644 index bd2c13e867..0000000000 --- a/src/cmd/8g/ggen.go +++ /dev/null @@ -1,940 +0,0 @@ -// Copyright 2009 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/gc" - "cmd/internal/obj" - "cmd/internal/obj/x86" -) - -func defframe(ptxt *obj.Prog) { - var n *gc.Node - - // fill in argument size, stack size - ptxt.To.Type = obj.TYPE_TEXTSIZE - - ptxt.To.Val = int32(gc.Rnd(gc.Curfn.Type.Argwid, int64(gc.Widthptr))) - frame := uint32(gc.Rnd(gc.Stksize+gc.Maxarg, int64(gc.Widthreg))) - ptxt.To.Offset = int64(frame) - - // insert code to zero ambiguously live variables - // so that the garbage collector only sees initialized values - // when it looks for pointers. - p := ptxt - - hi := int64(0) - lo := hi - ax := uint32(0) - for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next { - n = l.N - if !n.Name.Needzero { - continue - } - if n.Class != gc.PAUTO { - gc.Fatal("needzero class %d", n.Class) - } - if n.Type.Width%int64(gc.Widthptr) != 0 || n.Xoffset%int64(gc.Widthptr) != 0 || n.Type.Width == 0 { - gc.Fatal("var %v has size %d offset %d", gc.Nconv(n, obj.FmtLong), int(n.Type.Width), int(n.Xoffset)) - } - if lo != hi && n.Xoffset+n.Type.Width == lo-int64(2*gc.Widthptr) { - // merge with range we already have - lo = n.Xoffset - - continue - } - - // zero old range - p = zerorange(p, int64(frame), lo, hi, &ax) - - // set new range - hi = n.Xoffset + n.Type.Width - - lo = n.Xoffset - } - - // zero final range - zerorange(p, int64(frame), lo, hi, &ax) -} - -func zerorange(p *obj.Prog, frame int64, lo int64, hi int64, ax *uint32) *obj.Prog { - cnt := hi - lo - if cnt == 0 { - return p - } - if *ax == 0 { - p = appendpp(p, x86.AMOVL, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, x86.REG_AX, 0) - *ax = 1 - } - - if cnt <= int64(4*gc.Widthreg) { - for i := int64(0); i < cnt; i += int64(gc.Widthreg) { - p = appendpp(p, x86.AMOVL, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, frame+lo+i) - } - } else if !gc.Nacl && cnt <= int64(128*gc.Widthreg) { - p = appendpp(p, x86.ALEAL, obj.TYPE_MEM, x86.REG_SP, frame+lo, obj.TYPE_REG, x86.REG_DI, 0) - p = appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_ADDR, 0, 1*(128-cnt/int64(gc.Widthreg))) - p.To.Sym = gc.Linksym(gc.Pkglookup("duffzero", gc.Runtimepkg)) - } else { - p = appendpp(p, x86.AMOVL, obj.TYPE_CONST, 0, cnt/int64(gc.Widthreg), obj.TYPE_REG, x86.REG_CX, 0) - p = appendpp(p, x86.ALEAL, obj.TYPE_MEM, x86.REG_SP, frame+lo, obj.TYPE_REG, x86.REG_DI, 0) - p = appendpp(p, x86.AREP, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0) - p = appendpp(p, x86.ASTOSL, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0) - } - - return p -} - -func appendpp(p *obj.Prog, as int, ftype int, freg int, foffset int64, ttype int, treg int, toffset int64) *obj.Prog { - q := gc.Ctxt.NewProg() - gc.Clearp(q) - q.As = int16(as) - q.Lineno = p.Lineno - q.From.Type = int16(ftype) - q.From.Reg = int16(freg) - q.From.Offset = foffset - q.To.Type = int16(ttype) - q.To.Reg = int16(treg) - q.To.Offset = toffset - q.Link = p.Link - p.Link = q - return q -} - -func clearfat(nl *gc.Node) { - /* clear a fat object */ - if gc.Debug['g'] != 0 { - gc.Dump("\nclearfat", nl) - } - - w := uint32(nl.Type.Width) - - // Avoid taking the address for simple enough types. - if gc.Componentgen(nil, nl) { - return - } - - c := w % 4 // bytes - q := w / 4 // quads - - if q < 4 { - // Write sequence of MOV 0, off(base) instead of using STOSL. - // The hope is that although the code will be slightly longer, - // the MOVs will have no dependencies and pipeline better - // than the unrolled STOSL loop. - // NOTE: Must use agen, not igen, so that optimizer sees address - // being taken. We are not writing on field boundaries. - var n1 gc.Node - gc.Regalloc(&n1, gc.Types[gc.Tptr], nil) - - gc.Agen(nl, &n1) - n1.Op = gc.OINDREG - var z gc.Node - gc.Nodconst(&z, gc.Types[gc.TUINT64], 0) - for { - tmp14 := q - q-- - if tmp14 <= 0 { - break - } - n1.Type = z.Type - gins(x86.AMOVL, &z, &n1) - n1.Xoffset += 4 - } - - gc.Nodconst(&z, gc.Types[gc.TUINT8], 0) - for { - tmp15 := c - c-- - if tmp15 <= 0 { - break - } - n1.Type = z.Type - gins(x86.AMOVB, &z, &n1) - n1.Xoffset++ - } - - gc.Regfree(&n1) - return - } - - var n1 gc.Node - gc.Nodreg(&n1, gc.Types[gc.Tptr], x86.REG_DI) - gc.Agen(nl, &n1) - gconreg(x86.AMOVL, 0, x86.REG_AX) - - if q > 128 || (q >= 4 && gc.Nacl) { - gconreg(x86.AMOVL, int64(q), x86.REG_CX) - gins(x86.AREP, nil, nil) // repeat - gins(x86.ASTOSL, nil, nil) // STOL AL,*(DI)+ - } else if q >= 4 { - p := gins(obj.ADUFFZERO, nil, nil) - p.To.Type = obj.TYPE_ADDR - p.To.Sym = gc.Linksym(gc.Pkglookup("duffzero", gc.Runtimepkg)) - - // 1 and 128 = magic constants: see ../../runtime/asm_386.s - p.To.Offset = 1 * (128 - int64(q)) - } else { - for q > 0 { - gins(x86.ASTOSL, nil, nil) // STOL AL,*(DI)+ - q-- - } - } - - for c > 0 { - gins(x86.ASTOSB, nil, nil) // STOB AL,*(DI)+ - c-- - } -} - -var panicdiv *gc.Node - -/* - * generate division. - * caller must set: - * ax = allocated AX register - * dx = allocated DX register - * generates one of: - * res = nl / nr - * res = nl % nr - * according to op. - */ -func dodiv(op int, nl *gc.Node, nr *gc.Node, res *gc.Node, ax *gc.Node, dx *gc.Node) { - // Have to be careful about handling - // most negative int divided by -1 correctly. - // The hardware will trap. - // Also the byte divide instruction needs AH, - // which we otherwise don't have to deal with. - // Easiest way to avoid for int8, int16: use int32. - // For int32 and int64, use explicit test. - // Could use int64 hw for int32. - t := nl.Type - - t0 := t - check := 0 - if gc.Issigned[t.Etype] { - check = 1 - if gc.Isconst(nl, gc.CTINT) && nl.Int() != -1< 0 && !gc.Samereg(x, res) { - gc.Tempname(oldx, gc.Types[gc.TINT32]) - gmove(x, oldx) - } - - gc.Regalloc(x, t, x) -} - -func restx(x *gc.Node, oldx *gc.Node) { - gc.Regfree(x) - - if oldx.Op != 0 { - x.Type = gc.Types[gc.TINT32] - gmove(oldx, x) - } -} - -/* - * generate division according to op, one of: - * res = nl / nr - * res = nl % nr - */ -func cgen_div(op int, nl *gc.Node, nr *gc.Node, res *gc.Node) { - if gc.Is64(nl.Type) { - gc.Fatal("cgen_div %v", nl.Type) - } - - var t *gc.Type - if gc.Issigned[nl.Type.Etype] { - t = gc.Types[gc.TINT32] - } else { - t = gc.Types[gc.TUINT32] - } - var ax gc.Node - var oldax gc.Node - savex(x86.REG_AX, &ax, &oldax, res, t) - var olddx gc.Node - var dx gc.Node - savex(x86.REG_DX, &dx, &olddx, res, t) - dodiv(op, nl, nr, res, &ax, &dx) - restx(&dx, &olddx) - restx(&ax, &oldax) -} - -/* - * generate shift according to op, one of: - * res = nl << nr - * res = nl >> nr - */ -func cgen_shift(op int, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) { - if nl.Type.Width > 4 { - gc.Fatal("cgen_shift %v", nl.Type) - } - - w := int(nl.Type.Width * 8) - - a := optoas(op, nl.Type) - - if nr.Op == gc.OLITERAL { - var n2 gc.Node - gc.Tempname(&n2, nl.Type) - gc.Cgen(nl, &n2) - var n1 gc.Node - gc.Regalloc(&n1, nl.Type, res) - gmove(&n2, &n1) - sc := uint64(nr.Int()) - if sc >= uint64(nl.Type.Width*8) { - // large shift gets 2 shifts by width-1 - gins(a, ncon(uint32(w)-1), &n1) - - gins(a, ncon(uint32(w)-1), &n1) - } else { - gins(a, nr, &n1) - } - gmove(&n1, res) - gc.Regfree(&n1) - return - } - - var oldcx gc.Node - var cx gc.Node - gc.Nodreg(&cx, gc.Types[gc.TUINT32], x86.REG_CX) - if reg[x86.REG_CX] > 1 && !gc.Samereg(&cx, res) { - gc.Tempname(&oldcx, gc.Types[gc.TUINT32]) - gmove(&cx, &oldcx) - } - - var n1 gc.Node - var nt gc.Node - if nr.Type.Width > 4 { - gc.Tempname(&nt, nr.Type) - n1 = nt - } else { - gc.Nodreg(&n1, gc.Types[gc.TUINT32], x86.REG_CX) - gc.Regalloc(&n1, nr.Type, &n1) // to hold the shift type in CX - } - - var n2 gc.Node - if gc.Samereg(&cx, res) { - gc.Regalloc(&n2, nl.Type, nil) - } else { - gc.Regalloc(&n2, nl.Type, res) - } - if nl.Ullman >= nr.Ullman { - gc.Cgen(nl, &n2) - gc.Cgen(nr, &n1) - } else { - gc.Cgen(nr, &n1) - gc.Cgen(nl, &n2) - } - - // test and fix up large shifts - if bounded { - if nr.Type.Width > 4 { - // delayed reg alloc - gc.Nodreg(&n1, gc.Types[gc.TUINT32], x86.REG_CX) - - gc.Regalloc(&n1, gc.Types[gc.TUINT32], &n1) // to hold the shift type in CX - var lo gc.Node - var hi gc.Node - split64(&nt, &lo, &hi) - gmove(&lo, &n1) - splitclean() - } - } else { - var p1 *obj.Prog - if nr.Type.Width > 4 { - // delayed reg alloc - gc.Nodreg(&n1, gc.Types[gc.TUINT32], x86.REG_CX) - - gc.Regalloc(&n1, gc.Types[gc.TUINT32], &n1) // to hold the shift type in CX - var lo gc.Node - var hi gc.Node - split64(&nt, &lo, &hi) - gmove(&lo, &n1) - gins(optoas(gc.OCMP, gc.Types[gc.TUINT32]), &hi, ncon(0)) - p2 := gc.Gbranch(optoas(gc.ONE, gc.Types[gc.TUINT32]), nil, +1) - gins(optoas(gc.OCMP, gc.Types[gc.TUINT32]), &n1, ncon(uint32(w))) - p1 = gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1) - splitclean() - gc.Patch(p2, gc.Pc) - } else { - gins(optoas(gc.OCMP, nr.Type), &n1, ncon(uint32(w))) - p1 = gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1) - } - - if op == gc.ORSH && gc.Issigned[nl.Type.Etype] { - gins(a, ncon(uint32(w)-1), &n2) - } else { - gmove(ncon(0), &n2) - } - - gc.Patch(p1, gc.Pc) - } - - gins(a, &n1, &n2) - - if oldcx.Op != 0 { - gmove(&oldcx, &cx) - } - - gmove(&n2, res) - - gc.Regfree(&n1) - gc.Regfree(&n2) -} - -/* - * generate byte multiply: - * res = nl * nr - * there is no 2-operand byte multiply instruction so - * we do a full-width multiplication and truncate afterwards. - */ -func cgen_bmul(op int, nl *gc.Node, nr *gc.Node, res *gc.Node) bool { - if optoas(op, nl.Type) != x86.AIMULB { - return false - } - - // copy from byte to full registers - t := gc.Types[gc.TUINT32] - - if gc.Issigned[nl.Type.Etype] { - t = gc.Types[gc.TINT32] - } - - // largest ullman on left. - if nl.Ullman < nr.Ullman { - tmp := nl - nl = nr - nr = tmp - } - - var nt gc.Node - gc.Tempname(&nt, nl.Type) - gc.Cgen(nl, &nt) - var n1 gc.Node - gc.Regalloc(&n1, t, res) - gc.Cgen(nr, &n1) - var n2 gc.Node - gc.Regalloc(&n2, t, nil) - gmove(&nt, &n2) - a := optoas(op, t) - gins(a, &n2, &n1) - gc.Regfree(&n2) - gmove(&n1, res) - gc.Regfree(&n1) - - return true -} - -/* - * generate high multiply: - * res = (nl*nr) >> width - */ -func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) { - var n1 gc.Node - var n2 gc.Node - var ax gc.Node - var dx gc.Node - - t := nl.Type - a := optoas(gc.OHMUL, t) - - // gen nl in n1. - gc.Tempname(&n1, t) - - gc.Cgen(nl, &n1) - - // gen nr in n2. - gc.Regalloc(&n2, t, res) - - gc.Cgen(nr, &n2) - - // multiply. - gc.Nodreg(&ax, t, x86.REG_AX) - - gmove(&n2, &ax) - gins(a, &n1, nil) - gc.Regfree(&n2) - - if t.Width == 1 { - // byte multiply behaves differently. - gc.Nodreg(&ax, t, x86.REG_AH) - - gc.Nodreg(&dx, t, x86.REG_DX) - gmove(&ax, &dx) - } - - gc.Nodreg(&dx, t, x86.REG_DX) - gmove(&dx, res) -} - -/* - * generate floating-point operation. - */ -func cgen_float(n *gc.Node, res *gc.Node) { - nl := n.Left - switch n.Op { - case gc.OEQ, - gc.ONE, - gc.OLT, - gc.OLE, - gc.OGE: - p1 := gc.Gbranch(obj.AJMP, nil, 0) - p2 := gc.Pc - gmove(gc.Nodbool(true), res) - p3 := gc.Gbranch(obj.AJMP, nil, 0) - gc.Patch(p1, gc.Pc) - gc.Bgen(n, true, 0, p2) - gmove(gc.Nodbool(false), res) - gc.Patch(p3, gc.Pc) - return - - case gc.OPLUS: - gc.Cgen(nl, res) - return - - case gc.OCONV: - if gc.Eqtype(n.Type, nl.Type) || gc.Noconv(n.Type, nl.Type) { - gc.Cgen(nl, res) - return - } - - var n2 gc.Node - gc.Tempname(&n2, n.Type) - var n1 gc.Node - gc.Mgen(nl, &n1, res) - gmove(&n1, &n2) - gmove(&n2, res) - gc.Mfree(&n1) - return - } - - if gc.Thearch.Use387 { - cgen_float387(n, res) - } else { - cgen_floatsse(n, res) - } -} - -// floating-point. 387 (not SSE2) -func cgen_float387(n *gc.Node, res *gc.Node) { - var f0 gc.Node - var f1 gc.Node - - nl := n.Left - nr := n.Right - gc.Nodreg(&f0, nl.Type, x86.REG_F0) - gc.Nodreg(&f1, n.Type, x86.REG_F0+1) - if nr != nil { - // binary - if nl.Ullman >= nr.Ullman { - gc.Cgen(nl, &f0) - if nr.Addable { - gins(foptoas(int(n.Op), n.Type, 0), nr, &f0) - } else { - gc.Cgen(nr, &f0) - gins(foptoas(int(n.Op), n.Type, Fpop), &f0, &f1) - } - } else { - gc.Cgen(nr, &f0) - if nl.Addable { - gins(foptoas(int(n.Op), n.Type, Frev), nl, &f0) - } else { - gc.Cgen(nl, &f0) - gins(foptoas(int(n.Op), n.Type, Frev|Fpop), &f0, &f1) - } - } - - gmove(&f0, res) - return - } - - // unary - gc.Cgen(nl, &f0) - - if n.Op != gc.OCONV && n.Op != gc.OPLUS { - gins(foptoas(int(n.Op), n.Type, 0), nil, nil) - } - gmove(&f0, res) - return -} - -func cgen_floatsse(n *gc.Node, res *gc.Node) { - var a int - - nl := n.Left - nr := n.Right - switch n.Op { - default: - gc.Dump("cgen_floatsse", n) - gc.Fatal("cgen_floatsse %v", gc.Oconv(int(n.Op), 0)) - return - - case gc.OMINUS, - gc.OCOM: - nr = gc.Nodintconst(-1) - gc.Convlit(&nr, n.Type) - a = foptoas(gc.OMUL, nl.Type, 0) - goto sbop - - // symmetric binary - case gc.OADD, - gc.OMUL: - a = foptoas(int(n.Op), nl.Type, 0) - - goto sbop - - // asymmetric binary - case gc.OSUB, - gc.OMOD, - gc.ODIV: - a = foptoas(int(n.Op), nl.Type, 0) - - goto abop - } - -sbop: // symmetric binary - if nl.Ullman < nr.Ullman || nl.Op == gc.OLITERAL { - r := nl - nl = nr - nr = r - } - -abop: // asymmetric binary - if nl.Ullman >= nr.Ullman { - var nt gc.Node - gc.Tempname(&nt, nl.Type) - gc.Cgen(nl, &nt) - var n2 gc.Node - gc.Mgen(nr, &n2, nil) - var n1 gc.Node - gc.Regalloc(&n1, nl.Type, res) - gmove(&nt, &n1) - gins(a, &n2, &n1) - gmove(&n1, res) - gc.Regfree(&n1) - gc.Mfree(&n2) - } else { - var n2 gc.Node - gc.Regalloc(&n2, nr.Type, res) - gc.Cgen(nr, &n2) - var n1 gc.Node - gc.Regalloc(&n1, nl.Type, nil) - gc.Cgen(nl, &n1) - gins(a, &n2, &n1) - gc.Regfree(&n2) - gmove(&n1, res) - gc.Regfree(&n1) - } - - return -} - -func bgen_float(n *gc.Node, wantTrue bool, likely int, to *obj.Prog) { - nl := n.Left - nr := n.Right - a := int(n.Op) - if !wantTrue { - // brcom is not valid on floats when NaN is involved. - p1 := gc.Gbranch(obj.AJMP, nil, 0) - p2 := gc.Gbranch(obj.AJMP, nil, 0) - gc.Patch(p1, gc.Pc) - - // No need to avoid re-genning ninit. - bgen_float(n, true, -likely, p2) - - gc.Patch(gc.Gbranch(obj.AJMP, nil, 0), to) - gc.Patch(p2, gc.Pc) - return - } - - if gc.Thearch.Use387 { - a = gc.Brrev(a) // because the args are stacked - if a == gc.OGE || a == gc.OGT { - // only < and <= work right with NaN; reverse if needed - nl, nr = nr, nl - a = gc.Brrev(a) - } - - var ax, n2, tmp gc.Node - gc.Nodreg(&tmp, nr.Type, x86.REG_F0) - gc.Nodreg(&n2, nr.Type, x86.REG_F0+1) - gc.Nodreg(&ax, gc.Types[gc.TUINT16], x86.REG_AX) - if gc.Simsimtype(nr.Type) == gc.TFLOAT64 { - if nl.Ullman > nr.Ullman { - gc.Cgen(nl, &tmp) - gc.Cgen(nr, &tmp) - gins(x86.AFXCHD, &tmp, &n2) - } else { - gc.Cgen(nr, &tmp) - gc.Cgen(nl, &tmp) - } - - gins(x86.AFUCOMIP, &tmp, &n2) - gins(x86.AFMOVDP, &tmp, &tmp) // annoying pop but still better than STSW+SAHF - } else { - // TODO(rsc): The moves back and forth to memory - // here are for truncating the value to 32 bits. - // This handles 32-bit comparison but presumably - // all the other ops have the same problem. - // We need to figure out what the right general - // solution is, besides telling people to use float64. - var t1 gc.Node - gc.Tempname(&t1, gc.Types[gc.TFLOAT32]) - - var t2 gc.Node - gc.Tempname(&t2, gc.Types[gc.TFLOAT32]) - gc.Cgen(nr, &t1) - gc.Cgen(nl, &t2) - gmove(&t2, &tmp) - gins(x86.AFCOMFP, &t1, &tmp) - gins(x86.AFSTSW, nil, &ax) - gins(x86.ASAHF, nil, nil) - } - } else { - // Not 387 - if !nl.Addable { - nl = gc.CgenTemp(nl) - } - if !nr.Addable { - nr = gc.CgenTemp(nr) - } - - var n2 gc.Node - gc.Regalloc(&n2, nr.Type, nil) - gmove(nr, &n2) - nr = &n2 - - if nl.Op != gc.OREGISTER { - var n3 gc.Node - gc.Regalloc(&n3, nl.Type, nil) - gmove(nl, &n3) - nl = &n3 - } - - if a == gc.OGE || a == gc.OGT { - // only < and <= work right with NaN; reverse if needed - nl, nr = nr, nl - a = gc.Brrev(a) - } - - gins(foptoas(gc.OCMP, nr.Type, 0), nl, nr) - if nl.Op == gc.OREGISTER { - gc.Regfree(nl) - } - gc.Regfree(nr) - } - - switch a { - case gc.OEQ: - // neither NE nor P - p1 := gc.Gbranch(x86.AJNE, nil, -likely) - p2 := gc.Gbranch(x86.AJPS, nil, -likely) - gc.Patch(gc.Gbranch(obj.AJMP, nil, 0), to) - gc.Patch(p1, gc.Pc) - gc.Patch(p2, gc.Pc) - case gc.ONE: - // either NE or P - gc.Patch(gc.Gbranch(x86.AJNE, nil, likely), to) - gc.Patch(gc.Gbranch(x86.AJPS, nil, likely), to) - default: - gc.Patch(gc.Gbranch(optoas(a, nr.Type), nil, likely), to) - } -} - -// Called after regopt and peep have run. -// Expand CHECKNIL pseudo-op into actual nil pointer check. -func expandchecks(firstp *obj.Prog) { - var p1 *obj.Prog - var p2 *obj.Prog - - for p := firstp; p != nil; p = p.Link { - if p.As != obj.ACHECKNIL { - continue - } - if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers - gc.Warnl(int(p.Lineno), "generated nil check") - } - - // check is - // CMP arg, $0 - // JNE 2(PC) (likely) - // MOV AX, 0 - p1 = gc.Ctxt.NewProg() - - p2 = gc.Ctxt.NewProg() - gc.Clearp(p1) - gc.Clearp(p2) - p1.Link = p2 - p2.Link = p.Link - p.Link = p1 - p1.Lineno = p.Lineno - p2.Lineno = p.Lineno - p1.Pc = 9999 - p2.Pc = 9999 - p.As = x86.ACMPL - p.To.Type = obj.TYPE_CONST - p.To.Offset = 0 - p1.As = x86.AJNE - p1.From.Type = obj.TYPE_CONST - p1.From.Offset = 1 // likely - p1.To.Type = obj.TYPE_BRANCH - p1.To.Val = p2.Link - - // crash by write to memory address 0. - // if possible, since we know arg is 0, use 0(arg), - // which will be shorter to encode than plain 0. - p2.As = x86.AMOVL - - p2.From.Type = obj.TYPE_REG - p2.From.Reg = x86.REG_AX - if regtyp(&p.From) { - p2.To.Type = obj.TYPE_MEM - p2.To.Reg = p.From.Reg - } else { - p2.To.Type = obj.TYPE_MEM - } - p2.To.Offset = 0 - } -} - -// addr += index*width if possible. -func addindex(index *gc.Node, width int64, addr *gc.Node) bool { - switch width { - case 1, 2, 4, 8: - p1 := gins(x86.ALEAL, index, addr) - p1.From.Type = obj.TYPE_MEM - p1.From.Scale = int16(width) - p1.From.Index = p1.From.Reg - p1.From.Reg = p1.To.Reg - return true - } - return false -} - -// res = runtime.getg() -func getg(res *gc.Node) { - var n1 gc.Node - gc.Regalloc(&n1, res.Type, res) - mov := optoas(gc.OAS, gc.Types[gc.Tptr]) - p := gins(mov, nil, &n1) - p.From.Type = obj.TYPE_REG - p.From.Reg = x86.REG_TLS - p = gins(mov, nil, &n1) - p.From = p.To - p.From.Type = obj.TYPE_MEM - p.From.Index = x86.REG_TLS - p.From.Scale = 1 - gmove(&n1, res) - gc.Regfree(&n1) -} diff --git a/src/cmd/8g/gsubr.go b/src/cmd/8g/gsubr.go deleted file mode 100644 index 99bce6eaba..0000000000 --- a/src/cmd/8g/gsubr.go +++ /dev/null @@ -1,1798 +0,0 @@ -// Derived from Inferno utils/8c/txt.c -// http://code.google.com/p/inferno-os/source/browse/utils/8c/txt.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package main - -import ( - "cmd/internal/gc" - "cmd/internal/gc/big" - "cmd/internal/obj" - "cmd/internal/obj/x86" - "fmt" -) - -// TODO(rsc): Can make this bigger if we move -// the text segment up higher in 8l for all GOOS. -// At the same time, can raise StackBig in ../../runtime/stack.h. -var unmappedzero uint32 = 4096 - -// foptoas flags -const ( - Frev = 1 << 0 - Fpop = 1 << 1 - Fpop2 = 1 << 2 -) - -/* - * return Axxx for Oxxx on type t. - */ -func optoas(op int, t *gc.Type) int { - if t == nil { - gc.Fatal("optoas: t is nil") - } - - a := obj.AXXX - switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) { - default: - gc.Fatal("optoas: no entry %v-%v", gc.Oconv(int(op), 0), t) - - case gc.OADDR<<16 | gc.TPTR32: - a = x86.ALEAL - - case gc.OEQ<<16 | gc.TBOOL, - gc.OEQ<<16 | gc.TINT8, - gc.OEQ<<16 | gc.TUINT8, - gc.OEQ<<16 | gc.TINT16, - gc.OEQ<<16 | gc.TUINT16, - gc.OEQ<<16 | gc.TINT32, - gc.OEQ<<16 | gc.TUINT32, - gc.OEQ<<16 | gc.TINT64, - gc.OEQ<<16 | gc.TUINT64, - gc.OEQ<<16 | gc.TPTR32, - gc.OEQ<<16 | gc.TPTR64, - gc.OEQ<<16 | gc.TFLOAT32, - gc.OEQ<<16 | gc.TFLOAT64: - a = x86.AJEQ - - case gc.ONE<<16 | gc.TBOOL, - gc.ONE<<16 | gc.TINT8, - gc.ONE<<16 | gc.TUINT8, - gc.ONE<<16 | gc.TINT16, - gc.ONE<<16 | gc.TUINT16, - gc.ONE<<16 | gc.TINT32, - gc.ONE<<16 | gc.TUINT32, - gc.ONE<<16 | gc.TINT64, - gc.ONE<<16 | gc.TUINT64, - gc.ONE<<16 | gc.TPTR32, - gc.ONE<<16 | gc.TPTR64, - gc.ONE<<16 | gc.TFLOAT32, - gc.ONE<<16 | gc.TFLOAT64: - a = x86.AJNE - - case gc.OLT<<16 | gc.TINT8, - gc.OLT<<16 | gc.TINT16, - gc.OLT<<16 | gc.TINT32, - gc.OLT<<16 | gc.TINT64: - a = x86.AJLT - - case gc.OLT<<16 | gc.TUINT8, - gc.OLT<<16 | gc.TUINT16, - gc.OLT<<16 | gc.TUINT32, - gc.OLT<<16 | gc.TUINT64: - a = x86.AJCS - - case gc.OLE<<16 | gc.TINT8, - gc.OLE<<16 | gc.TINT16, - gc.OLE<<16 | gc.TINT32, - gc.OLE<<16 | gc.TINT64: - a = x86.AJLE - - case gc.OLE<<16 | gc.TUINT8, - gc.OLE<<16 | gc.TUINT16, - gc.OLE<<16 | gc.TUINT32, - gc.OLE<<16 | gc.TUINT64: - a = x86.AJLS - - case gc.OGT<<16 | gc.TINT8, - gc.OGT<<16 | gc.TINT16, - gc.OGT<<16 | gc.TINT32, - gc.OGT<<16 | gc.TINT64: - a = x86.AJGT - - case gc.OGT<<16 | gc.TUINT8, - gc.OGT<<16 | gc.TUINT16, - gc.OGT<<16 | gc.TUINT32, - gc.OGT<<16 | gc.TUINT64, - gc.OLT<<16 | gc.TFLOAT32, - gc.OLT<<16 | gc.TFLOAT64: - a = x86.AJHI - - case gc.OGE<<16 | gc.TINT8, - gc.OGE<<16 | gc.TINT16, - gc.OGE<<16 | gc.TINT32, - gc.OGE<<16 | gc.TINT64: - a = x86.AJGE - - case gc.OGE<<16 | gc.TUINT8, - gc.OGE<<16 | gc.TUINT16, - gc.OGE<<16 | gc.TUINT32, - gc.OGE<<16 | gc.TUINT64, - gc.OLE<<16 | gc.TFLOAT32, - gc.OLE<<16 | gc.TFLOAT64: - a = x86.AJCC - - case gc.OCMP<<16 | gc.TBOOL, - gc.OCMP<<16 | gc.TINT8, - gc.OCMP<<16 | gc.TUINT8: - a = x86.ACMPB - - case gc.OCMP<<16 | gc.TINT16, - gc.OCMP<<16 | gc.TUINT16: - a = x86.ACMPW - - case gc.OCMP<<16 | gc.TINT32, - gc.OCMP<<16 | gc.TUINT32, - gc.OCMP<<16 | gc.TPTR32: - a = x86.ACMPL - - case gc.OAS<<16 | gc.TBOOL, - gc.OAS<<16 | gc.TINT8, - gc.OAS<<16 | gc.TUINT8: - a = x86.AMOVB - - case gc.OAS<<16 | gc.TINT16, - gc.OAS<<16 | gc.TUINT16: - a = x86.AMOVW - - case gc.OAS<<16 | gc.TINT32, - gc.OAS<<16 | gc.TUINT32, - gc.OAS<<16 | gc.TPTR32: - a = x86.AMOVL - - case gc.OAS<<16 | gc.TFLOAT32: - a = x86.AMOVSS - - case gc.OAS<<16 | gc.TFLOAT64: - a = x86.AMOVSD - - case gc.OADD<<16 | gc.TINT8, - gc.OADD<<16 | gc.TUINT8: - a = x86.AADDB - - case gc.OADD<<16 | gc.TINT16, - gc.OADD<<16 | gc.TUINT16: - a = x86.AADDW - - case gc.OADD<<16 | gc.TINT32, - gc.OADD<<16 | gc.TUINT32, - gc.OADD<<16 | gc.TPTR32: - a = x86.AADDL - - case gc.OSUB<<16 | gc.TINT8, - gc.OSUB<<16 | gc.TUINT8: - a = x86.ASUBB - - case gc.OSUB<<16 | gc.TINT16, - gc.OSUB<<16 | gc.TUINT16: - a = x86.ASUBW - - case gc.OSUB<<16 | gc.TINT32, - gc.OSUB<<16 | gc.TUINT32, - gc.OSUB<<16 | gc.TPTR32: - a = x86.ASUBL - - case gc.OINC<<16 | gc.TINT8, - gc.OINC<<16 | gc.TUINT8: - a = x86.AINCB - - case gc.OINC<<16 | gc.TINT16, - gc.OINC<<16 | gc.TUINT16: - a = x86.AINCW - - case gc.OINC<<16 | gc.TINT32, - gc.OINC<<16 | gc.TUINT32, - gc.OINC<<16 | gc.TPTR32: - a = x86.AINCL - - case gc.ODEC<<16 | gc.TINT8, - gc.ODEC<<16 | gc.TUINT8: - a = x86.ADECB - - case gc.ODEC<<16 | gc.TINT16, - gc.ODEC<<16 | gc.TUINT16: - a = x86.ADECW - - case gc.ODEC<<16 | gc.TINT32, - gc.ODEC<<16 | gc.TUINT32, - gc.ODEC<<16 | gc.TPTR32: - a = x86.ADECL - - case gc.OCOM<<16 | gc.TINT8, - gc.OCOM<<16 | gc.TUINT8: - a = x86.ANOTB - - case gc.OCOM<<16 | gc.TINT16, - gc.OCOM<<16 | gc.TUINT16: - a = x86.ANOTW - - case gc.OCOM<<16 | gc.TINT32, - gc.OCOM<<16 | gc.TUINT32, - gc.OCOM<<16 | gc.TPTR32: - a = x86.ANOTL - - case gc.OMINUS<<16 | gc.TINT8, - gc.OMINUS<<16 | gc.TUINT8: - a = x86.ANEGB - - case gc.OMINUS<<16 | gc.TINT16, - gc.OMINUS<<16 | gc.TUINT16: - a = x86.ANEGW - - case gc.OMINUS<<16 | gc.TINT32, - gc.OMINUS<<16 | gc.TUINT32, - gc.OMINUS<<16 | gc.TPTR32: - a = x86.ANEGL - - case gc.OAND<<16 | gc.TINT8, - gc.OAND<<16 | gc.TUINT8: - a = x86.AANDB - - case gc.OAND<<16 | gc.TINT16, - gc.OAND<<16 | gc.TUINT16: - a = x86.AANDW - - case gc.OAND<<16 | gc.TINT32, - gc.OAND<<16 | gc.TUINT32, - gc.OAND<<16 | gc.TPTR32: - a = x86.AANDL - - case gc.OOR<<16 | gc.TINT8, - gc.OOR<<16 | gc.TUINT8: - a = x86.AORB - - case gc.OOR<<16 | gc.TINT16, - gc.OOR<<16 | gc.TUINT16: - a = x86.AORW - - case gc.OOR<<16 | gc.TINT32, - gc.OOR<<16 | gc.TUINT32, - gc.OOR<<16 | gc.TPTR32: - a = x86.AORL - - case gc.OXOR<<16 | gc.TINT8, - gc.OXOR<<16 | gc.TUINT8: - a = x86.AXORB - - case gc.OXOR<<16 | gc.TINT16, - gc.OXOR<<16 | gc.TUINT16: - a = x86.AXORW - - case gc.OXOR<<16 | gc.TINT32, - gc.OXOR<<16 | gc.TUINT32, - gc.OXOR<<16 | gc.TPTR32: - a = x86.AXORL - - case gc.OLROT<<16 | gc.TINT8, - gc.OLROT<<16 | gc.TUINT8: - a = x86.AROLB - - case gc.OLROT<<16 | gc.TINT16, - gc.OLROT<<16 | gc.TUINT16: - a = x86.AROLW - - case gc.OLROT<<16 | gc.TINT32, - gc.OLROT<<16 | gc.TUINT32, - gc.OLROT<<16 | gc.TPTR32: - a = x86.AROLL - - case gc.OLSH<<16 | gc.TINT8, - gc.OLSH<<16 | gc.TUINT8: - a = x86.ASHLB - - case gc.OLSH<<16 | gc.TINT16, - gc.OLSH<<16 | gc.TUINT16: - a = x86.ASHLW - - case gc.OLSH<<16 | gc.TINT32, - gc.OLSH<<16 | gc.TUINT32, - gc.OLSH<<16 | gc.TPTR32: - a = x86.ASHLL - - case gc.ORSH<<16 | gc.TUINT8: - a = x86.ASHRB - - case gc.ORSH<<16 | gc.TUINT16: - a = x86.ASHRW - - case gc.ORSH<<16 | gc.TUINT32, - gc.ORSH<<16 | gc.TPTR32: - a = x86.ASHRL - - case gc.ORSH<<16 | gc.TINT8: - a = x86.ASARB - - case gc.ORSH<<16 | gc.TINT16: - a = x86.ASARW - - case gc.ORSH<<16 | gc.TINT32: - a = x86.ASARL - - case gc.OHMUL<<16 | gc.TINT8, - gc.OMUL<<16 | gc.TINT8, - gc.OMUL<<16 | gc.TUINT8: - a = x86.AIMULB - - case gc.OHMUL<<16 | gc.TINT16, - gc.OMUL<<16 | gc.TINT16, - gc.OMUL<<16 | gc.TUINT16: - a = x86.AIMULW - - case gc.OHMUL<<16 | gc.TINT32, - gc.OMUL<<16 | gc.TINT32, - gc.OMUL<<16 | gc.TUINT32, - gc.OMUL<<16 | gc.TPTR32: - a = x86.AIMULL - - case gc.OHMUL<<16 | gc.TUINT8: - a = x86.AMULB - - case gc.OHMUL<<16 | gc.TUINT16: - a = x86.AMULW - - case gc.OHMUL<<16 | gc.TUINT32, - gc.OHMUL<<16 | gc.TPTR32: - a = x86.AMULL - - case gc.ODIV<<16 | gc.TINT8, - gc.OMOD<<16 | gc.TINT8: - a = x86.AIDIVB - - case gc.ODIV<<16 | gc.TUINT8, - gc.OMOD<<16 | gc.TUINT8: - a = x86.ADIVB - - case gc.ODIV<<16 | gc.TINT16, - gc.OMOD<<16 | gc.TINT16: - a = x86.AIDIVW - - case gc.ODIV<<16 | gc.TUINT16, - gc.OMOD<<16 | gc.TUINT16: - a = x86.ADIVW - - case gc.ODIV<<16 | gc.TINT32, - gc.OMOD<<16 | gc.TINT32: - a = x86.AIDIVL - - case gc.ODIV<<16 | gc.TUINT32, - gc.ODIV<<16 | gc.TPTR32, - gc.OMOD<<16 | gc.TUINT32, - gc.OMOD<<16 | gc.TPTR32: - a = x86.ADIVL - - case gc.OEXTEND<<16 | gc.TINT16: - a = x86.ACWD - - case gc.OEXTEND<<16 | gc.TINT32: - a = x86.ACDQ - } - - return a -} - -func foptoas(op int, t *gc.Type, flg int) int { - a := obj.AXXX - et := int(gc.Simtype[t.Etype]) - - if !gc.Thearch.Use387 { - switch uint32(op)<<16 | uint32(et) { - default: - gc.Fatal("foptoas-sse: no entry %v-%v", gc.Oconv(int(op), 0), t) - - case gc.OCMP<<16 | gc.TFLOAT32: - a = x86.AUCOMISS - - case gc.OCMP<<16 | gc.TFLOAT64: - a = x86.AUCOMISD - - case gc.OAS<<16 | gc.TFLOAT32: - a = x86.AMOVSS - - case gc.OAS<<16 | gc.TFLOAT64: - a = x86.AMOVSD - - case gc.OADD<<16 | gc.TFLOAT32: - a = x86.AADDSS - - case gc.OADD<<16 | gc.TFLOAT64: - a = x86.AADDSD - - case gc.OSUB<<16 | gc.TFLOAT32: - a = x86.ASUBSS - - case gc.OSUB<<16 | gc.TFLOAT64: - a = x86.ASUBSD - - case gc.OMUL<<16 | gc.TFLOAT32: - a = x86.AMULSS - - case gc.OMUL<<16 | gc.TFLOAT64: - a = x86.AMULSD - - case gc.ODIV<<16 | gc.TFLOAT32: - a = x86.ADIVSS - - case gc.ODIV<<16 | gc.TFLOAT64: - a = x86.ADIVSD - } - - return a - } - - // If we need Fpop, it means we're working on - // two different floating-point registers, not memory. - // There the instruction only has a float64 form. - if flg&Fpop != 0 { - et = gc.TFLOAT64 - } - - // clear Frev if unneeded - switch op { - case gc.OADD, - gc.OMUL: - flg &^= Frev - } - - switch uint32(op)<<16 | (uint32(et)<<8 | uint32(flg)) { - case gc.OADD<<16 | (gc.TFLOAT32<<8 | 0): - return x86.AFADDF - - case gc.OADD<<16 | (gc.TFLOAT64<<8 | 0): - return x86.AFADDD - - case gc.OADD<<16 | (gc.TFLOAT64<<8 | Fpop): - return x86.AFADDDP - - case gc.OSUB<<16 | (gc.TFLOAT32<<8 | 0): - return x86.AFSUBF - - case gc.OSUB<<16 | (gc.TFLOAT32<<8 | Frev): - return x86.AFSUBRF - - case gc.OSUB<<16 | (gc.TFLOAT64<<8 | 0): - return x86.AFSUBD - - case gc.OSUB<<16 | (gc.TFLOAT64<<8 | Frev): - return x86.AFSUBRD - - case gc.OSUB<<16 | (gc.TFLOAT64<<8 | Fpop): - return x86.AFSUBDP - - case gc.OSUB<<16 | (gc.TFLOAT64<<8 | (Fpop | Frev)): - return x86.AFSUBRDP - - case gc.OMUL<<16 | (gc.TFLOAT32<<8 | 0): - return x86.AFMULF - - case gc.OMUL<<16 | (gc.TFLOAT64<<8 | 0): - return x86.AFMULD - - case gc.OMUL<<16 | (gc.TFLOAT64<<8 | Fpop): - return x86.AFMULDP - - case gc.ODIV<<16 | (gc.TFLOAT32<<8 | 0): - return x86.AFDIVF - - case gc.ODIV<<16 | (gc.TFLOAT32<<8 | Frev): - return x86.AFDIVRF - - case gc.ODIV<<16 | (gc.TFLOAT64<<8 | 0): - return x86.AFDIVD - - case gc.ODIV<<16 | (gc.TFLOAT64<<8 | Frev): - return x86.AFDIVRD - - case gc.ODIV<<16 | (gc.TFLOAT64<<8 | Fpop): - return x86.AFDIVDP - - case gc.ODIV<<16 | (gc.TFLOAT64<<8 | (Fpop | Frev)): - return x86.AFDIVRDP - - case gc.OCMP<<16 | (gc.TFLOAT32<<8 | 0): - return x86.AFCOMF - - case gc.OCMP<<16 | (gc.TFLOAT32<<8 | Fpop): - return x86.AFCOMFP - - case gc.OCMP<<16 | (gc.TFLOAT64<<8 | 0): - return x86.AFCOMD - - case gc.OCMP<<16 | (gc.TFLOAT64<<8 | Fpop): - return x86.AFCOMDP - - case gc.OCMP<<16 | (gc.TFLOAT64<<8 | Fpop2): - return x86.AFCOMDPP - - case gc.OMINUS<<16 | (gc.TFLOAT32<<8 | 0): - return x86.AFCHS - - case gc.OMINUS<<16 | (gc.TFLOAT64<<8 | 0): - return x86.AFCHS - } - - gc.Fatal("foptoas %v %v %#x", gc.Oconv(int(op), 0), t, flg) - return 0 -} - -var resvd = []int{ - // REG_DI, // for movstring - // REG_SI, // for movstring - - x86.REG_AX, // for divide - x86.REG_CX, // for shift - x86.REG_DX, // for divide - x86.REG_SP, // for stack -} - -/* - * generate - * as $c, reg - */ -func gconreg(as int, c int64, reg int) { - var n1 gc.Node - var n2 gc.Node - - gc.Nodconst(&n1, gc.Types[gc.TINT64], c) - gc.Nodreg(&n2, gc.Types[gc.TINT64], reg) - gins(as, &n1, &n2) -} - -/* - * generate - * as $c, n - */ -func ginscon(as int, c int64, n2 *gc.Node) { - var n1 gc.Node - gc.Nodconst(&n1, gc.Types[gc.TINT32], c) - 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 - */ -func nswap(a *gc.Node, b *gc.Node) { - t := *a - *a = *b - *b = t -} - -/* - * return constant i node. - * overwritten by next call, but useful in calls to gins. - */ - -var ncon_n gc.Node - -func ncon(i uint32) *gc.Node { - if ncon_n.Type == nil { - gc.Nodconst(&ncon_n, gc.Types[gc.TUINT32], 0) - } - ncon_n.SetInt(int64(i)) - return &ncon_n -} - -var sclean [10]gc.Node - -var nsclean int - -/* - * n is a 64-bit value. fill in lo and hi to refer to its 32-bit halves. - */ -func split64(n *gc.Node, lo *gc.Node, hi *gc.Node) { - if !gc.Is64(n.Type) { - gc.Fatal("split64 %v", n.Type) - } - - if nsclean >= len(sclean) { - gc.Fatal("split64 clean") - } - sclean[nsclean].Op = gc.OEMPTY - nsclean++ - switch n.Op { - default: - switch n.Op { - default: - var n1 gc.Node - if !dotaddable(n, &n1) { - gc.Igen(n, &n1, nil) - sclean[nsclean-1] = n1 - } - - n = &n1 - - case gc.ONAME: - if n.Class == gc.PPARAMREF { - var n1 gc.Node - gc.Cgen(n.Name.Heapaddr, &n1) - sclean[nsclean-1] = n1 - n = &n1 - } - - // nothing - case gc.OINDREG: - break - } - - *lo = *n - *hi = *n - lo.Type = gc.Types[gc.TUINT32] - if n.Type.Etype == gc.TINT64 { - hi.Type = gc.Types[gc.TINT32] - } else { - hi.Type = gc.Types[gc.TUINT32] - } - hi.Xoffset += 4 - - case gc.OLITERAL: - var n1 gc.Node - n.Convconst(&n1, n.Type) - i := n1.Int() - gc.Nodconst(lo, gc.Types[gc.TUINT32], int64(uint32(i))) - i >>= 32 - if n.Type.Etype == gc.TINT64 { - gc.Nodconst(hi, gc.Types[gc.TINT32], int64(int32(i))) - } else { - gc.Nodconst(hi, gc.Types[gc.TUINT32], int64(uint32(i))) - } - } -} - -func splitclean() { - if nsclean <= 0 { - gc.Fatal("splitclean") - } - nsclean-- - if sclean[nsclean].Op != gc.OEMPTY { - gc.Regfree(&sclean[nsclean]) - } -} - -// set up nodes representing fp constants -var ( - zerof gc.Node - two63f gc.Node - two64f gc.Node - bignodes_did bool -) - -func bignodes() { - if bignodes_did { - return - } - bignodes_did = true - - gc.Nodconst(&zerof, gc.Types[gc.TINT64], 0) - zerof.Convconst(&zerof, gc.Types[gc.TFLOAT64]) - - var i big.Int - i.SetInt64(1) - i.Lsh(&i, 63) - var bigi gc.Node - - gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0) - bigi.SetBigInt(&i) - bigi.Convconst(&two63f, gc.Types[gc.TFLOAT64]) - - gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0) - i.Lsh(&i, 1) - bigi.SetBigInt(&i) - bigi.Convconst(&two64f, gc.Types[gc.TFLOAT64]) -} - -func memname(n *gc.Node, t *gc.Type) { - gc.Tempname(n, t) - n.Sym = gc.Lookup("." + n.Sym.Name[1:]) // keep optimizer from registerizing - n.Orig.Sym = n.Sym -} - -func gmove(f *gc.Node, t *gc.Node) { - if gc.Debug['M'] != 0 { - fmt.Printf("gmove %v -> %v\n", f, t) - } - - ft := gc.Simsimtype(f.Type) - tt := gc.Simsimtype(t.Type) - cvt := t.Type - - if gc.Iscomplex[ft] || gc.Iscomplex[tt] { - gc.Complexmove(f, t) - return - } - - if gc.Isfloat[ft] || gc.Isfloat[tt] { - floatmove(f, t) - return - } - - // cannot have two integer memory operands; - // except 64-bit, which always copies via registers anyway. - var r1 gc.Node - var a int - if gc.Isint[ft] && gc.Isint[tt] && !gc.Is64(f.Type) && !gc.Is64(t.Type) && gc.Ismem(f) && gc.Ismem(t) { - goto hard - } - - // convert constant to desired type - if f.Op == gc.OLITERAL { - var con gc.Node - f.Convconst(&con, t.Type) - f = &con - ft = gc.Simsimtype(con.Type) - } - - // value -> value copy, only one memory operand. - // figure out the instruction to use. - // break out of switch for one-instruction gins. - // goto rdst for "destination must be register". - // goto hard for "convert to cvt type first". - // otherwise handle and return. - - switch uint32(ft)<<16 | uint32(tt) { - default: - // should not happen - gc.Fatal("gmove %v -> %v", f, t) - return - - /* - * integer copy and truncate - */ - case gc.TINT8<<16 | gc.TINT8, // same size - gc.TINT8<<16 | gc.TUINT8, - gc.TUINT8<<16 | gc.TINT8, - gc.TUINT8<<16 | gc.TUINT8: - a = x86.AMOVB - - case gc.TINT16<<16 | gc.TINT8, // truncate - gc.TUINT16<<16 | gc.TINT8, - gc.TINT32<<16 | gc.TINT8, - gc.TUINT32<<16 | gc.TINT8, - gc.TINT16<<16 | gc.TUINT8, - gc.TUINT16<<16 | gc.TUINT8, - gc.TINT32<<16 | gc.TUINT8, - gc.TUINT32<<16 | gc.TUINT8: - a = x86.AMOVB - - goto rsrc - - case gc.TINT64<<16 | gc.TINT8, // truncate low word - gc.TUINT64<<16 | gc.TINT8, - gc.TINT64<<16 | gc.TUINT8, - gc.TUINT64<<16 | gc.TUINT8: - var flo gc.Node - var fhi gc.Node - split64(f, &flo, &fhi) - - var r1 gc.Node - gc.Nodreg(&r1, t.Type, x86.REG_AX) - gmove(&flo, &r1) - gins(x86.AMOVB, &r1, t) - splitclean() - return - - case gc.TINT16<<16 | gc.TINT16, // same size - gc.TINT16<<16 | gc.TUINT16, - gc.TUINT16<<16 | gc.TINT16, - gc.TUINT16<<16 | gc.TUINT16: - a = x86.AMOVW - - case gc.TINT32<<16 | gc.TINT16, // truncate - gc.TUINT32<<16 | gc.TINT16, - gc.TINT32<<16 | gc.TUINT16, - gc.TUINT32<<16 | gc.TUINT16: - a = x86.AMOVW - - goto rsrc - - case gc.TINT64<<16 | gc.TINT16, // truncate low word - gc.TUINT64<<16 | gc.TINT16, - gc.TINT64<<16 | gc.TUINT16, - gc.TUINT64<<16 | gc.TUINT16: - var flo gc.Node - var fhi gc.Node - split64(f, &flo, &fhi) - - var r1 gc.Node - gc.Nodreg(&r1, t.Type, x86.REG_AX) - gmove(&flo, &r1) - gins(x86.AMOVW, &r1, t) - splitclean() - return - - case gc.TINT32<<16 | gc.TINT32, // same size - gc.TINT32<<16 | gc.TUINT32, - gc.TUINT32<<16 | gc.TINT32, - gc.TUINT32<<16 | gc.TUINT32: - a = x86.AMOVL - - case gc.TINT64<<16 | gc.TINT32, // truncate - gc.TUINT64<<16 | gc.TINT32, - gc.TINT64<<16 | gc.TUINT32, - gc.TUINT64<<16 | gc.TUINT32: - var fhi gc.Node - var flo gc.Node - split64(f, &flo, &fhi) - - var r1 gc.Node - gc.Nodreg(&r1, t.Type, x86.REG_AX) - gmove(&flo, &r1) - gins(x86.AMOVL, &r1, t) - splitclean() - return - - case gc.TINT64<<16 | gc.TINT64, // same size - gc.TINT64<<16 | gc.TUINT64, - gc.TUINT64<<16 | gc.TINT64, - gc.TUINT64<<16 | gc.TUINT64: - var fhi gc.Node - var flo gc.Node - split64(f, &flo, &fhi) - - var tlo gc.Node - var thi gc.Node - split64(t, &tlo, &thi) - if f.Op == gc.OLITERAL { - gins(x86.AMOVL, &flo, &tlo) - gins(x86.AMOVL, &fhi, &thi) - } else { - var r1 gc.Node - gc.Nodreg(&r1, gc.Types[gc.TUINT32], x86.REG_AX) - var r2 gc.Node - gc.Nodreg(&r2, gc.Types[gc.TUINT32], x86.REG_DX) - gins(x86.AMOVL, &flo, &r1) - gins(x86.AMOVL, &fhi, &r2) - gins(x86.AMOVL, &r1, &tlo) - gins(x86.AMOVL, &r2, &thi) - } - - splitclean() - splitclean() - return - - /* - * integer up-conversions - */ - case gc.TINT8<<16 | gc.TINT16, // sign extend int8 - gc.TINT8<<16 | gc.TUINT16: - a = x86.AMOVBWSX - - goto rdst - - case gc.TINT8<<16 | gc.TINT32, - gc.TINT8<<16 | gc.TUINT32: - a = x86.AMOVBLSX - goto rdst - - case gc.TINT8<<16 | gc.TINT64, // convert via int32 - gc.TINT8<<16 | gc.TUINT64: - cvt = gc.Types[gc.TINT32] - - goto hard - - case gc.TUINT8<<16 | gc.TINT16, // zero extend uint8 - gc.TUINT8<<16 | gc.TUINT16: - a = x86.AMOVBWZX - - goto rdst - - case gc.TUINT8<<16 | gc.TINT32, - gc.TUINT8<<16 | gc.TUINT32: - a = x86.AMOVBLZX - goto rdst - - case gc.TUINT8<<16 | gc.TINT64, // convert via uint32 - gc.TUINT8<<16 | gc.TUINT64: - cvt = gc.Types[gc.TUINT32] - - goto hard - - case gc.TINT16<<16 | gc.TINT32, // sign extend int16 - gc.TINT16<<16 | gc.TUINT32: - a = x86.AMOVWLSX - - goto rdst - - case gc.TINT16<<16 | gc.TINT64, // convert via int32 - gc.TINT16<<16 | gc.TUINT64: - cvt = gc.Types[gc.TINT32] - - goto hard - - case gc.TUINT16<<16 | gc.TINT32, // zero extend uint16 - gc.TUINT16<<16 | gc.TUINT32: - a = x86.AMOVWLZX - - goto rdst - - case gc.TUINT16<<16 | gc.TINT64, // convert via uint32 - gc.TUINT16<<16 | gc.TUINT64: - cvt = gc.Types[gc.TUINT32] - - goto hard - - case gc.TINT32<<16 | gc.TINT64, // sign extend int32 - gc.TINT32<<16 | gc.TUINT64: - var thi gc.Node - var tlo gc.Node - split64(t, &tlo, &thi) - - var flo gc.Node - gc.Nodreg(&flo, tlo.Type, x86.REG_AX) - var fhi gc.Node - gc.Nodreg(&fhi, thi.Type, x86.REG_DX) - gmove(f, &flo) - gins(x86.ACDQ, nil, nil) - gins(x86.AMOVL, &flo, &tlo) - gins(x86.AMOVL, &fhi, &thi) - splitclean() - return - - case gc.TUINT32<<16 | gc.TINT64, // zero extend uint32 - gc.TUINT32<<16 | gc.TUINT64: - var tlo gc.Node - var thi gc.Node - split64(t, &tlo, &thi) - - gmove(f, &tlo) - gins(x86.AMOVL, ncon(0), &thi) - splitclean() - return - } - - gins(a, f, t) - return - - // requires register source -rsrc: - gc.Regalloc(&r1, f.Type, t) - - gmove(f, &r1) - gins(a, &r1, t) - gc.Regfree(&r1) - return - - // requires register destination -rdst: - { - gc.Regalloc(&r1, t.Type, t) - - gins(a, f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - } - - // requires register intermediate -hard: - gc.Regalloc(&r1, cvt, t) - - gmove(f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return -} - -func floatmove(f *gc.Node, t *gc.Node) { - var r1 gc.Node - - ft := gc.Simsimtype(f.Type) - tt := gc.Simsimtype(t.Type) - cvt := t.Type - - // cannot have two floating point memory operands. - if gc.Isfloat[ft] && gc.Isfloat[tt] && gc.Ismem(f) && gc.Ismem(t) { - goto hard - } - - // convert constant to desired type - if f.Op == gc.OLITERAL { - var con gc.Node - f.Convconst(&con, t.Type) - f = &con - ft = gc.Simsimtype(con.Type) - - // some constants can't move directly to memory. - if gc.Ismem(t) { - // float constants come from memory. - if gc.Isfloat[tt] { - goto hard - } - } - } - - // value -> value copy, only one memory operand. - // figure out the instruction to use. - // break out of switch for one-instruction gins. - // goto rdst for "destination must be register". - // goto hard for "convert to cvt type first". - // otherwise handle and return. - - switch uint32(ft)<<16 | uint32(tt) { - default: - if gc.Thearch.Use387 { - floatmove_387(f, t) - } else { - floatmove_sse(f, t) - } - return - - // float to very long integer. - case gc.TFLOAT32<<16 | gc.TINT64, - gc.TFLOAT64<<16 | gc.TINT64: - if f.Op == gc.OREGISTER { - cvt = f.Type - goto hardmem - } - - var r1 gc.Node - gc.Nodreg(&r1, gc.Types[ft], x86.REG_F0) - if ft == gc.TFLOAT32 { - gins(x86.AFMOVF, f, &r1) - } else { - gins(x86.AFMOVD, f, &r1) - } - - // set round to zero mode during conversion - var t1 gc.Node - memname(&t1, gc.Types[gc.TUINT16]) - - var t2 gc.Node - memname(&t2, gc.Types[gc.TUINT16]) - gins(x86.AFSTCW, nil, &t1) - gins(x86.AMOVW, ncon(0xf7f), &t2) - gins(x86.AFLDCW, &t2, nil) - if tt == gc.TINT16 { - gins(x86.AFMOVWP, &r1, t) - } else if tt == gc.TINT32 { - gins(x86.AFMOVLP, &r1, t) - } else { - gins(x86.AFMOVVP, &r1, t) - } - gins(x86.AFLDCW, &t1, nil) - return - - case gc.TFLOAT32<<16 | gc.TUINT64, - gc.TFLOAT64<<16 | gc.TUINT64: - if !gc.Ismem(f) { - cvt = f.Type - goto hardmem - } - - bignodes() - var f0 gc.Node - gc.Nodreg(&f0, gc.Types[ft], x86.REG_F0) - var f1 gc.Node - gc.Nodreg(&f1, gc.Types[ft], x86.REG_F0+1) - var ax gc.Node - gc.Nodreg(&ax, gc.Types[gc.TUINT16], x86.REG_AX) - - if ft == gc.TFLOAT32 { - gins(x86.AFMOVF, f, &f0) - } else { - gins(x86.AFMOVD, f, &f0) - } - - // if 0 > v { answer = 0 } - gins(x86.AFMOVD, &zerof, &f0) - - gins(x86.AFUCOMIP, &f0, &f1) - p1 := gc.Gbranch(optoas(gc.OGT, gc.Types[tt]), nil, 0) - - // if 1<<64 <= v { answer = 0 too } - gins(x86.AFMOVD, &two64f, &f0) - - gins(x86.AFUCOMIP, &f0, &f1) - p2 := gc.Gbranch(optoas(gc.OGT, gc.Types[tt]), nil, 0) - gc.Patch(p1, gc.Pc) - gins(x86.AFMOVVP, &f0, t) // don't care about t, but will pop the stack - var thi gc.Node - var tlo gc.Node - split64(t, &tlo, &thi) - gins(x86.AMOVL, ncon(0), &tlo) - gins(x86.AMOVL, ncon(0), &thi) - splitclean() - p1 = gc.Gbranch(obj.AJMP, nil, 0) - gc.Patch(p2, gc.Pc) - - // in range; algorithm is: - // if small enough, use native float64 -> int64 conversion. - // otherwise, subtract 2^63, convert, and add it back. - - // set round to zero mode during conversion - var t1 gc.Node - memname(&t1, gc.Types[gc.TUINT16]) - - var t2 gc.Node - memname(&t2, gc.Types[gc.TUINT16]) - gins(x86.AFSTCW, nil, &t1) - gins(x86.AMOVW, ncon(0xf7f), &t2) - gins(x86.AFLDCW, &t2, nil) - - // actual work - gins(x86.AFMOVD, &two63f, &f0) - - gins(x86.AFUCOMIP, &f0, &f1) - p2 = gc.Gbranch(optoas(gc.OLE, gc.Types[tt]), nil, 0) - gins(x86.AFMOVVP, &f0, t) - p3 := gc.Gbranch(obj.AJMP, nil, 0) - gc.Patch(p2, gc.Pc) - gins(x86.AFMOVD, &two63f, &f0) - gins(x86.AFSUBDP, &f0, &f1) - gins(x86.AFMOVVP, &f0, t) - split64(t, &tlo, &thi) - gins(x86.AXORL, ncon(0x80000000), &thi) // + 2^63 - gc.Patch(p3, gc.Pc) - splitclean() - - // restore rounding mode - gins(x86.AFLDCW, &t1, nil) - - gc.Patch(p1, gc.Pc) - return - - /* - * integer to float - */ - case gc.TINT64<<16 | gc.TFLOAT32, - gc.TINT64<<16 | gc.TFLOAT64: - if t.Op == gc.OREGISTER { - goto hardmem - } - var f0 gc.Node - gc.Nodreg(&f0, t.Type, x86.REG_F0) - gins(x86.AFMOVV, f, &f0) - if tt == gc.TFLOAT32 { - gins(x86.AFMOVFP, &f0, t) - } else { - gins(x86.AFMOVDP, &f0, t) - } - return - - // algorithm is: - // if small enough, use native int64 -> float64 conversion. - // otherwise, halve (rounding to odd?), convert, and double. - case gc.TUINT64<<16 | gc.TFLOAT32, - gc.TUINT64<<16 | gc.TFLOAT64: - var ax gc.Node - gc.Nodreg(&ax, gc.Types[gc.TUINT32], x86.REG_AX) - - var dx gc.Node - gc.Nodreg(&dx, gc.Types[gc.TUINT32], x86.REG_DX) - var cx gc.Node - gc.Nodreg(&cx, gc.Types[gc.TUINT32], x86.REG_CX) - var t1 gc.Node - gc.Tempname(&t1, f.Type) - var tlo gc.Node - var thi gc.Node - split64(&t1, &tlo, &thi) - gmove(f, &t1) - gins(x86.ACMPL, &thi, ncon(0)) - p1 := gc.Gbranch(x86.AJLT, nil, 0) - - // native - var r1 gc.Node - gc.Nodreg(&r1, gc.Types[tt], x86.REG_F0) - - gins(x86.AFMOVV, &t1, &r1) - if tt == gc.TFLOAT32 { - gins(x86.AFMOVFP, &r1, t) - } else { - gins(x86.AFMOVDP, &r1, t) - } - p2 := gc.Gbranch(obj.AJMP, nil, 0) - - // simulated - gc.Patch(p1, gc.Pc) - - gmove(&tlo, &ax) - gmove(&thi, &dx) - p1 = gins(x86.ASHRL, ncon(1), &ax) - p1.From.Index = x86.REG_DX // double-width shift DX -> AX - p1.From.Scale = 0 - gins(x86.AMOVL, ncon(0), &cx) - gins(x86.ASETCC, nil, &cx) - gins(x86.AORL, &cx, &ax) - gins(x86.ASHRL, ncon(1), &dx) - gmove(&dx, &thi) - gmove(&ax, &tlo) - gc.Nodreg(&r1, gc.Types[tt], x86.REG_F0) - var r2 gc.Node - gc.Nodreg(&r2, gc.Types[tt], x86.REG_F0+1) - gins(x86.AFMOVV, &t1, &r1) - gins(x86.AFMOVD, &r1, &r1) - gins(x86.AFADDDP, &r1, &r2) - if tt == gc.TFLOAT32 { - gins(x86.AFMOVFP, &r1, t) - } else { - gins(x86.AFMOVDP, &r1, t) - } - gc.Patch(p2, gc.Pc) - splitclean() - return - } - - // requires register intermediate -hard: - gc.Regalloc(&r1, cvt, t) - - gmove(f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - - // requires memory intermediate -hardmem: - gc.Tempname(&r1, cvt) - - gmove(f, &r1) - gmove(&r1, t) - return -} - -func floatmove_387(f *gc.Node, t *gc.Node) { - var r1 gc.Node - var a int - - ft := gc.Simsimtype(f.Type) - tt := gc.Simsimtype(t.Type) - cvt := t.Type - - switch uint32(ft)<<16 | uint32(tt) { - default: - goto fatal - - /* - * float to integer - */ - case gc.TFLOAT32<<16 | gc.TINT16, - gc.TFLOAT32<<16 | gc.TINT32, - gc.TFLOAT32<<16 | gc.TINT64, - gc.TFLOAT64<<16 | gc.TINT16, - gc.TFLOAT64<<16 | gc.TINT32, - gc.TFLOAT64<<16 | gc.TINT64: - if t.Op == gc.OREGISTER { - goto hardmem - } - var r1 gc.Node - gc.Nodreg(&r1, gc.Types[ft], x86.REG_F0) - if f.Op != gc.OREGISTER { - if ft == gc.TFLOAT32 { - gins(x86.AFMOVF, f, &r1) - } else { - gins(x86.AFMOVD, f, &r1) - } - } - - // set round to zero mode during conversion - var t1 gc.Node - memname(&t1, gc.Types[gc.TUINT16]) - - var t2 gc.Node - memname(&t2, gc.Types[gc.TUINT16]) - gins(x86.AFSTCW, nil, &t1) - gins(x86.AMOVW, ncon(0xf7f), &t2) - gins(x86.AFLDCW, &t2, nil) - if tt == gc.TINT16 { - gins(x86.AFMOVWP, &r1, t) - } else if tt == gc.TINT32 { - gins(x86.AFMOVLP, &r1, t) - } else { - gins(x86.AFMOVVP, &r1, t) - } - gins(x86.AFLDCW, &t1, nil) - return - - // convert via int32. - case gc.TFLOAT32<<16 | gc.TINT8, - gc.TFLOAT32<<16 | gc.TUINT16, - gc.TFLOAT32<<16 | gc.TUINT8, - gc.TFLOAT64<<16 | gc.TINT8, - gc.TFLOAT64<<16 | gc.TUINT16, - gc.TFLOAT64<<16 | gc.TUINT8: - var t1 gc.Node - gc.Tempname(&t1, gc.Types[gc.TINT32]) - - gmove(f, &t1) - switch tt { - default: - gc.Fatal("gmove %v", t) - - case gc.TINT8: - gins(x86.ACMPL, &t1, ncon(-0x80&(1<<32-1))) - p1 := gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TINT32]), nil, -1) - gins(x86.ACMPL, &t1, ncon(0x7f)) - p2 := gc.Gbranch(optoas(gc.OGT, gc.Types[gc.TINT32]), nil, -1) - p3 := gc.Gbranch(obj.AJMP, nil, 0) - gc.Patch(p1, gc.Pc) - gc.Patch(p2, gc.Pc) - gmove(ncon(-0x80&(1<<32-1)), &t1) - gc.Patch(p3, gc.Pc) - gmove(&t1, t) - - case gc.TUINT8: - gins(x86.ATESTL, ncon(0xffffff00), &t1) - p1 := gc.Gbranch(x86.AJEQ, nil, +1) - gins(x86.AMOVL, ncon(0), &t1) - gc.Patch(p1, gc.Pc) - gmove(&t1, t) - - case gc.TUINT16: - gins(x86.ATESTL, ncon(0xffff0000), &t1) - p1 := gc.Gbranch(x86.AJEQ, nil, +1) - gins(x86.AMOVL, ncon(0), &t1) - gc.Patch(p1, gc.Pc) - gmove(&t1, t) - } - - return - - // convert via int64. - case gc.TFLOAT32<<16 | gc.TUINT32, - gc.TFLOAT64<<16 | gc.TUINT32: - cvt = gc.Types[gc.TINT64] - - goto hardmem - - /* - * integer to float - */ - case gc.TINT16<<16 | gc.TFLOAT32, - gc.TINT16<<16 | gc.TFLOAT64, - gc.TINT32<<16 | gc.TFLOAT32, - gc.TINT32<<16 | gc.TFLOAT64, - gc.TINT64<<16 | gc.TFLOAT32, - gc.TINT64<<16 | gc.TFLOAT64: - if t.Op != gc.OREGISTER { - goto hard - } - if f.Op == gc.OREGISTER { - cvt = f.Type - goto hardmem - } - - switch ft { - case gc.TINT16: - a = x86.AFMOVW - - case gc.TINT32: - a = x86.AFMOVL - - default: - a = x86.AFMOVV - } - - // convert via int32 memory - case gc.TINT8<<16 | gc.TFLOAT32, - gc.TINT8<<16 | gc.TFLOAT64, - gc.TUINT16<<16 | gc.TFLOAT32, - gc.TUINT16<<16 | gc.TFLOAT64, - gc.TUINT8<<16 | gc.TFLOAT32, - gc.TUINT8<<16 | gc.TFLOAT64: - cvt = gc.Types[gc.TINT32] - - goto hardmem - - // convert via int64 memory - case gc.TUINT32<<16 | gc.TFLOAT32, - gc.TUINT32<<16 | gc.TFLOAT64: - cvt = gc.Types[gc.TINT64] - - goto hardmem - - // The way the code generator uses floating-point - // registers, a move from F0 to F0 is intended as a no-op. - // On the x86, it's not: it pushes a second copy of F0 - // on the floating point stack. So toss it away here. - // Also, F0 is the *only* register we ever evaluate - // into, so we should only see register/register as F0/F0. - /* - * float to float - */ - case gc.TFLOAT32<<16 | gc.TFLOAT32, - gc.TFLOAT64<<16 | gc.TFLOAT64: - if gc.Ismem(f) && gc.Ismem(t) { - goto hard - } - if f.Op == gc.OREGISTER && t.Op == gc.OREGISTER { - if f.Reg != x86.REG_F0 || t.Reg != x86.REG_F0 { - goto fatal - } - return - } - - a = x86.AFMOVF - if ft == gc.TFLOAT64 { - a = x86.AFMOVD - } - if gc.Ismem(t) { - if f.Op != gc.OREGISTER || f.Reg != x86.REG_F0 { - gc.Fatal("gmove %v", f) - } - a = x86.AFMOVFP - if ft == gc.TFLOAT64 { - a = x86.AFMOVDP - } - } - - case gc.TFLOAT32<<16 | gc.TFLOAT64: - if gc.Ismem(f) && gc.Ismem(t) { - goto hard - } - if f.Op == gc.OREGISTER && t.Op == gc.OREGISTER { - if f.Reg != x86.REG_F0 || t.Reg != x86.REG_F0 { - goto fatal - } - return - } - - if f.Op == gc.OREGISTER { - gins(x86.AFMOVDP, f, t) - } else { - gins(x86.AFMOVF, f, t) - } - return - - case gc.TFLOAT64<<16 | gc.TFLOAT32: - if gc.Ismem(f) && gc.Ismem(t) { - goto hard - } - if f.Op == gc.OREGISTER && t.Op == gc.OREGISTER { - var r1 gc.Node - gc.Tempname(&r1, gc.Types[gc.TFLOAT32]) - gins(x86.AFMOVFP, f, &r1) - gins(x86.AFMOVF, &r1, t) - return - } - - if f.Op == gc.OREGISTER { - gins(x86.AFMOVFP, f, t) - } else { - gins(x86.AFMOVD, f, t) - } - return - } - - gins(a, f, t) - return - - // requires register intermediate -hard: - gc.Regalloc(&r1, cvt, t) - - gmove(f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - - // requires memory intermediate -hardmem: - gc.Tempname(&r1, cvt) - - gmove(f, &r1) - gmove(&r1, t) - return - - // should not happen -fatal: - gc.Fatal("gmove %v -> %v", gc.Nconv(f, obj.FmtLong), gc.Nconv(t, obj.FmtLong)) - - return -} - -func floatmove_sse(f *gc.Node, t *gc.Node) { - var r1 gc.Node - var cvt *gc.Type - var a int - - ft := gc.Simsimtype(f.Type) - tt := gc.Simsimtype(t.Type) - - switch uint32(ft)<<16 | uint32(tt) { - // should not happen - default: - gc.Fatal("gmove %v -> %v", f, t) - - return - - // convert via int32. - /* - * float to integer - */ - case gc.TFLOAT32<<16 | gc.TINT16, - gc.TFLOAT32<<16 | gc.TINT8, - gc.TFLOAT32<<16 | gc.TUINT16, - gc.TFLOAT32<<16 | gc.TUINT8, - gc.TFLOAT64<<16 | gc.TINT16, - gc.TFLOAT64<<16 | gc.TINT8, - gc.TFLOAT64<<16 | gc.TUINT16, - gc.TFLOAT64<<16 | gc.TUINT8: - cvt = gc.Types[gc.TINT32] - - goto hard - - // convert via int64. - case gc.TFLOAT32<<16 | gc.TUINT32, - gc.TFLOAT64<<16 | gc.TUINT32: - cvt = gc.Types[gc.TINT64] - - goto hardmem - - case gc.TFLOAT32<<16 | gc.TINT32: - a = x86.ACVTTSS2SL - goto rdst - - case gc.TFLOAT64<<16 | gc.TINT32: - a = x86.ACVTTSD2SL - goto rdst - - // convert via int32 memory - /* - * integer to float - */ - case gc.TINT8<<16 | gc.TFLOAT32, - gc.TINT8<<16 | gc.TFLOAT64, - gc.TINT16<<16 | gc.TFLOAT32, - gc.TINT16<<16 | gc.TFLOAT64, - gc.TUINT16<<16 | gc.TFLOAT32, - gc.TUINT16<<16 | gc.TFLOAT64, - gc.TUINT8<<16 | gc.TFLOAT32, - gc.TUINT8<<16 | gc.TFLOAT64: - cvt = gc.Types[gc.TINT32] - - goto hard - - // convert via int64 memory - case gc.TUINT32<<16 | gc.TFLOAT32, - gc.TUINT32<<16 | gc.TFLOAT64: - cvt = gc.Types[gc.TINT64] - - goto hardmem - - case gc.TINT32<<16 | gc.TFLOAT32: - a = x86.ACVTSL2SS - goto rdst - - case gc.TINT32<<16 | gc.TFLOAT64: - a = x86.ACVTSL2SD - goto rdst - - /* - * float to float - */ - case gc.TFLOAT32<<16 | gc.TFLOAT32: - a = x86.AMOVSS - - case gc.TFLOAT64<<16 | gc.TFLOAT64: - a = x86.AMOVSD - - case gc.TFLOAT32<<16 | gc.TFLOAT64: - a = x86.ACVTSS2SD - goto rdst - - case gc.TFLOAT64<<16 | gc.TFLOAT32: - a = x86.ACVTSD2SS - goto rdst - } - - gins(a, f, t) - return - - // requires register intermediate -hard: - gc.Regalloc(&r1, cvt, t) - - gmove(f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - - // requires memory intermediate -hardmem: - gc.Tempname(&r1, cvt) - - gmove(f, &r1) - gmove(&r1, t) - return - - // requires register destination -rdst: - gc.Regalloc(&r1, t.Type, t) - - gins(a, f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return -} - -func samaddr(f *gc.Node, t *gc.Node) bool { - if f.Op != t.Op { - return false - } - - switch f.Op { - case gc.OREGISTER: - if f.Reg != t.Reg { - break - } - return true - } - - return false -} - -/* - * generate one instruction: - * as f, t - */ -func gins(as int, f *gc.Node, t *gc.Node) *obj.Prog { - if as == x86.AFMOVF && f != nil && f.Op == gc.OREGISTER && t != nil && t.Op == gc.OREGISTER { - gc.Fatal("gins MOVF reg, reg") - } - if as == x86.ACVTSD2SS && f != nil && f.Op == gc.OLITERAL { - gc.Fatal("gins CVTSD2SS const") - } - if as == x86.AMOVSD && t != nil && t.Op == gc.OREGISTER && t.Reg == x86.REG_F0 { - gc.Fatal("gins MOVSD into F0") - } - - if as == x86.AMOVL && f != nil && f.Op == gc.OADDR && f.Left.Op == gc.ONAME && f.Left.Class != gc.PEXTERN && f.Left.Class != gc.PFUNC { - // Turn MOVL $xxx(FP/SP) into LEAL xxx. - // These should be equivalent but most of the backend - // only expects to see LEAL, because that's what we had - // historically generated. Various hidden assumptions are baked in by now. - as = x86.ALEAL - f = f.Left - } - - switch as { - case x86.AMOVB, - x86.AMOVW, - x86.AMOVL: - if f != nil && t != nil && samaddr(f, t) { - return nil - } - - case x86.ALEAL: - if f != nil && gc.Isconst(f, gc.CTNIL) { - gc.Fatal("gins LEAL nil %v", f.Type) - } - } - - p := gc.Prog(as) - gc.Naddr(&p.From, f) - gc.Naddr(&p.To, t) - - if gc.Debug['g'] != 0 { - fmt.Printf("%v\n", p) - } - - w := 0 - switch as { - case x86.AMOVB: - w = 1 - - case x86.AMOVW: - w = 2 - - case x86.AMOVL: - w = 4 - } - - if true && w != 0 && f != nil && (p.From.Width > int64(w) || p.To.Width > int64(w)) { - gc.Dump("bad width from:", f) - gc.Dump("bad width to:", t) - gc.Fatal("bad width: %v (%d, %d)\n", p, p.From.Width, p.To.Width) - } - - if p.To.Type == obj.TYPE_ADDR && w > 0 { - gc.Fatal("bad use of addr: %v", p) - } - - return p -} - -func ginsnop() { - var reg gc.Node - gc.Nodreg(®, gc.Types[gc.TINT], x86.REG_AX) - gins(x86.AXCHGL, ®, ®) -} - -func dotaddable(n *gc.Node, n1 *gc.Node) bool { - if n.Op != gc.ODOT { - return false - } - - var oary [10]int64 - var nn *gc.Node - o := gc.Dotoffset(n, oary[:], &nn) - if nn != nil && nn.Addable && o == 1 && oary[0] >= 0 { - *n1 = *nn - n1.Type = n.Type - n1.Xoffset += oary[0] - return true - } - - return false -} - -func sudoclean() { -} - -func sudoaddable(as int, n *gc.Node, a *obj.Addr) bool { - *a = obj.Addr{} - return false -} diff --git a/src/cmd/8g/peep.go b/src/cmd/8g/peep.go deleted file mode 100644 index e309aea785..0000000000 --- a/src/cmd/8g/peep.go +++ /dev/null @@ -1,814 +0,0 @@ -// Derived from Inferno utils/6c/peep.c -// http://code.google.com/p/inferno-os/source/browse/utils/6c/peep.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package main - -import ( - "cmd/internal/gc" - "cmd/internal/obj" - "cmd/internal/obj/x86" - "fmt" -) - -const ( - REGEXT = 0 - exregoffset = x86.REG_DI -) - -var gactive uint32 - -// do we need the carry bit -func needc(p *obj.Prog) bool { - for p != nil { - if p.Info.Flags&gc.UseCarry != 0 { - return true - } - if p.Info.Flags&(gc.SetCarry|gc.KillCarry) != 0 { - return false - } - p = p.Link - } - - return false -} - -func rnops(r *gc.Flow) *gc.Flow { - if r != nil { - var p *obj.Prog - var r1 *gc.Flow - for { - p = r.Prog - if p.As != obj.ANOP || p.From.Type != obj.TYPE_NONE || p.To.Type != obj.TYPE_NONE { - break - } - r1 = gc.Uniqs(r) - if r1 == nil { - break - } - r = r1 - } - } - - return r -} - -func peep(firstp *obj.Prog) { - g := gc.Flowstart(firstp, nil) - if g == nil { - return - } - gactive = 0 - - // byte, word arithmetic elimination. - elimshortmov(g) - - // constant propagation - // find MOV $con,R followed by - // another MOV $con,R without - // setting R in the interim - var p *obj.Prog - for r := g.Start; r != nil; r = r.Link { - p = r.Prog - switch p.As { - case x86.ALEAL: - if regtyp(&p.To) { - if p.From.Sym != nil { - if p.From.Index == x86.REG_NONE { - conprop(r) - } - } - } - - case x86.AMOVB, - x86.AMOVW, - x86.AMOVL, - x86.AMOVSS, - x86.AMOVSD: - if regtyp(&p.To) { - if p.From.Type == obj.TYPE_CONST || p.From.Type == obj.TYPE_FCONST { - conprop(r) - } - } - } - } - - var r1 *gc.Flow - var p1 *obj.Prog - var r *gc.Flow - var t int -loop1: - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - gc.Dumpit("loop1", g.Start, 0) - } - - t = 0 - for r = g.Start; r != nil; r = r.Link { - p = r.Prog - switch p.As { - case x86.AMOVL, - x86.AMOVSS, - x86.AMOVSD: - if regtyp(&p.To) { - if regtyp(&p.From) { - if copyprop(g, r) { - excise(r) - t++ - } else if subprop(r) && copyprop(g, r) { - excise(r) - t++ - } - } - } - - case x86.AMOVBLZX, - x86.AMOVWLZX, - x86.AMOVBLSX, - x86.AMOVWLSX: - if regtyp(&p.To) { - r1 = rnops(gc.Uniqs(r)) - if r1 != nil { - p1 = r1.Prog - if p.As == p1.As && p.To.Type == p1.From.Type && p.To.Reg == p1.From.Reg { - p1.As = x86.AMOVL - t++ - } - } - } - - case x86.AADDL, - x86.AADDW: - if p.From.Type != obj.TYPE_CONST || needc(p.Link) { - break - } - if p.From.Offset == -1 { - if p.As == x86.AADDL { - p.As = x86.ADECL - } else { - p.As = x86.ADECW - } - p.From = obj.Addr{} - break - } - - if p.From.Offset == 1 { - if p.As == x86.AADDL { - p.As = x86.AINCL - } else { - p.As = x86.AINCW - } - p.From = obj.Addr{} - break - } - - case x86.ASUBL, - x86.ASUBW: - if p.From.Type != obj.TYPE_CONST || needc(p.Link) { - break - } - if p.From.Offset == -1 { - if p.As == x86.ASUBL { - p.As = x86.AINCL - } else { - p.As = x86.AINCW - } - p.From = obj.Addr{} - break - } - - if p.From.Offset == 1 { - if p.As == x86.ASUBL { - p.As = x86.ADECL - } else { - p.As = x86.ADECW - } - p.From = obj.Addr{} - break - } - } - } - - if t != 0 { - goto loop1 - } - - // MOVSD removal. - // We never use packed registers, so a MOVSD between registers - // can be replaced by MOVAPD, which moves the pair of float64s - // instead of just the lower one. We only use the lower one, but - // the processor can do better if we do moves using both. - for r := g.Start; r != nil; r = r.Link { - p = r.Prog - if p.As == x86.AMOVSD { - if regtyp(&p.From) { - if regtyp(&p.To) { - p.As = x86.AMOVAPD - } - } - } - } - - gc.Flowend(g) -} - -func excise(r *gc.Flow) { - p := r.Prog - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("%v ===delete===\n", p) - } - - obj.Nopout(p) - - gc.Ostats.Ndelmov++ -} - -func regtyp(a *obj.Addr) bool { - return a.Type == obj.TYPE_REG && (x86.REG_AX <= a.Reg && a.Reg <= x86.REG_DI || x86.REG_X0 <= a.Reg && a.Reg <= x86.REG_X7) -} - -// movb elimination. -// movb is simulated by the linker -// when a register other than ax, bx, cx, dx -// is used, so rewrite to other instructions -// when possible. a movb into a register -// can smash the entire 64-bit register without -// causing any trouble. -func elimshortmov(g *gc.Graph) { - var p *obj.Prog - - for r := g.Start; r != nil; r = r.Link { - p = r.Prog - if regtyp(&p.To) { - switch p.As { - case x86.AINCB, - x86.AINCW: - p.As = x86.AINCL - - case x86.ADECB, - x86.ADECW: - p.As = x86.ADECL - - case x86.ANEGB, - x86.ANEGW: - p.As = x86.ANEGL - - case x86.ANOTB, - x86.ANOTW: - p.As = x86.ANOTL - } - - if regtyp(&p.From) || p.From.Type == obj.TYPE_CONST { - // move or artihmetic into partial register. - // from another register or constant can be movl. - // we don't switch to 32-bit arithmetic if it can - // change how the carry bit is set (and the carry bit is needed). - switch p.As { - case x86.AMOVB, - x86.AMOVW: - p.As = x86.AMOVL - - case x86.AADDB, - x86.AADDW: - if !needc(p.Link) { - p.As = x86.AADDL - } - - case x86.ASUBB, - x86.ASUBW: - if !needc(p.Link) { - p.As = x86.ASUBL - } - - case x86.AMULB, - x86.AMULW: - p.As = x86.AMULL - - case x86.AIMULB, - x86.AIMULW: - p.As = x86.AIMULL - - case x86.AANDB, - x86.AANDW: - p.As = x86.AANDL - - case x86.AORB, - x86.AORW: - p.As = x86.AORL - - case x86.AXORB, - x86.AXORW: - p.As = x86.AXORL - - case x86.ASHLB, - x86.ASHLW: - p.As = x86.ASHLL - } - } else { - // explicit zero extension - switch p.As { - case x86.AMOVB: - p.As = x86.AMOVBLZX - - case x86.AMOVW: - p.As = x86.AMOVWLZX - } - } - } - } -} - -/* - * the idea is to substitute - * one register for another - * from one MOV to another - * MOV a, R0 - * ADD b, R0 / no use of R1 - * MOV R0, R1 - * would be converted to - * MOV a, R1 - * ADD b, R1 - * MOV R1, R0 - * hopefully, then the former or latter MOV - * will be eliminated by copy propagation. - */ -func subprop(r0 *gc.Flow) bool { - p := r0.Prog - v1 := &p.From - if !regtyp(v1) { - return false - } - v2 := &p.To - if !regtyp(v2) { - return false - } - for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("\t? %v\n", r.Prog) - } - if gc.Uniqs(r) == nil { - break - } - p = r.Prog - if p.As == obj.AVARDEF || p.As == obj.AVARKILL { - continue - } - if p.Info.Flags&gc.Call != 0 { - return false - } - - if p.Info.Reguse|p.Info.Regset != 0 { - return false - } - - if (p.Info.Flags&gc.Move != 0) && (p.Info.Flags&(gc.SizeL|gc.SizeQ|gc.SizeF|gc.SizeD) != 0) && p.To.Type == v1.Type && p.To.Reg == v1.Reg { - copysub(&p.To, v1, v2, 1) - if gc.Debug['P'] != 0 { - fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog) - if p.From.Type == v2.Type && p.From.Reg == v2.Reg { - fmt.Printf(" excise") - } - fmt.Printf("\n") - } - - for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { - p = r.Prog - copysub(&p.From, v1, v2, 1) - copysub(&p.To, v1, v2, 1) - if gc.Debug['P'] != 0 { - fmt.Printf("%v\n", r.Prog) - } - } - - t := int(v1.Reg) - v1.Reg = v2.Reg - v2.Reg = int16(t) - if gc.Debug['P'] != 0 { - fmt.Printf("%v last\n", r.Prog) - } - return true - } - - if copyau(&p.From, v2) || copyau(&p.To, v2) { - break - } - if copysub(&p.From, v1, v2, 0) != 0 || copysub(&p.To, v1, v2, 0) != 0 { - break - } - } - - return false -} - -/* - * The idea is to remove redundant copies. - * v1->v2 F=0 - * (use v2 s/v2/v1/)* - * set v1 F=1 - * use v2 return fail - * ----------------- - * v1->v2 F=0 - * (use v2 s/v2/v1/)* - * set v1 F=1 - * set v2 return success - */ -func copyprop(g *gc.Graph, r0 *gc.Flow) bool { - p := r0.Prog - v1 := &p.From - v2 := &p.To - if copyas(v1, v2) { - return true - } - gactive++ - return copy1(v1, v2, r0.S1, 0) -} - -func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f int) bool { - if uint32(r.Active) == gactive { - if gc.Debug['P'] != 0 { - fmt.Printf("act set; return 1\n") - } - return true - } - - r.Active = int32(gactive) - if gc.Debug['P'] != 0 { - fmt.Printf("copy %v->%v f=%d\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), f) - } - var t int - var p *obj.Prog - for ; r != nil; r = r.S1 { - p = r.Prog - if gc.Debug['P'] != 0 { - fmt.Printf("%v", p) - } - if f == 0 && gc.Uniqp(r) == nil { - f = 1 - if gc.Debug['P'] != 0 { - fmt.Printf("; merge; f=%d", f) - } - } - - t = copyu(p, v2, nil) - switch t { - case 2: /* rar, can't split */ - if gc.Debug['P'] != 0 { - fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2)) - } - return false - - case 3: /* set */ - if gc.Debug['P'] != 0 { - fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2)) - } - return true - - case 1, /* used, substitute */ - 4: /* use and set */ - if f != 0 { - if gc.Debug['P'] == 0 { - return false - } - if t == 4 { - fmt.Printf("; %v used+set and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f) - } else { - fmt.Printf("; %v used and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f) - } - return false - } - - if copyu(p, v2, v1) != 0 { - if gc.Debug['P'] != 0 { - fmt.Printf("; sub fail; return 0\n") - } - return false - } - - if gc.Debug['P'] != 0 { - fmt.Printf("; sub %v/%v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1)) - } - if t == 4 { - if gc.Debug['P'] != 0 { - fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2)) - } - return true - } - } - - if f == 0 { - t = copyu(p, v1, nil) - if f == 0 && (t == 2 || t == 3 || t == 4) { - f = 1 - if gc.Debug['P'] != 0 { - fmt.Printf("; %v set and !f; f=%d", gc.Ctxt.Dconv(v1), f) - } - } - } - - if gc.Debug['P'] != 0 { - fmt.Printf("\n") - } - if r.S2 != nil { - if !copy1(v1, v2, r.S2, f) { - return false - } - } - } - - return true -} - -/* - * return - * 1 if v only used (and substitute), - * 2 if read-alter-rewrite - * 3 if set - * 4 if set and used - * 0 otherwise (not touched) - */ -func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int { - switch p.As { - case obj.AJMP: - if s != nil { - if copysub(&p.To, v, s, 1) != 0 { - return 1 - } - return 0 - } - - if copyau(&p.To, v) { - return 1 - } - return 0 - - case obj.ARET: - if s != nil { - return 1 - } - return 3 - - case obj.ACALL: - if REGEXT != 0 /*TypeKind(100016)*/ && v.Type == obj.TYPE_REG && v.Reg <= REGEXT && v.Reg > exregoffset { - return 2 - } - if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG { - return 2 - } - if v.Type == p.From.Type && v.Reg == p.From.Reg { - return 2 - } - - if s != nil { - if copysub(&p.To, v, s, 1) != 0 { - return 1 - } - return 0 - } - - if copyau(&p.To, v) { - return 4 - } - return 3 - - case obj.ATEXT: - if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG { - return 3 - } - return 0 - } - - if p.As == obj.AVARDEF || p.As == obj.AVARKILL { - return 0 - } - - if (p.Info.Reguse|p.Info.Regset)&RtoB(int(v.Reg)) != 0 { - return 2 - } - - if p.Info.Flags&gc.LeftAddr != 0 { - if copyas(&p.From, v) { - return 2 - } - } - - if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightRead|gc.RightWrite { - if copyas(&p.To, v) { - return 2 - } - } - - if p.Info.Flags&gc.RightWrite != 0 { - if copyas(&p.To, v) { - if s != nil { - return copysub(&p.From, v, s, 1) - } - if copyau(&p.From, v) { - return 4 - } - return 3 - } - } - - if p.Info.Flags&(gc.LeftAddr|gc.LeftRead|gc.LeftWrite|gc.RightAddr|gc.RightRead|gc.RightWrite) != 0 { - if s != nil { - if copysub(&p.From, v, s, 1) != 0 { - return 1 - } - return copysub(&p.To, v, s, 1) - } - - if copyau(&p.From, v) { - return 1 - } - if copyau(&p.To, v) { - return 1 - } - } - - return 0 -} - -/* - * direct reference, - * could be set/use depending on - * semantics - */ -func copyas(a *obj.Addr, v *obj.Addr) bool { - if x86.REG_AL <= a.Reg && a.Reg <= x86.REG_BL { - gc.Fatal("use of byte register") - } - if x86.REG_AL <= v.Reg && v.Reg <= x86.REG_BL { - gc.Fatal("use of byte register") - } - - if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg { - return false - } - if regtyp(v) { - return true - } - if (v.Type == obj.TYPE_MEM || v.Type == obj.TYPE_ADDR) && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) { - if v.Offset == a.Offset { - return true - } - } - return false -} - -func sameaddr(a *obj.Addr, v *obj.Addr) bool { - if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg { - return false - } - if regtyp(v) { - return true - } - if (v.Type == obj.TYPE_MEM || v.Type == obj.TYPE_ADDR) && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) { - if v.Offset == a.Offset { - return true - } - } - return false -} - -/* - * either direct or indirect - */ -func copyau(a *obj.Addr, v *obj.Addr) bool { - if copyas(a, v) { - return true - } - if regtyp(v) { - if (a.Type == obj.TYPE_MEM || a.Type == obj.TYPE_ADDR) && a.Reg == v.Reg { - return true - } - if a.Index == v.Reg { - return true - } - } - - return false -} - -/* - * substitute s for v in a - * return failure to substitute - */ -func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f int) int { - if copyas(a, v) { - reg := int(s.Reg) - if reg >= x86.REG_AX && reg <= x86.REG_DI || reg >= x86.REG_X0 && reg <= x86.REG_X7 { - if f != 0 { - a.Reg = int16(reg) - } - } - - return 0 - } - - if regtyp(v) { - reg := int(v.Reg) - if (a.Type == obj.TYPE_MEM || a.Type == obj.TYPE_ADDR) && int(a.Reg) == reg { - if (s.Reg == x86.REG_BP) && a.Index != obj.TYPE_NONE { - return 1 /* can't use BP-base with index */ - } - if f != 0 { - a.Reg = s.Reg - } - } - - // return 0; - if int(a.Index) == reg { - if f != 0 { - a.Index = s.Reg - } - return 0 - } - - return 0 - } - - return 0 -} - -func conprop(r0 *gc.Flow) { - var p *obj.Prog - var t int - - p0 := r0.Prog - v0 := &p0.To - r := r0 - -loop: - r = gc.Uniqs(r) - if r == nil || r == r0 { - return - } - if gc.Uniqp(r) == nil { - return - } - - p = r.Prog - t = copyu(p, v0, nil) - switch t { - case 0, // miss - 1: // use - goto loop - - case 2, // rar - 4: // use and set - break - - case 3: // set - if p.As == p0.As { - if p.From.Type == p0.From.Type { - if p.From.Reg == p0.From.Reg { - if p.From.Node == p0.From.Node { - if p.From.Offset == p0.From.Offset { - if p.From.Scale == p0.From.Scale { - if p.From.Type == obj.TYPE_FCONST && p.From.Val.(float64) == p0.From.Val.(float64) { - if p.From.Index == p0.From.Index { - excise(r) - goto loop - } - } - } - } - } - } - } - } - } -} - -func smallindir(a *obj.Addr, reg *obj.Addr) bool { - return regtyp(reg) && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && a.Index == x86.REG_NONE && 0 <= a.Offset && a.Offset < 4096 -} - -func stackaddr(a *obj.Addr) bool { - return a.Type == obj.TYPE_REG && a.Reg == x86.REG_SP -} diff --git a/src/cmd/8g/prog.go b/src/cmd/8g/prog.go deleted file mode 100644 index 1346c20f2b..0000000000 --- a/src/cmd/8g/prog.go +++ /dev/null @@ -1,292 +0,0 @@ -// Copyright 2013 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/gc" - "cmd/internal/obj" - "cmd/internal/obj/x86" -) - -var ( - AX = RtoB(x86.REG_AX) - BX = RtoB(x86.REG_BX) - CX = RtoB(x86.REG_CX) - DX = RtoB(x86.REG_DX) - DI = RtoB(x86.REG_DI) - SI = RtoB(x86.REG_SI) - LeftRdwr uint32 = gc.LeftRead | gc.LeftWrite - RightRdwr uint32 = gc.RightRead | gc.RightWrite -) - -// This table gives the basic information about instruction -// generated by the compiler and processed in the optimizer. -// See opt.h for bit definitions. -// -// Instructions not generated need not be listed. -// As an exception to that rule, we typically write down all the -// size variants of an operation even if we just use a subset. -// -// The table is formatted for 8-space tabs. -var progtable = [x86.ALAST]obj.ProgInfo{ - obj.ATYPE: {gc.Pseudo | gc.Skip, 0, 0, 0}, - obj.ATEXT: {gc.Pseudo, 0, 0, 0}, - obj.AFUNCDATA: {gc.Pseudo, 0, 0, 0}, - obj.APCDATA: {gc.Pseudo, 0, 0, 0}, - obj.AUNDEF: {gc.Break, 0, 0, 0}, - obj.AUSEFIELD: {gc.OK, 0, 0, 0}, - obj.ACHECKNIL: {gc.LeftRead, 0, 0, 0}, - obj.AVARDEF: {gc.Pseudo | gc.RightWrite, 0, 0, 0}, - obj.AVARKILL: {gc.Pseudo | gc.RightWrite, 0, 0, 0}, - - // NOP is an internal no-op that also stands - // for USED and SET annotations, not the Intel opcode. - obj.ANOP: {gc.LeftRead | gc.RightWrite, 0, 0, 0}, - x86.AADCL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry | gc.UseCarry, 0, 0, 0}, - x86.AADCW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry | gc.UseCarry, 0, 0, 0}, - x86.AADDB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.AADDL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.AADDW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.AADDSD: {gc.SizeD | gc.LeftRead | RightRdwr, 0, 0, 0}, - x86.AADDSS: {gc.SizeF | gc.LeftRead | RightRdwr, 0, 0, 0}, - x86.AANDB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.AANDL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.AANDW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - obj.ACALL: {gc.RightAddr | gc.Call | gc.KillCarry, 0, 0, 0}, - x86.ACDQ: {gc.OK, AX, AX | DX, 0}, - x86.ACWD: {gc.OK, AX, AX | DX, 0}, - x86.ACLD: {gc.OK, 0, 0, 0}, - x86.ASTD: {gc.OK, 0, 0, 0}, - x86.ACMPB: {gc.SizeB | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, - x86.ACMPL: {gc.SizeL | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, - x86.ACMPW: {gc.SizeW | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, - x86.ACOMISD: {gc.SizeD | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, - x86.ACOMISS: {gc.SizeF | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, - x86.ACVTSD2SL: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.ACVTSD2SS: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.ACVTSL2SD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.ACVTSL2SS: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.ACVTSS2SD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.ACVTSS2SL: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.ACVTTSD2SL: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.ACVTTSS2SL: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.ADECB: {gc.SizeB | RightRdwr, 0, 0, 0}, - x86.ADECL: {gc.SizeL | RightRdwr, 0, 0, 0}, - x86.ADECW: {gc.SizeW | RightRdwr, 0, 0, 0}, - x86.ADIVB: {gc.SizeB | gc.LeftRead | gc.SetCarry, AX, AX, 0}, - x86.ADIVL: {gc.SizeL | gc.LeftRead | gc.SetCarry, AX | DX, AX | DX, 0}, - x86.ADIVW: {gc.SizeW | gc.LeftRead | gc.SetCarry, AX | DX, AX | DX, 0}, - x86.ADIVSD: {gc.SizeD | gc.LeftRead | RightRdwr, 0, 0, 0}, - x86.ADIVSS: {gc.SizeF | gc.LeftRead | RightRdwr, 0, 0, 0}, - x86.AFLDCW: {gc.SizeW | gc.LeftAddr, 0, 0, 0}, - x86.AFSTCW: {gc.SizeW | gc.RightAddr, 0, 0, 0}, - x86.AFSTSW: {gc.SizeW | gc.RightAddr | gc.RightWrite, 0, 0, 0}, - x86.AFADDD: {gc.SizeD | gc.LeftAddr | RightRdwr, 0, 0, 0}, - x86.AFADDDP: {gc.SizeD | gc.LeftAddr | RightRdwr, 0, 0, 0}, - x86.AFADDF: {gc.SizeF | gc.LeftAddr | RightRdwr, 0, 0, 0}, - x86.AFCOMD: {gc.SizeD | gc.LeftAddr | gc.RightRead, 0, 0, 0}, - x86.AFCOMDP: {gc.SizeD | gc.LeftAddr | gc.RightRead, 0, 0, 0}, - x86.AFCOMDPP: {gc.SizeD | gc.LeftAddr | gc.RightRead, 0, 0, 0}, - x86.AFCOMF: {gc.SizeF | gc.LeftAddr | gc.RightRead, 0, 0, 0}, - x86.AFCOMFP: {gc.SizeF | gc.LeftAddr | gc.RightRead, 0, 0, 0}, - x86.AFUCOMIP: {gc.SizeF | gc.LeftAddr | gc.RightRead, 0, 0, 0}, - x86.AFCHS: {gc.SizeD | RightRdwr, 0, 0, 0}, // also SizeF - - x86.AFDIVDP: {gc.SizeD | gc.LeftAddr | RightRdwr, 0, 0, 0}, - x86.AFDIVF: {gc.SizeF | gc.LeftAddr | RightRdwr, 0, 0, 0}, - x86.AFDIVD: {gc.SizeD | gc.LeftAddr | RightRdwr, 0, 0, 0}, - x86.AFDIVRDP: {gc.SizeD | gc.LeftAddr | RightRdwr, 0, 0, 0}, - x86.AFDIVRF: {gc.SizeF | gc.LeftAddr | RightRdwr, 0, 0, 0}, - x86.AFDIVRD: {gc.SizeD | gc.LeftAddr | RightRdwr, 0, 0, 0}, - x86.AFXCHD: {gc.SizeD | LeftRdwr | RightRdwr, 0, 0, 0}, - x86.AFSUBD: {gc.SizeD | gc.LeftAddr | RightRdwr, 0, 0, 0}, - x86.AFSUBDP: {gc.SizeD | gc.LeftAddr | RightRdwr, 0, 0, 0}, - x86.AFSUBF: {gc.SizeF | gc.LeftAddr | RightRdwr, 0, 0, 0}, - x86.AFSUBRD: {gc.SizeD | gc.LeftAddr | RightRdwr, 0, 0, 0}, - x86.AFSUBRDP: {gc.SizeD | gc.LeftAddr | RightRdwr, 0, 0, 0}, - x86.AFSUBRF: {gc.SizeF | gc.LeftAddr | RightRdwr, 0, 0, 0}, - x86.AFMOVD: {gc.SizeD | gc.LeftAddr | gc.RightWrite, 0, 0, 0}, - x86.AFMOVF: {gc.SizeF | gc.LeftAddr | gc.RightWrite, 0, 0, 0}, - x86.AFMOVL: {gc.SizeL | gc.LeftAddr | gc.RightWrite, 0, 0, 0}, - x86.AFMOVW: {gc.SizeW | gc.LeftAddr | gc.RightWrite, 0, 0, 0}, - x86.AFMOVV: {gc.SizeQ | gc.LeftAddr | gc.RightWrite, 0, 0, 0}, - - // These instructions are marked as RightAddr - // so that the register optimizer does not try to replace the - // memory references with integer register references. - // But they do not use the previous value at the address, so - // we also mark them RightWrite. - x86.AFMOVDP: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.RightAddr, 0, 0, 0}, - x86.AFMOVFP: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.RightAddr, 0, 0, 0}, - x86.AFMOVLP: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.RightAddr, 0, 0, 0}, - x86.AFMOVWP: {gc.SizeW | gc.LeftRead | gc.RightWrite | gc.RightAddr, 0, 0, 0}, - x86.AFMOVVP: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.RightAddr, 0, 0, 0}, - x86.AFMULD: {gc.SizeD | gc.LeftAddr | RightRdwr, 0, 0, 0}, - x86.AFMULDP: {gc.SizeD | gc.LeftAddr | RightRdwr, 0, 0, 0}, - x86.AFMULF: {gc.SizeF | gc.LeftAddr | RightRdwr, 0, 0, 0}, - x86.AIDIVB: {gc.SizeB | gc.LeftRead | gc.SetCarry, AX, AX, 0}, - x86.AIDIVL: {gc.SizeL | gc.LeftRead | gc.SetCarry, AX | DX, AX | DX, 0}, - x86.AIDIVW: {gc.SizeW | gc.LeftRead | gc.SetCarry, AX | DX, AX | DX, 0}, - x86.AIMULB: {gc.SizeB | gc.LeftRead | gc.SetCarry, AX, AX, 0}, - x86.AIMULL: {gc.SizeL | gc.LeftRead | gc.ImulAXDX | gc.SetCarry, 0, 0, 0}, - x86.AIMULW: {gc.SizeW | gc.LeftRead | gc.ImulAXDX | gc.SetCarry, 0, 0, 0}, - x86.AINCB: {gc.SizeB | RightRdwr, 0, 0, 0}, - x86.AINCL: {gc.SizeL | RightRdwr, 0, 0, 0}, - x86.AINCW: {gc.SizeW | RightRdwr, 0, 0, 0}, - x86.AJCC: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - x86.AJCS: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - x86.AJEQ: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - x86.AJGE: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - x86.AJGT: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - x86.AJHI: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - x86.AJLE: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - x86.AJLS: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - x86.AJLT: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - x86.AJMI: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - x86.AJNE: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - x86.AJOC: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - x86.AJOS: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - x86.AJPC: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - x86.AJPL: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - x86.AJPS: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, - obj.AJMP: {gc.Jump | gc.Break | gc.KillCarry, 0, 0, 0}, - x86.ALEAL: {gc.LeftAddr | gc.RightWrite, 0, 0, 0}, - x86.AMOVBLSX: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.AMOVBLZX: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.AMOVBWSX: {gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.AMOVBWZX: {gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.AMOVWLSX: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.AMOVWLZX: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - x86.AMOVB: {gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, - x86.AMOVL: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, - x86.AMOVW: {gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, - x86.AMOVSB: {gc.OK, DI | SI, DI | SI, 0}, - x86.AMOVSL: {gc.OK, DI | SI, DI | SI, 0}, - x86.AMOVSW: {gc.OK, DI | SI, DI | SI, 0}, - obj.ADUFFCOPY: {gc.OK, DI | SI, DI | SI | CX, 0}, - x86.AMOVSD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, - x86.AMOVSS: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, - - // We use MOVAPD as a faster synonym for MOVSD. - x86.AMOVAPD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, - x86.AMULB: {gc.SizeB | gc.LeftRead | gc.SetCarry, AX, AX, 0}, - x86.AMULL: {gc.SizeL | gc.LeftRead | gc.SetCarry, AX, AX | DX, 0}, - x86.AMULW: {gc.SizeW | gc.LeftRead | gc.SetCarry, AX, AX | DX, 0}, - x86.AMULSD: {gc.SizeD | gc.LeftRead | RightRdwr, 0, 0, 0}, - x86.AMULSS: {gc.SizeF | gc.LeftRead | RightRdwr, 0, 0, 0}, - x86.ANEGB: {gc.SizeB | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.ANEGL: {gc.SizeL | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.ANEGW: {gc.SizeW | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.ANOTB: {gc.SizeB | RightRdwr, 0, 0, 0}, - x86.ANOTL: {gc.SizeL | RightRdwr, 0, 0, 0}, - x86.ANOTW: {gc.SizeW | RightRdwr, 0, 0, 0}, - x86.AORB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.AORL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.AORW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.APOPL: {gc.SizeL | gc.RightWrite, 0, 0, 0}, - x86.APUSHL: {gc.SizeL | gc.LeftRead, 0, 0, 0}, - x86.ARCLB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry, 0, 0, 0}, - x86.ARCLL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry, 0, 0, 0}, - x86.ARCLW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry, 0, 0, 0}, - x86.ARCRB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry, 0, 0, 0}, - x86.ARCRL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry, 0, 0, 0}, - x86.ARCRW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry, 0, 0, 0}, - x86.AREP: {gc.OK, CX, CX, 0}, - x86.AREPN: {gc.OK, CX, CX, 0}, - obj.ARET: {gc.Break | gc.KillCarry, 0, 0, 0}, - x86.AROLB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.AROLL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.AROLW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ARORB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ARORL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ARORW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ASAHF: {gc.OK, AX, AX, 0}, - x86.ASALB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ASALL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ASALW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ASARB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ASARL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ASARW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ASBBB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.SetCarry | gc.UseCarry, 0, 0, 0}, - x86.ASBBL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry | gc.UseCarry, 0, 0, 0}, - x86.ASBBW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry | gc.UseCarry, 0, 0, 0}, - x86.ASETCC: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, - x86.ASETCS: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, - x86.ASETEQ: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, - x86.ASETGE: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, - x86.ASETGT: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, - x86.ASETHI: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, - x86.ASETLE: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, - x86.ASETLS: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, - x86.ASETLT: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, - x86.ASETMI: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, - x86.ASETNE: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, - x86.ASETOC: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, - x86.ASETOS: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, - x86.ASETPC: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, - x86.ASETPL: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, - x86.ASETPS: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, - x86.ASHLB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ASHLL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ASHLW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ASHRB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ASHRL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ASHRW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, - x86.ASTOSB: {gc.OK, AX | DI, DI, 0}, - x86.ASTOSL: {gc.OK, AX | DI, DI, 0}, - x86.ASTOSW: {gc.OK, AX | DI, DI, 0}, - obj.ADUFFZERO: {gc.OK, AX | DI, DI, 0}, - x86.ASUBB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.ASUBL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.ASUBW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.ASUBSD: {gc.SizeD | gc.LeftRead | RightRdwr, 0, 0, 0}, - x86.ASUBSS: {gc.SizeF | gc.LeftRead | RightRdwr, 0, 0, 0}, - x86.ATESTB: {gc.SizeB | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, - x86.ATESTL: {gc.SizeL | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, - x86.ATESTW: {gc.SizeW | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, - x86.AUCOMISD: {gc.SizeD | gc.LeftRead | gc.RightRead, 0, 0, 0}, - x86.AUCOMISS: {gc.SizeF | gc.LeftRead | gc.RightRead, 0, 0, 0}, - x86.AXCHGB: {gc.SizeB | LeftRdwr | RightRdwr, 0, 0, 0}, - x86.AXCHGL: {gc.SizeL | LeftRdwr | RightRdwr, 0, 0, 0}, - x86.AXCHGW: {gc.SizeW | LeftRdwr | RightRdwr, 0, 0, 0}, - x86.AXORB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.AXORL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, - x86.AXORW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, -} - -func proginfo(p *obj.Prog) { - info := &p.Info - *info = progtable[p.As] - if info.Flags == 0 { - gc.Fatal("unknown instruction %v", p) - } - - if (info.Flags&gc.ShiftCX != 0) && p.From.Type != obj.TYPE_CONST { - info.Reguse |= CX - } - - if info.Flags&gc.ImulAXDX != 0 { - if p.To.Type == obj.TYPE_NONE { - info.Reguse |= AX - info.Regset |= AX | DX - } else { - info.Flags |= RightRdwr - } - } - - // Addressing makes some registers used. - if p.From.Type == obj.TYPE_MEM && p.From.Name == obj.NAME_NONE { - info.Regindex |= RtoB(int(p.From.Reg)) - } - if p.From.Index != x86.REG_NONE { - info.Regindex |= RtoB(int(p.From.Index)) - } - if p.To.Type == obj.TYPE_MEM && p.To.Name == obj.NAME_NONE { - info.Regindex |= RtoB(int(p.To.Reg)) - } - if p.To.Index != x86.REG_NONE { - info.Regindex |= RtoB(int(p.To.Index)) - } -} diff --git a/src/cmd/8g/reg.go b/src/cmd/8g/reg.go deleted file mode 100644 index 50b5b97ab1..0000000000 --- a/src/cmd/8g/reg.go +++ /dev/null @@ -1,112 +0,0 @@ -// Derived from Inferno utils/6c/reg.c -// http://code.google.com/p/inferno-os/source/browse/utils/6c/reg.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package main - -import "cmd/internal/obj/x86" -import "cmd/internal/gc" - -const ( - NREGVAR = 16 /* 8 integer + 8 floating */ -) - -var reg [x86.MAXREG]uint8 - -var regname = []string{ - ".ax", - ".cx", - ".dx", - ".bx", - ".sp", - ".bp", - ".si", - ".di", - ".x0", - ".x1", - ".x2", - ".x3", - ".x4", - ".x5", - ".x6", - ".x7", -} - -func regnames(n *int) []string { - *n = NREGVAR - return regname -} - -func excludedregs() uint64 { - return RtoB(x86.REG_SP) -} - -func doregbits(r int) uint64 { - b := uint64(0) - if r >= x86.REG_AX && r <= x86.REG_DI { - b |= RtoB(r) - } else if r >= x86.REG_AL && r <= x86.REG_BL { - b |= RtoB(r - x86.REG_AL + x86.REG_AX) - } else if r >= x86.REG_AH && r <= x86.REG_BH { - b |= RtoB(r - x86.REG_AH + x86.REG_AX) - } else if r >= x86.REG_X0 && r <= x86.REG_X0+7 { - b |= FtoB(r) - } - return b -} - -func RtoB(r int) uint64 { - if r < x86.REG_AX || r > x86.REG_DI { - return 0 - } - return 1 << uint(r-x86.REG_AX) -} - -func BtoR(b uint64) int { - b &= 0xff - if b == 0 { - return 0 - } - return gc.Bitno(b) + x86.REG_AX -} - -func FtoB(f int) uint64 { - if f < x86.REG_X0 || f > x86.REG_X7 { - return 0 - } - return 1 << uint(f-x86.REG_X0+8) -} - -func BtoF(b uint64) int { - b &= 0xFF00 - if b == 0 { - return 0 - } - return gc.Bitno(b) - 8 + x86.REG_X0 -} diff --git a/src/cmd/8l/asm.go b/src/cmd/8l/asm.go deleted file mode 100644 index a736d43686..0000000000 --- a/src/cmd/8l/asm.go +++ /dev/null @@ -1,643 +0,0 @@ -// Inferno utils/8l/asm.c -// http://code.google.com/p/inferno-os/source/browse/utils/8l/asm.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package main - -import ( - "cmd/internal/ld" - "cmd/internal/obj" - "fmt" - "log" -) - -func gentext() { -} - -func adddynrela(rela *ld.LSym, s *ld.LSym, r *ld.Reloc) { - log.Fatalf("adddynrela not implemented") -} - -func adddynrel(s *ld.LSym, r *ld.Reloc) { - targ := r.Sym - ld.Ctxt.Cursym = s - - switch r.Type { - default: - if r.Type >= 256 { - ld.Diag("unexpected relocation type %d", r.Type) - return - } - - // Handle relocations found in ELF object files. - case 256 + ld.R_386_PC32: - if targ.Type == obj.SDYNIMPORT { - ld.Diag("unexpected R_386_PC32 relocation for dynamic symbol %s", targ.Name) - } - if targ.Type == 0 || targ.Type == obj.SXREF { - ld.Diag("unknown symbol %s in pcrel", targ.Name) - } - r.Type = obj.R_PCREL - r.Add += 4 - return - - case 256 + ld.R_386_PLT32: - r.Type = obj.R_PCREL - r.Add += 4 - if targ.Type == obj.SDYNIMPORT { - addpltsym(ld.Ctxt, targ) - r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) - r.Add += int64(targ.Plt) - } - - return - - case 256 + ld.R_386_GOT32: - if targ.Type != obj.SDYNIMPORT { - // have symbol - if r.Off >= 2 && s.P[r.Off-2] == 0x8b { - // turn MOVL of GOT entry into LEAL of symbol address, relative to GOT. - s.P[r.Off-2] = 0x8d - - r.Type = obj.R_GOTOFF - return - } - - if r.Off >= 2 && s.P[r.Off-2] == 0xff && s.P[r.Off-1] == 0xb3 { - // turn PUSHL of GOT entry into PUSHL of symbol itself. - // use unnecessary SS prefix to keep instruction same length. - s.P[r.Off-2] = 0x36 - - s.P[r.Off-1] = 0x68 - r.Type = obj.R_ADDR - return - } - - ld.Diag("unexpected GOT reloc for non-dynamic symbol %s", targ.Name) - return - } - - addgotsym(ld.Ctxt, targ) - r.Type = obj.R_CONST // write r->add during relocsym - r.Sym = nil - r.Add += int64(targ.Got) - return - - case 256 + ld.R_386_GOTOFF: - r.Type = obj.R_GOTOFF - return - - case 256 + ld.R_386_GOTPC: - r.Type = obj.R_PCREL - r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0) - r.Add += 4 - return - - case 256 + ld.R_386_32: - if targ.Type == obj.SDYNIMPORT { - ld.Diag("unexpected R_386_32 relocation for dynamic symbol %s", targ.Name) - } - r.Type = obj.R_ADDR - return - - case 512 + ld.MACHO_GENERIC_RELOC_VANILLA*2 + 0: - r.Type = obj.R_ADDR - if targ.Type == obj.SDYNIMPORT { - ld.Diag("unexpected reloc for dynamic symbol %s", targ.Name) - } - return - - case 512 + ld.MACHO_GENERIC_RELOC_VANILLA*2 + 1: - if targ.Type == obj.SDYNIMPORT { - addpltsym(ld.Ctxt, targ) - r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) - r.Add = int64(targ.Plt) - r.Type = obj.R_PCREL - return - } - - r.Type = obj.R_PCREL - return - - case 512 + ld.MACHO_FAKE_GOTPCREL: - if targ.Type != obj.SDYNIMPORT { - // have symbol - // turn MOVL of GOT entry into LEAL of symbol itself - if r.Off < 2 || s.P[r.Off-2] != 0x8b { - ld.Diag("unexpected GOT reloc for non-dynamic symbol %s", targ.Name) - return - } - - s.P[r.Off-2] = 0x8d - r.Type = obj.R_PCREL - return - } - - addgotsym(ld.Ctxt, targ) - r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0) - r.Add += int64(targ.Got) - r.Type = obj.R_PCREL - return - } - - // Handle references to ELF symbols from our own object files. - if targ.Type != obj.SDYNIMPORT { - return - } - - switch r.Type { - case obj.R_CALL, - obj.R_PCREL: - addpltsym(ld.Ctxt, targ) - r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) - r.Add = int64(targ.Plt) - return - - case obj.R_ADDR: - if s.Type != obj.SDATA { - break - } - if ld.Iself { - 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)) - r.Type = obj.R_CONST // write r->add during relocsym - r.Sym = nil - return - } - - if ld.HEADTYPE == obj.Hdarwin && s.Size == PtrSize && r.Off == 0 { - // Mach-O relocations are a royal pain to lay out. - // They use a compact stateful bytecode representation - // that is too much bother to deal with. - // Instead, interpret the C declaration - // void *_Cvar_stderr = &stderr; - // as making _Cvar_stderr the name of a GOT entry - // for stderr. This is separate from the usual GOT entry, - // 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. - ld.Adddynsym(ld.Ctxt, targ) - - got := ld.Linklookup(ld.Ctxt, ".got", 0) - s.Type = got.Type | obj.SSUB - s.Outer = got - s.Sub = got.Sub - got.Sub = s - s.Value = got.Size - ld.Adduint32(ld.Ctxt, got, 0) - ld.Adduint32(ld.Ctxt, ld.Linklookup(ld.Ctxt, ".linkedit.got", 0), uint32(targ.Dynid)) - r.Type = 256 // ignore during relocsym - return - } - - if ld.HEADTYPE == obj.Hwindows && s.Size == PtrSize { - // nothing to do, the relocation will be laid out in pereloc1 - return - } - } - - ld.Ctxt.Cursym = s - ld.Diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ.Name, r.Type, targ.Type) -} - -func elfreloc1(r *ld.Reloc, sectoff int64) int { - ld.Thearch.Lput(uint32(sectoff)) - - elfsym := r.Xsym.Elfsym - switch r.Type { - default: - return -1 - - case obj.R_ADDR: - if r.Siz == 4 { - ld.Thearch.Lput(ld.R_386_32 | uint32(elfsym)<<8) - } else { - return -1 - } - - case obj.R_CALL, - obj.R_PCREL: - if r.Siz == 4 { - ld.Thearch.Lput(ld.R_386_PC32 | uint32(elfsym)<<8) - } else { - return -1 - } - - case obj.R_TLS_LE: - if r.Siz == 4 { - ld.Thearch.Lput(ld.R_386_TLS_LE | uint32(elfsym)<<8) - } else { - return -1 - } - } - - return 0 -} - -func machoreloc1(r *ld.Reloc, sectoff int64) int { - var v uint32 - - rs := r.Xsym - - if rs.Type == obj.SHOSTOBJ { - if rs.Dynid < 0 { - ld.Diag("reloc %d to non-macho symbol %s type=%d", r.Type, rs.Name, rs.Type) - return -1 - } - - v = uint32(rs.Dynid) - v |= 1 << 27 // external relocation - } else { - v = uint32((rs.Sect.(*ld.Section)).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) - return -1 - } - } - - switch r.Type { - default: - return -1 - - case obj.R_ADDR: - v |= ld.MACHO_GENERIC_RELOC_VANILLA << 28 - - case obj.R_CALL, - obj.R_PCREL: - v |= 1 << 24 // pc-relative bit - v |= ld.MACHO_GENERIC_RELOC_VANILLA << 28 - } - - switch r.Siz { - default: - return -1 - - case 1: - v |= 0 << 25 - - case 2: - v |= 1 << 25 - - case 4: - v |= 2 << 25 - - case 8: - v |= 3 << 25 - } - - ld.Thearch.Lput(uint32(sectoff)) - ld.Thearch.Lput(v) - return 0 -} - -func pereloc1(r *ld.Reloc, sectoff int64) bool { - var v uint32 - - rs := r.Xsym - - if rs.Dynid < 0 { - ld.Diag("reloc %d to non-coff symbol %s type=%d", r.Type, rs.Name, rs.Type) - return false - } - - ld.Thearch.Lput(uint32(sectoff)) - ld.Thearch.Lput(uint32(rs.Dynid)) - - switch r.Type { - default: - return false - - case obj.R_ADDR: - v = ld.IMAGE_REL_I386_DIR32 - - case obj.R_CALL, - obj.R_PCREL: - v = ld.IMAGE_REL_I386_REL32 - } - - ld.Thearch.Wput(uint16(v)) - - return true -} - -func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int { - if ld.Linkmode == ld.LinkExternal { - return -1 - } - switch r.Type { - case obj.R_CONST: - *val = r.Add - return 0 - - case obj.R_GOTOFF: - *val = ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ld.Linklookup(ld.Ctxt, ".got", 0)) - return 0 - } - - return -1 -} - -func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 { - log.Fatalf("unexpected relocation variant") - return t -} - -func elfsetupplt() { - plt := ld.Linklookup(ld.Ctxt, ".plt", 0) - got := ld.Linklookup(ld.Ctxt, ".got.plt", 0) - if plt.Size == 0 { - // pushl got+4 - ld.Adduint8(ld.Ctxt, plt, 0xff) - - ld.Adduint8(ld.Ctxt, plt, 0x35) - ld.Addaddrplus(ld.Ctxt, plt, got, 4) - - // jmp *got+8 - ld.Adduint8(ld.Ctxt, plt, 0xff) - - ld.Adduint8(ld.Ctxt, plt, 0x25) - ld.Addaddrplus(ld.Ctxt, plt, got, 8) - - // zero pad - ld.Adduint32(ld.Ctxt, plt, 0) - - // assume got->size == 0 too - ld.Addaddrplus(ld.Ctxt, got, ld.Linklookup(ld.Ctxt, ".dynamic", 0), 0) - - ld.Adduint32(ld.Ctxt, got, 0) - ld.Adduint32(ld.Ctxt, got, 0) - } -} - -func addpltsym(ctxt *ld.Link, s *ld.LSym) { - if s.Plt >= 0 { - return - } - - ld.Adddynsym(ctxt, s) - - if ld.Iself { - plt := ld.Linklookup(ctxt, ".plt", 0) - got := ld.Linklookup(ctxt, ".got.plt", 0) - rel := ld.Linklookup(ctxt, ".rel.plt", 0) - if plt.Size == 0 { - elfsetupplt() - } - - // jmpq *got+size - ld.Adduint8(ctxt, plt, 0xff) - - ld.Adduint8(ctxt, plt, 0x25) - ld.Addaddrplus(ctxt, plt, got, got.Size) - - // add to got: pointer to current pos in plt - ld.Addaddrplus(ctxt, got, plt, plt.Size) - - // pushl $x - ld.Adduint8(ctxt, plt, 0x68) - - ld.Adduint32(ctxt, plt, uint32(rel.Size)) - - // jmp .plt - ld.Adduint8(ctxt, plt, 0xe9) - - ld.Adduint32(ctxt, plt, uint32(-(plt.Size + 4))) - - // rel - ld.Addaddrplus(ctxt, rel, got, got.Size-4) - - ld.Adduint32(ctxt, rel, ld.ELF32_R_INFO(uint32(s.Dynid), ld.R_386_JMP_SLOT)) - - s.Plt = int32(plt.Size - 16) - } else if ld.HEADTYPE == obj.Hdarwin { - // Same laziness as in 6l. - - plt := ld.Linklookup(ctxt, ".plt", 0) - - addgotsym(ctxt, s) - - ld.Adduint32(ctxt, ld.Linklookup(ctxt, ".linkedit.plt", 0), uint32(s.Dynid)) - - // jmpq *got+size(IP) - s.Plt = int32(plt.Size) - - ld.Adduint8(ctxt, plt, 0xff) - ld.Adduint8(ctxt, plt, 0x25) - ld.Addaddrplus(ctxt, plt, ld.Linklookup(ctxt, ".got", 0), int64(s.Got)) - } else { - ld.Diag("addpltsym: unsupported binary format") - } -} - -func addgotsym(ctxt *ld.Link, s *ld.LSym) { - if s.Got >= 0 { - return - } - - ld.Adddynsym(ctxt, s) - got := ld.Linklookup(ctxt, ".got", 0) - s.Got = int32(got.Size) - ld.Adduint32(ctxt, got, 0) - - if ld.Iself { - rel := ld.Linklookup(ctxt, ".rel", 0) - ld.Addaddrplus(ctxt, rel, got, int64(s.Got)) - ld.Adduint32(ctxt, rel, ld.ELF32_R_INFO(uint32(s.Dynid), ld.R_386_GLOB_DAT)) - } else if ld.HEADTYPE == obj.Hdarwin { - ld.Adduint32(ctxt, ld.Linklookup(ctxt, ".linkedit.got", 0), uint32(s.Dynid)) - } else { - ld.Diag("addgotsym: unsupported binary format") - } -} - -func asmb() { - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime()) - } - ld.Bso.Flush() - - if ld.Iself { - ld.Asmbelfsetup() - } - - sect := ld.Segtext.Sect - ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) - ld.Codeblk(int64(sect.Vaddr), int64(sect.Length)) - for sect = sect.Next; sect != nil; sect = sect.Next { - ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) - ld.Datblk(int64(sect.Vaddr), int64(sect.Length)) - } - - if ld.Segrodata.Filelen > 0 { - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) - } - ld.Bso.Flush() - - ld.Cseek(int64(ld.Segrodata.Fileoff)) - ld.Datblk(int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) - } - - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f datblk\n", obj.Cputime()) - } - ld.Bso.Flush() - - ld.Cseek(int64(ld.Segdata.Fileoff)) - ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) - - machlink := uint32(0) - if ld.HEADTYPE == obj.Hdarwin { - if ld.Debug['v'] != 0 { - 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)) - - ld.Segdwarf.Fileoff = uint64(ld.Cpos()) - ld.Dwarfemitdebugsections() - ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff - - machlink = uint32(ld.Domacholink()) - } - - ld.Symsize = 0 - ld.Spsize = 0 - ld.Lcsize = 0 - symo := uint32(0) - if ld.Debug['s'] == 0 { - // TODO: rationalize - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime()) - } - ld.Bso.Flush() - switch ld.HEADTYPE { - default: - if ld.Iself { - symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) - symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND))) - } - - case obj.Hplan9: - 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)) - - case obj.Hwindows: - symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) - symo = uint32(ld.Rnd(int64(symo), ld.PEFILEALIGN)) - } - - ld.Cseek(int64(symo)) - switch ld.HEADTYPE { - default: - if ld.Iself { - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f elfsym\n", obj.Cputime()) - } - ld.Asmelfsym() - ld.Cflush() - ld.Cwrite(ld.Elfstrdat) - - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) - } - ld.Dwarfemitdebugsections() - - if ld.Linkmode == ld.LinkExternal { - ld.Elfemitreloc() - } - } - - case obj.Hplan9: - ld.Asmplan9sym() - ld.Cflush() - - sym := ld.Linklookup(ld.Ctxt, "pclntab", 0) - if sym != nil { - ld.Lcsize = int32(len(sym.P)) - for i := 0; int32(i) < ld.Lcsize; i++ { - ld.Cput(uint8(sym.P[i])) - } - - ld.Cflush() - } - - case obj.Hwindows: - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) - } - ld.Dwarfemitdebugsections() - - case obj.Hdarwin: - if ld.Linkmode == ld.LinkExternal { - ld.Machoemitreloc() - } - } - } - - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f headr\n", obj.Cputime()) - } - ld.Bso.Flush() - ld.Cseek(0) - switch ld.HEADTYPE { - default: - case obj.Hplan9: /* plan9 */ - magic := int32(4*11*11 + 7) - - ld.Lputb(uint32(magic)) /* magic */ - ld.Lputb(uint32(ld.Segtext.Filelen)) /* sizes */ - ld.Lputb(uint32(ld.Segdata.Filelen)) - ld.Lputb(uint32(ld.Segdata.Length - ld.Segdata.Filelen)) - ld.Lputb(uint32(ld.Symsize)) /* nsyms */ - ld.Lputb(uint32(ld.Entryvalue())) /* va of entry */ - ld.Lputb(uint32(ld.Spsize)) /* sp offsets */ - ld.Lputb(uint32(ld.Lcsize)) /* line offsets */ - - case obj.Hdarwin: - ld.Asmbmacho() - - case obj.Hlinux, - obj.Hfreebsd, - obj.Hnetbsd, - obj.Hopenbsd, - obj.Hnacl: - ld.Asmbelf(int64(symo)) - - case obj.Hwindows: - ld.Asmbpe() - } - - ld.Cflush() -} diff --git a/src/cmd/8l/l.go b/src/cmd/8l/l.go deleted file mode 100644 index 5cb9f8d8af..0000000000 --- a/src/cmd/8l/l.go +++ /dev/null @@ -1,47 +0,0 @@ -// Inferno utils/8l/l.h -// http://code.google.com/p/inferno-os/source/browse/utils/8l/l.h -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package main - -const ( - thechar = '8' - PtrSize = 4 - IntSize = 4 - RegSize = 4 - MaxAlign = 32 // max data alignment - FuncAlign = 16 - MINLC = 1 -) - -/* 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 deleted file mode 100644 index bea0d03cfe..0000000000 --- a/src/cmd/8l/obj.go +++ /dev/null @@ -1,187 +0,0 @@ -// Inferno utils/8l/obj.c -// http://code.google.com/p/inferno-os/source/browse/utils/8l/obj.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package main - -import ( - "cmd/internal/ld" - "cmd/internal/obj" - "fmt" - "log" -) - -// Reading object files. - -func main() { - linkarchinit() - ld.Ldmain() -} - -func linkarchinit() { - ld.Thestring = "386" - ld.Thelinkarch = &ld.Link386 - - ld.Thearch.Thechar = thechar - ld.Thearch.Ptrsize = ld.Thelinkarch.Ptrsize - ld.Thearch.Intsize = ld.Thelinkarch.Ptrsize - ld.Thearch.Regsize = ld.Thelinkarch.Regsize - ld.Thearch.Funcalign = FuncAlign - ld.Thearch.Maxalign = MaxAlign - ld.Thearch.Minlc = MINLC - ld.Thearch.Dwarfregsp = DWARFREGSP - ld.Thearch.Dwarfreglr = DWARFREGLR - - ld.Thearch.Adddynrel = adddynrel - ld.Thearch.Archinit = archinit - ld.Thearch.Archreloc = archreloc - ld.Thearch.Archrelocvariant = archrelocvariant - ld.Thearch.Asmb = asmb - ld.Thearch.Elfreloc1 = elfreloc1 - ld.Thearch.Elfsetupplt = elfsetupplt - ld.Thearch.Gentext = gentext - ld.Thearch.Machoreloc1 = machoreloc1 - ld.Thearch.PEreloc1 = pereloc1 - ld.Thearch.Lput = ld.Lputl - ld.Thearch.Wput = ld.Wputl - ld.Thearch.Vput = ld.Vputl - - ld.Thearch.Linuxdynld = "/lib/ld-linux.so.2" - ld.Thearch.Freebsddynld = "/usr/libexec/ld-elf.so.1" - ld.Thearch.Openbsddynld = "/usr/libexec/ld.so" - ld.Thearch.Netbsddynld = "/usr/libexec/ld.elf_so" - ld.Thearch.Solarisdynld = "/lib/ld.so.1" -} - -func archinit() { - // getgoextlinkenabled is based on GO_EXTLINK_ENABLED when - // Go was built; see ../../make.bash. - if ld.Linkmode == ld.LinkAuto && obj.Getgoextlinkenabled() == "0" { - ld.Linkmode = ld.LinkInternal - } - - switch ld.HEADTYPE { - default: - if ld.Linkmode == ld.LinkAuto { - ld.Linkmode = ld.LinkInternal - } - if ld.Linkmode == ld.LinkExternal && obj.Getgoextlinkenabled() != "1" { - log.Fatalf("cannot use -linkmode=external with -H %s", ld.Headstr(int(ld.HEADTYPE))) - } - - case obj.Hdarwin, - obj.Hfreebsd, - obj.Hlinux, - obj.Hnetbsd, - obj.Hopenbsd, - obj.Hwindows: - break - } - - switch ld.HEADTYPE { - default: - ld.Exitf("unknown -H option: %v", ld.HEADTYPE) - - case obj.Hplan9: /* plan 9 */ - ld.HEADR = 32 - - if ld.INITTEXT == -1 { - ld.INITTEXT = 4096 + 32 - } - if ld.INITDAT == -1 { - ld.INITDAT = 0 - } - if ld.INITRND == -1 { - ld.INITRND = 4096 - } - - case obj.Hdarwin: /* apple MACH */ - ld.Machoinit() - - ld.HEADR = ld.INITIAL_MACHO_HEADR - if ld.INITTEXT == -1 { - ld.INITTEXT = 4096 + int64(ld.HEADR) - } - if ld.INITDAT == -1 { - ld.INITDAT = 0 - } - if ld.INITRND == -1 { - ld.INITRND = 4096 - } - - case obj.Hlinux, /* elf32 executable */ - obj.Hfreebsd, - obj.Hnetbsd, - obj.Hopenbsd: - ld.Elfinit() - - ld.HEADR = ld.ELFRESERVE - if ld.INITTEXT == -1 { - ld.INITTEXT = 0x08048000 + int64(ld.HEADR) - } - if ld.INITDAT == -1 { - ld.INITDAT = 0 - } - if ld.INITRND == -1 { - ld.INITRND = 4096 - } - - case obj.Hnacl: - ld.Elfinit() - ld.HEADR = 0x10000 - ld.Funcalign = 32 - if ld.INITTEXT == -1 { - ld.INITTEXT = 0x20000 - } - if ld.INITDAT == -1 { - ld.INITDAT = 0 - } - if ld.INITRND == -1 { - ld.INITRND = 0x10000 - } - - case obj.Hwindows: /* PE executable */ - ld.Peinit() - - ld.HEADR = ld.PEFILEHEADR - if ld.INITTEXT == -1 { - ld.INITTEXT = ld.PEBASE + int64(ld.PESECTHEADR) - } - if ld.INITDAT == -1 { - ld.INITDAT = 0 - } - if ld.INITRND == -1 { - ld.INITRND = ld.PESECTALIGN - } - } - - if ld.INITDAT != 0 && ld.INITRND != 0 { - fmt.Printf("warning: -D0x%x is ignored because of -R0x%x\n", uint64(ld.INITDAT), uint32(ld.INITRND)) - } -} diff --git a/src/cmd/9g/cgen.go b/src/cmd/9g/cgen.go deleted file mode 100644 index 5d24a6ff67..0000000000 --- a/src/cmd/9g/cgen.go +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2009 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/gc" - "cmd/internal/obj" - "cmd/internal/obj/ppc64" -) - -func blockcopy(n, res *gc.Node, osrc, odst, w int64) { - // determine alignment. - // want to avoid unaligned access, so have to use - // smaller operations for less aligned types. - // for example moving [4]byte must use 4 MOVB not 1 MOVW. - align := int(n.Type.Align) - - var op int - switch align { - default: - gc.Fatal("sgen: invalid alignment %d for %v", align, n.Type) - - case 1: - op = ppc64.AMOVBU - - case 2: - op = ppc64.AMOVHU - - case 4: - op = ppc64.AMOVWZU // there is no lwau, only lwaux - - case 8: - op = ppc64.AMOVDU - } - - if w%int64(align) != 0 { - gc.Fatal("sgen: unaligned size %d (align=%d) for %v", w, align, n.Type) - } - c := int32(w / int64(align)) - - // if we are copying forward on the stack and - // the src and dst overlap, then reverse direction - dir := align - - if osrc < odst && int64(odst) < int64(osrc)+w { - dir = -dir - } - - var dst gc.Node - var src gc.Node - if n.Ullman >= res.Ullman { - gc.Agenr(n, &dst, res) // temporarily use dst - gc.Regalloc(&src, gc.Types[gc.Tptr], nil) - gins(ppc64.AMOVD, &dst, &src) - if res.Op == gc.ONAME { - gc.Gvardef(res) - } - gc.Agen(res, &dst) - } else { - if res.Op == gc.ONAME { - gc.Gvardef(res) - } - gc.Agenr(res, &dst, res) - gc.Agenr(n, &src, nil) - } - - var tmp gc.Node - gc.Regalloc(&tmp, gc.Types[gc.Tptr], nil) - - // set up end marker - var nend gc.Node - - // move src and dest to the end of block if necessary - if dir < 0 { - if c >= 4 { - gc.Regalloc(&nend, gc.Types[gc.Tptr], nil) - gins(ppc64.AMOVD, &src, &nend) - } - - p := gins(ppc64.AADD, nil, &src) - p.From.Type = obj.TYPE_CONST - p.From.Offset = w - - p = gins(ppc64.AADD, nil, &dst) - p.From.Type = obj.TYPE_CONST - p.From.Offset = w - } else { - p := gins(ppc64.AADD, nil, &src) - p.From.Type = obj.TYPE_CONST - p.From.Offset = int64(-dir) - - p = gins(ppc64.AADD, nil, &dst) - p.From.Type = obj.TYPE_CONST - p.From.Offset = int64(-dir) - - if c >= 4 { - gc.Regalloc(&nend, gc.Types[gc.Tptr], nil) - p := gins(ppc64.AMOVD, &src, &nend) - p.From.Type = obj.TYPE_ADDR - p.From.Offset = w - } - } - - // move - // TODO: enable duffcopy for larger copies. - if c >= 4 { - p := gins(op, &src, &tmp) - p.From.Type = obj.TYPE_MEM - p.From.Offset = int64(dir) - ploop := p - - p = gins(op, &tmp, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = int64(dir) - - p = gins(ppc64.ACMP, &src, &nend) - - gc.Patch(gc.Gbranch(ppc64.ABNE, nil, 0), ploop) - gc.Regfree(&nend) - } else { - // TODO(austin): Instead of generating ADD $-8,R8; ADD - // $-8,R7; n*(MOVDU 8(R8),R9; MOVDU R9,8(R7);) just - // generate the offsets directly and eliminate the - // ADDs. That will produce shorter, more - // pipeline-able code. - var p *obj.Prog - for { - tmp14 := c - c-- - if tmp14 <= 0 { - break - } - - p = gins(op, &src, &tmp) - p.From.Type = obj.TYPE_MEM - p.From.Offset = int64(dir) - - p = gins(op, &tmp, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = int64(dir) - } - } - - gc.Regfree(&dst) - gc.Regfree(&src) - gc.Regfree(&tmp) -} diff --git a/src/cmd/9g/galign.go b/src/cmd/9g/galign.go deleted file mode 100644 index 6e1612007b..0000000000 --- a/src/cmd/9g/galign.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2009 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/gc" - "cmd/internal/obj" - "cmd/internal/obj/ppc64" -) - -var thechar int = '9' - -var thestring string = "ppc64" - -var thelinkarch *obj.LinkArch - -func linkarchinit() { - thestring = obj.Getgoarch() - gc.Thearch.Thestring = thestring - if thestring == "ppc64le" { - thelinkarch = &ppc64.Linkppc64le - } else { - thelinkarch = &ppc64.Linkppc64 - } - gc.Thearch.Thelinkarch = thelinkarch -} - -var MAXWIDTH int64 = 1 << 50 - -/* - * go declares several platform-specific type aliases: - * int, uint, and uintptr - */ -var typedefs = []gc.Typedef{ - gc.Typedef{"int", gc.TINT, gc.TINT64}, - gc.Typedef{"uint", gc.TUINT, gc.TUINT64}, - gc.Typedef{"uintptr", gc.TUINTPTR, gc.TUINT64}, -} - -func betypeinit() { - gc.Widthptr = 8 - gc.Widthint = 8 - gc.Widthreg = 8 -} - -func main() { - gc.Thearch.Thechar = thechar - gc.Thearch.Thestring = thestring - gc.Thearch.Thelinkarch = thelinkarch - gc.Thearch.Typedefs = typedefs - gc.Thearch.REGSP = ppc64.REGSP - gc.Thearch.REGCTXT = ppc64.REGCTXT - gc.Thearch.REGCALLX = ppc64.REG_R3 - gc.Thearch.REGCALLX2 = ppc64.REG_R4 - gc.Thearch.REGRETURN = ppc64.REG_R3 - gc.Thearch.REGMIN = ppc64.REG_R0 - gc.Thearch.REGMAX = ppc64.REG_R31 - gc.Thearch.FREGMIN = ppc64.REG_F0 - gc.Thearch.FREGMAX = ppc64.REG_F31 - gc.Thearch.MAXWIDTH = MAXWIDTH - gc.Thearch.ReservedRegs = resvd - - gc.Thearch.Betypeinit = betypeinit - gc.Thearch.Cgen_hmul = cgen_hmul - gc.Thearch.Cgen_shift = cgen_shift - gc.Thearch.Clearfat = clearfat - gc.Thearch.Defframe = defframe - gc.Thearch.Dodiv = dodiv - gc.Thearch.Excise = excise - 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 - gc.Thearch.Linkarchinit = linkarchinit - gc.Thearch.Peep = peep - gc.Thearch.Proginfo = proginfo - gc.Thearch.Regtyp = regtyp - gc.Thearch.Sameaddr = sameaddr - gc.Thearch.Smallindir = smallindir - gc.Thearch.Stackaddr = stackaddr - gc.Thearch.Blockcopy = blockcopy - gc.Thearch.Sudoaddable = sudoaddable - gc.Thearch.Sudoclean = sudoclean - gc.Thearch.Excludedregs = excludedregs - gc.Thearch.RtoB = RtoB - gc.Thearch.FtoB = RtoB - gc.Thearch.BtoR = BtoR - gc.Thearch.BtoF = BtoF - gc.Thearch.Optoas = optoas - gc.Thearch.Doregbits = doregbits - gc.Thearch.Regnames = regnames - - gc.Main() - gc.Exit(0) -} diff --git a/src/cmd/9g/ggen.go b/src/cmd/9g/ggen.go deleted file mode 100644 index 3a10a2a760..0000000000 --- a/src/cmd/9g/ggen.go +++ /dev/null @@ -1,558 +0,0 @@ -// Copyright 2009 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/gc" - "cmd/internal/obj" - "cmd/internal/obj/ppc64" - "fmt" -) - -func defframe(ptxt *obj.Prog) { - var n *gc.Node - - // fill in argument size, stack size - ptxt.To.Type = obj.TYPE_TEXTSIZE - - ptxt.To.Val = int32(gc.Rnd(gc.Curfn.Type.Argwid, int64(gc.Widthptr))) - frame := uint32(gc.Rnd(gc.Stksize+gc.Maxarg, int64(gc.Widthreg))) - ptxt.To.Offset = int64(frame) - - // insert code to zero ambiguously live variables - // so that the garbage collector only sees initialized values - // when it looks for pointers. - p := ptxt - - hi := int64(0) - lo := hi - - // 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.Name.Needzero { - continue - } - if n.Class != gc.PAUTO { - gc.Fatal("needzero class %d", n.Class) - } - if n.Type.Width%int64(gc.Widthptr) != 0 || n.Xoffset%int64(gc.Widthptr) != 0 || n.Type.Width == 0 { - gc.Fatal("var %v has size %d offset %d", gc.Nconv(n, obj.FmtLong), int(n.Type.Width), int(n.Xoffset)) - } - - if lo != hi && n.Xoffset+n.Type.Width >= lo-int64(2*gc.Widthreg) { - // merge with range we already have - lo = n.Xoffset - - continue - } - - // zero old range - p = zerorange(p, int64(frame), lo, hi) - - // set new range - hi = n.Xoffset + n.Type.Width - - lo = n.Xoffset - } - - // zero final range - zerorange(p, int64(frame), lo, hi) -} - -func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog { - cnt := hi - lo - if cnt == 0 { - return p - } - if cnt < int64(4*gc.Widthptr) { - for i := int64(0); i < cnt; i += int64(gc.Widthptr) { - p = appendpp(p, ppc64.AMOVD, obj.TYPE_REG, ppc64.REGZERO, 0, obj.TYPE_MEM, ppc64.REGSP, 8+frame+lo+i) - } - } else if cnt <= int64(128*gc.Widthptr) { - p = appendpp(p, ppc64.AADD, obj.TYPE_CONST, 0, 8+frame+lo-8, obj.TYPE_REG, ppc64.REGRT1, 0) - p.Reg = ppc64.REGSP - p = appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0) - f := gc.Sysfunc("duffzero") - gc.Naddr(&p.To, f) - gc.Afunclit(&p.To, f) - p.To.Offset = 4 * (128 - cnt/int64(gc.Widthptr)) - } else { - p = appendpp(p, ppc64.AMOVD, obj.TYPE_CONST, 0, 8+frame+lo-8, obj.TYPE_REG, ppc64.REGTMP, 0) - p = appendpp(p, ppc64.AADD, obj.TYPE_REG, ppc64.REGTMP, 0, obj.TYPE_REG, ppc64.REGRT1, 0) - p.Reg = ppc64.REGSP - p = appendpp(p, ppc64.AMOVD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, ppc64.REGTMP, 0) - p = appendpp(p, ppc64.AADD, obj.TYPE_REG, ppc64.REGTMP, 0, obj.TYPE_REG, ppc64.REGRT2, 0) - p.Reg = ppc64.REGRT1 - p = appendpp(p, ppc64.AMOVDU, obj.TYPE_REG, ppc64.REGZERO, 0, obj.TYPE_MEM, ppc64.REGRT1, int64(gc.Widthptr)) - p1 := p - p = appendpp(p, ppc64.ACMP, obj.TYPE_REG, ppc64.REGRT1, 0, obj.TYPE_REG, ppc64.REGRT2, 0) - p = appendpp(p, ppc64.ABNE, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0) - gc.Patch(p, p1) - } - - return p -} - -func appendpp(p *obj.Prog, as int, ftype int, freg int, foffset int64, ttype int, treg int, toffset int64) *obj.Prog { - q := gc.Ctxt.NewProg() - gc.Clearp(q) - q.As = int16(as) - q.Lineno = p.Lineno - q.From.Type = int16(ftype) - q.From.Reg = int16(freg) - q.From.Offset = foffset - q.To.Type = int16(ttype) - q.To.Reg = int16(treg) - q.To.Offset = toffset - q.Link = p.Link - p.Link = q - return q -} - -func ginsnop() { - var reg gc.Node - gc.Nodreg(®, gc.Types[gc.TINT], ppc64.REG_R0) - gins(ppc64.AOR, ®, ®) -} - -var panicdiv *gc.Node - -/* - * generate division. - * generates one of: - * res = nl / nr - * res = nl % nr - * according to op. - */ -func dodiv(op int, nl *gc.Node, nr *gc.Node, res *gc.Node) { - // Have to be careful about handling - // most negative int divided by -1 correctly. - // The hardware will generate undefined result. - // Also need to explicitly trap on division on zero, - // the hardware will silently generate undefined result. - // DIVW will leave unpredicable result in higher 32-bit, - // so always use DIVD/DIVDU. - t := nl.Type - - t0 := t - check := 0 - if gc.Issigned[t.Etype] { - check = 1 - if gc.Isconst(nl, gc.CTINT) && nl.Int() != -(1<= nr.Ullman { - gc.Cgen(nl, &tl) - gc.Cgen(nr, &tr) - } else { - gc.Cgen(nr, &tr) - gc.Cgen(nl, &tl) - } - - if t != t0 { - // Convert - tl2 := tl - - tr2 := tr - tl.Type = t - tr.Type = t - gmove(&tl2, &tl) - gmove(&tr2, &tr) - } - - // Handle divide-by-zero panic. - p1 := gins(optoas(gc.OCMP, t), &tr, nil) - - p1.To.Type = obj.TYPE_REG - p1.To.Reg = ppc64.REGZERO - p1 = gc.Gbranch(optoas(gc.ONE, t), nil, +1) - if panicdiv == nil { - panicdiv = gc.Sysfunc("panicdivide") - } - gc.Ginscall(panicdiv, -1) - gc.Patch(p1, gc.Pc) - - var p2 *obj.Prog - if check != 0 { - var nm1 gc.Node - gc.Nodconst(&nm1, t, -1) - gins(optoas(gc.OCMP, t), &tr, &nm1) - p1 := gc.Gbranch(optoas(gc.ONE, t), nil, +1) - if op == gc.ODIV { - // a / (-1) is -a. - gins(optoas(gc.OMINUS, t), nil, &tl) - - gmove(&tl, res) - } else { - // a % (-1) is 0. - var nz gc.Node - gc.Nodconst(&nz, t, 0) - - gmove(&nz, res) - } - - p2 = gc.Gbranch(obj.AJMP, nil, 0) - gc.Patch(p1, gc.Pc) - } - - p1 = gins(a, &tr, &tl) - if op == gc.ODIV { - gc.Regfree(&tr) - gmove(&tl, res) - } else { - // A%B = A-(A/B*B) - var tm gc.Node - gc.Regalloc(&tm, t, nil) - - // patch div to use the 3 register form - // TODO(minux): add gins3? - p1.Reg = p1.To.Reg - - p1.To.Reg = tm.Reg - gins(optoas(gc.OMUL, t), &tr, &tm) - gc.Regfree(&tr) - gins(optoas(gc.OSUB, t), &tm, &tl) - gc.Regfree(&tm) - gmove(&tl, res) - } - - gc.Regfree(&tl) - if check != 0 { - gc.Patch(p2, gc.Pc) - } -} - -/* - * generate high multiply: - * res = (nl*nr) >> width - */ -func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) { - // largest ullman on left. - if nl.Ullman < nr.Ullman { - tmp := (*gc.Node)(nl) - nl = nr - nr = tmp - } - - t := (*gc.Type)(nl.Type) - w := int(int(t.Width * 8)) - var n1 gc.Node - gc.Cgenr(nl, &n1, res) - var n2 gc.Node - gc.Cgenr(nr, &n2, nil) - switch gc.Simtype[t.Etype] { - case gc.TINT8, - gc.TINT16, - gc.TINT32: - gins(optoas(gc.OMUL, t), &n2, &n1) - p := (*obj.Prog)(gins(ppc64.ASRAD, nil, &n1)) - p.From.Type = obj.TYPE_CONST - p.From.Offset = int64(w) - - case gc.TUINT8, - gc.TUINT16, - gc.TUINT32: - gins(optoas(gc.OMUL, t), &n2, &n1) - p := (*obj.Prog)(gins(ppc64.ASRD, nil, &n1)) - p.From.Type = obj.TYPE_CONST - p.From.Offset = int64(w) - - case gc.TINT64, - gc.TUINT64: - if gc.Issigned[t.Etype] { - gins(ppc64.AMULHD, &n2, &n1) - } else { - gins(ppc64.AMULHDU, &n2, &n1) - } - - default: - gc.Fatal("cgen_hmul %v", t) - } - - gc.Cgen(&n1, res) - gc.Regfree(&n1) - gc.Regfree(&n2) -} - -/* - * generate shift according to op, one of: - * res = nl << nr - * res = nl >> nr - */ -func cgen_shift(op int, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) { - a := int(optoas(op, nl.Type)) - - if nr.Op == gc.OLITERAL { - var n1 gc.Node - gc.Regalloc(&n1, nl.Type, res) - gc.Cgen(nl, &n1) - sc := uint64(nr.Int()) - if sc >= uint64(nl.Type.Width*8) { - // large shift gets 2 shifts by width-1 - var n3 gc.Node - gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1) - - gins(a, &n3, &n1) - gins(a, &n3, &n1) - } else { - gins(a, nr, &n1) - } - gmove(&n1, res) - gc.Regfree(&n1) - return - } - - if nl.Ullman >= gc.UINF { - var n4 gc.Node - gc.Tempname(&n4, nl.Type) - gc.Cgen(nl, &n4) - nl = &n4 - } - - if nr.Ullman >= gc.UINF { - var n5 gc.Node - gc.Tempname(&n5, nr.Type) - gc.Cgen(nr, &n5) - nr = &n5 - } - - // Allow either uint32 or uint64 as shift type, - // to avoid unnecessary conversion from uint32 to uint64 - // just to do the comparison. - tcount := gc.Types[gc.Simtype[nr.Type.Etype]] - - if tcount.Etype < gc.TUINT32 { - tcount = gc.Types[gc.TUINT32] - } - - var n1 gc.Node - gc.Regalloc(&n1, nr.Type, nil) // to hold the shift type in CX - var n3 gc.Node - gc.Regalloc(&n3, tcount, &n1) // to clear high bits of CX - - var n2 gc.Node - gc.Regalloc(&n2, nl.Type, res) - - if nl.Ullman >= nr.Ullman { - gc.Cgen(nl, &n2) - gc.Cgen(nr, &n1) - gmove(&n1, &n3) - } else { - gc.Cgen(nr, &n1) - gmove(&n1, &n3) - gc.Cgen(nl, &n2) - } - - gc.Regfree(&n3) - - // test and fix up large shifts - if !bounded { - gc.Nodconst(&n3, tcount, nl.Type.Width*8) - gins(optoas(gc.OCMP, tcount), &n1, &n3) - p1 := (*obj.Prog)(gc.Gbranch(optoas(gc.OLT, tcount), nil, +1)) - if op == gc.ORSH && gc.Issigned[nl.Type.Etype] { - gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1) - gins(a, &n3, &n2) - } else { - gc.Nodconst(&n3, nl.Type, 0) - gmove(&n3, &n2) - } - - gc.Patch(p1, gc.Pc) - } - - gins(a, &n1, &n2) - - gmove(&n2, res) - - gc.Regfree(&n1) - gc.Regfree(&n2) -} - -func clearfat(nl *gc.Node) { - /* clear a fat object */ - if gc.Debug['g'] != 0 { - fmt.Printf("clearfat %v (%v, size: %d)\n", nl, nl.Type, nl.Type.Width) - } - - w := uint64(uint64(nl.Type.Width)) - - // Avoid taking the address for simple enough types. - if gc.Componentgen(nil, nl) { - return - } - - c := uint64(w % 8) // bytes - q := uint64(w / 8) // dwords - - if gc.Reginuse(ppc64.REGRT1) { - gc.Fatal("%v in use during clearfat", obj.Rconv(ppc64.REGRT1)) - } - - var r0 gc.Node - gc.Nodreg(&r0, gc.Types[gc.TUINT64], ppc64.REGZERO) - var dst gc.Node - gc.Nodreg(&dst, gc.Types[gc.Tptr], ppc64.REGRT1) - gc.Regrealloc(&dst) - gc.Agen(nl, &dst) - - var boff uint64 - if q > 128 { - p := gins(ppc64.ASUB, nil, &dst) - p.From.Type = obj.TYPE_CONST - p.From.Offset = 8 - - var end gc.Node - gc.Regalloc(&end, gc.Types[gc.Tptr], nil) - p = gins(ppc64.AMOVD, &dst, &end) - p.From.Type = obj.TYPE_ADDR - p.From.Offset = int64(q * 8) - - p = gins(ppc64.AMOVDU, &r0, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = 8 - pl := (*obj.Prog)(p) - - p = gins(ppc64.ACMP, &dst, &end) - gc.Patch(gc.Gbranch(ppc64.ABNE, nil, 0), pl) - - gc.Regfree(&end) - - // The loop leaves R3 on the last zeroed dword - boff = 8 - } else if q >= 4 { - p := gins(ppc64.ASUB, nil, &dst) - p.From.Type = obj.TYPE_CONST - p.From.Offset = 8 - f := (*gc.Node)(gc.Sysfunc("duffzero")) - p = gins(obj.ADUFFZERO, nil, f) - gc.Afunclit(&p.To, f) - - // 4 and 128 = magic constants: see ../../runtime/asm_ppc64x.s - p.To.Offset = int64(4 * (128 - q)) - - // duffzero leaves R3 on the last zeroed dword - boff = 8 - } else { - var p *obj.Prog - for t := uint64(0); t < q; t++ { - p = gins(ppc64.AMOVD, &r0, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = int64(8 * t) - } - - boff = 8 * q - } - - var p *obj.Prog - for t := uint64(0); t < c; t++ { - p = gins(ppc64.AMOVB, &r0, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = int64(t + boff) - } - - gc.Regfree(&dst) -} - -// Called after regopt and peep have run. -// Expand CHECKNIL pseudo-op into actual nil pointer check. -func expandchecks(firstp *obj.Prog) { - var p1 *obj.Prog - var p2 *obj.Prog - - for p := (*obj.Prog)(firstp); p != nil; p = p.Link { - if gc.Debug_checknil != 0 && gc.Ctxt.Debugvlog != 0 { - fmt.Printf("expandchecks: %v\n", p) - } - if p.As != obj.ACHECKNIL { - continue - } - if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers - gc.Warnl(int(p.Lineno), "generated nil check") - } - if p.From.Type != obj.TYPE_REG { - gc.Fatal("invalid nil check %v\n", p) - } - - /* - // check is - // TD $4, R0, arg (R0 is always zero) - // eqv. to: - // tdeq r0, arg - // NOTE: this needs special runtime support to make SIGTRAP recoverable. - reg = p->from.reg; - p->as = ATD; - p->from = p->to = p->from3 = zprog.from; - p->from.type = TYPE_CONST; - p->from.offset = 4; - p->from.reg = 0; - p->reg = REGZERO; - p->to.type = TYPE_REG; - p->to.reg = reg; - */ - // check is - // CMP arg, R0 - // BNE 2(PC) [likely] - // MOVD R0, 0(R0) - p1 = gc.Ctxt.NewProg() - - p2 = gc.Ctxt.NewProg() - gc.Clearp(p1) - gc.Clearp(p2) - p1.Link = p2 - p2.Link = p.Link - p.Link = p1 - p1.Lineno = p.Lineno - p2.Lineno = p.Lineno - p1.Pc = 9999 - p2.Pc = 9999 - p.As = ppc64.ACMP - p.To.Type = obj.TYPE_REG - p.To.Reg = ppc64.REGZERO - p1.As = ppc64.ABNE - - //p1->from.type = TYPE_CONST; - //p1->from.offset = 1; // likely - p1.To.Type = obj.TYPE_BRANCH - - p1.To.Val = p2.Link - - // crash by write to memory address 0. - p2.As = ppc64.AMOVD - - p2.From.Type = obj.TYPE_REG - p2.From.Reg = ppc64.REGZERO - p2.To.Type = obj.TYPE_MEM - p2.To.Reg = ppc64.REGZERO - p2.To.Offset = 0 - } -} - -// res = runtime.getg() -func getg(res *gc.Node) { - var n1 gc.Node - gc.Nodreg(&n1, res.Type, ppc64.REGG) - gmove(&n1, res) -} diff --git a/src/cmd/9g/gsubr.go b/src/cmd/9g/gsubr.go deleted file mode 100644 index 3a7c884fd1..0000000000 --- a/src/cmd/9g/gsubr.go +++ /dev/null @@ -1,1031 +0,0 @@ -// Derived from Inferno utils/6c/txt.c -// http://code.google.com/p/inferno-os/source/browse/utils/6c/txt.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package main - -import ( - "cmd/internal/gc" - "cmd/internal/gc/big" - "cmd/internal/obj" - "cmd/internal/obj/ppc64" - "fmt" -) - -var resvd = []int{ - ppc64.REGZERO, - ppc64.REGSP, // reserved for SP - // We need to preserve the C ABI TLS pointer because sigtramp - // may happen during C code and needs to access the g. C - // clobbers REGG, so if Go were to clobber REGTLS, sigtramp - // won't know which convention to use. By preserving REGTLS, - // we can just retrieve g from TLS when we aren't sure. - ppc64.REGTLS, - - // TODO(austin): Consolidate REGTLS and REGG? - ppc64.REGG, - ppc64.REGTMP, // REGTMP - ppc64.FREGCVI, - ppc64.FREGZERO, - ppc64.FREGHALF, - ppc64.FREGONE, - ppc64.FREGTWO, -} - -/* - * generate - * as $c, n - */ -func ginscon(as int, c int64, n2 *gc.Node) { - var n1 gc.Node - - gc.Nodconst(&n1, gc.Types[gc.TINT64], c) - - if as != ppc64.AMOVD && (c < -ppc64.BIG || c > ppc64.BIG) || n2.Op != gc.OREGISTER || as == ppc64.AMULLD { - // cannot have more than 16-bit of immediate in ADD, etc. - // instead, MOV into register first. - var ntmp gc.Node - gc.Regalloc(&ntmp, gc.Types[gc.TINT64], nil) - - rawgins(ppc64.AMOVD, &n1, &ntmp) - rawgins(as, &ntmp, n2) - gc.Regfree(&ntmp) - return - } - - rawgins(as, &n1, n2) -} - -/* - * generate - * as n, $c (CMP/CMPU) - */ -func ginscon2(as int, n2 *gc.Node, c int64) { - var n1 gc.Node - - gc.Nodconst(&n1, gc.Types[gc.TINT64], c) - - switch as { - default: - gc.Fatal("ginscon2") - - case ppc64.ACMP: - if -ppc64.BIG <= c && c <= ppc64.BIG { - rawgins(as, n2, &n1) - return - } - - case ppc64.ACMPU: - if 0 <= c && c <= 2*ppc64.BIG { - rawgins(as, n2, &n1) - return - } - } - - // MOV n1 into register first - var ntmp gc.Node - gc.Regalloc(&ntmp, gc.Types[gc.TINT64], nil) - - rawgins(ppc64.AMOVD, &n1, &ntmp) - rawgins(as, n2, &ntmp) - 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, n2.Int()) - } 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 -var ( - bigi gc.Node - bigf gc.Node - bignodes_did bool -) - -func bignodes() { - if bignodes_did { - return - } - bignodes_did = true - - var i big.Int - i.SetInt64(1) - i.Lsh(&i, 63) - - gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0) - bigi.SetBigInt(&i) - - bigi.Convconst(&bigf, gc.Types[gc.TFLOAT64]) -} - -/* - * generate move: - * t = f - * hard part is conversions. - */ -func gmove(f *gc.Node, t *gc.Node) { - if gc.Debug['M'] != 0 { - fmt.Printf("gmove %v -> %v\n", gc.Nconv(f, obj.FmtLong), gc.Nconv(t, obj.FmtLong)) - } - - ft := int(gc.Simsimtype(f.Type)) - tt := int(gc.Simsimtype(t.Type)) - cvt := (*gc.Type)(t.Type) - - if gc.Iscomplex[ft] || gc.Iscomplex[tt] { - gc.Complexmove(f, t) - return - } - - // cannot have two memory operands - var r2 gc.Node - var r1 gc.Node - var a int - if gc.Ismem(f) && gc.Ismem(t) { - goto hard - } - - // convert constant to desired type - if f.Op == gc.OLITERAL { - var con gc.Node - switch tt { - default: - f.Convconst(&con, t.Type) - - case gc.TINT32, - gc.TINT16, - gc.TINT8: - var con gc.Node - f.Convconst(&con, gc.Types[gc.TINT64]) - var r1 gc.Node - gc.Regalloc(&r1, con.Type, t) - gins(ppc64.AMOVD, &con, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - - case gc.TUINT32, - gc.TUINT16, - gc.TUINT8: - var con gc.Node - f.Convconst(&con, gc.Types[gc.TUINT64]) - var r1 gc.Node - gc.Regalloc(&r1, con.Type, t) - gins(ppc64.AMOVD, &con, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - } - - f = &con - ft = tt // so big switch will choose a simple mov - - // constants can't move directly to memory. - if gc.Ismem(t) { - goto hard - } - } - - // float constants come from memory. - //if(isfloat[tt]) - // goto hard; - - // 64-bit immediates are also from memory. - //if(isint[tt]) - // goto hard; - //// 64-bit immediates are really 32-bit sign-extended - //// unless moving into a register. - //if(isint[tt]) { - // if(mpcmpfixfix(con.val.u.xval, minintval[TINT32]) < 0) - // goto hard; - // if(mpcmpfixfix(con.val.u.xval, maxintval[TINT32]) > 0) - // goto hard; - //} - - // value -> value copy, only one memory operand. - // figure out the instruction to use. - // break out of switch for one-instruction gins. - // goto rdst for "destination must be register". - // goto hard for "convert to cvt type first". - // otherwise handle and return. - - switch uint32(ft)<<16 | uint32(tt) { - default: - gc.Fatal("gmove %v -> %v", gc.Tconv(f.Type, obj.FmtLong), gc.Tconv(t.Type, obj.FmtLong)) - - /* - * integer copy and truncate - */ - case gc.TINT8<<16 | gc.TINT8, // same size - gc.TUINT8<<16 | gc.TINT8, - gc.TINT16<<16 | gc.TINT8, - // truncate - gc.TUINT16<<16 | gc.TINT8, - gc.TINT32<<16 | gc.TINT8, - gc.TUINT32<<16 | gc.TINT8, - gc.TINT64<<16 | gc.TINT8, - gc.TUINT64<<16 | gc.TINT8: - a = ppc64.AMOVB - - case gc.TINT8<<16 | gc.TUINT8, // same size - gc.TUINT8<<16 | gc.TUINT8, - gc.TINT16<<16 | gc.TUINT8, - // truncate - gc.TUINT16<<16 | gc.TUINT8, - gc.TINT32<<16 | gc.TUINT8, - gc.TUINT32<<16 | gc.TUINT8, - gc.TINT64<<16 | gc.TUINT8, - gc.TUINT64<<16 | gc.TUINT8: - a = ppc64.AMOVBZ - - case gc.TINT16<<16 | gc.TINT16, // same size - gc.TUINT16<<16 | gc.TINT16, - gc.TINT32<<16 | gc.TINT16, - // truncate - gc.TUINT32<<16 | gc.TINT16, - gc.TINT64<<16 | gc.TINT16, - gc.TUINT64<<16 | gc.TINT16: - a = ppc64.AMOVH - - case gc.TINT16<<16 | gc.TUINT16, // same size - gc.TUINT16<<16 | gc.TUINT16, - gc.TINT32<<16 | gc.TUINT16, - // truncate - gc.TUINT32<<16 | gc.TUINT16, - gc.TINT64<<16 | gc.TUINT16, - gc.TUINT64<<16 | gc.TUINT16: - a = ppc64.AMOVHZ - - case gc.TINT32<<16 | gc.TINT32, // same size - gc.TUINT32<<16 | gc.TINT32, - gc.TINT64<<16 | gc.TINT32, - // truncate - gc.TUINT64<<16 | gc.TINT32: - a = ppc64.AMOVW - - case gc.TINT32<<16 | gc.TUINT32, // same size - gc.TUINT32<<16 | gc.TUINT32, - gc.TINT64<<16 | gc.TUINT32, - gc.TUINT64<<16 | gc.TUINT32: - a = ppc64.AMOVWZ - - case gc.TINT64<<16 | gc.TINT64, // same size - gc.TINT64<<16 | gc.TUINT64, - gc.TUINT64<<16 | gc.TINT64, - gc.TUINT64<<16 | gc.TUINT64: - a = ppc64.AMOVD - - /* - * integer up-conversions - */ - case gc.TINT8<<16 | gc.TINT16, // sign extend int8 - gc.TINT8<<16 | gc.TUINT16, - gc.TINT8<<16 | gc.TINT32, - gc.TINT8<<16 | gc.TUINT32, - gc.TINT8<<16 | gc.TINT64, - gc.TINT8<<16 | gc.TUINT64: - a = ppc64.AMOVB - - goto rdst - - case gc.TUINT8<<16 | gc.TINT16, // zero extend uint8 - gc.TUINT8<<16 | gc.TUINT16, - gc.TUINT8<<16 | gc.TINT32, - gc.TUINT8<<16 | gc.TUINT32, - gc.TUINT8<<16 | gc.TINT64, - gc.TUINT8<<16 | gc.TUINT64: - a = ppc64.AMOVBZ - - goto rdst - - case gc.TINT16<<16 | gc.TINT32, // sign extend int16 - gc.TINT16<<16 | gc.TUINT32, - gc.TINT16<<16 | gc.TINT64, - gc.TINT16<<16 | gc.TUINT64: - a = ppc64.AMOVH - - goto rdst - - case gc.TUINT16<<16 | gc.TINT32, // zero extend uint16 - gc.TUINT16<<16 | gc.TUINT32, - gc.TUINT16<<16 | gc.TINT64, - gc.TUINT16<<16 | gc.TUINT64: - a = ppc64.AMOVHZ - - goto rdst - - case gc.TINT32<<16 | gc.TINT64, // sign extend int32 - gc.TINT32<<16 | gc.TUINT64: - a = ppc64.AMOVW - - goto rdst - - case gc.TUINT32<<16 | gc.TINT64, // zero extend uint32 - gc.TUINT32<<16 | gc.TUINT64: - a = ppc64.AMOVWZ - - goto rdst - - //warn("gmove: convert float to int not implemented: %N -> %N\n", f, t); - //return; - // algorithm is: - // if small enough, use native float64 -> int64 conversion. - // otherwise, subtract 2^63, convert, and add it back. - /* - * float to integer - */ - case gc.TFLOAT32<<16 | gc.TINT32, - gc.TFLOAT64<<16 | gc.TINT32, - gc.TFLOAT32<<16 | gc.TINT64, - gc.TFLOAT64<<16 | gc.TINT64, - gc.TFLOAT32<<16 | gc.TINT16, - gc.TFLOAT32<<16 | gc.TINT8, - gc.TFLOAT32<<16 | gc.TUINT16, - gc.TFLOAT32<<16 | gc.TUINT8, - gc.TFLOAT64<<16 | gc.TINT16, - gc.TFLOAT64<<16 | gc.TINT8, - gc.TFLOAT64<<16 | gc.TUINT16, - gc.TFLOAT64<<16 | gc.TUINT8, - gc.TFLOAT32<<16 | gc.TUINT32, - gc.TFLOAT64<<16 | gc.TUINT32, - gc.TFLOAT32<<16 | gc.TUINT64, - gc.TFLOAT64<<16 | gc.TUINT64: - bignodes() - - var r1 gc.Node - gc.Regalloc(&r1, gc.Types[ft], f) - gmove(f, &r1) - if tt == gc.TUINT64 { - gc.Regalloc(&r2, gc.Types[gc.TFLOAT64], nil) - gmove(&bigf, &r2) - gins(ppc64.AFCMPU, &r1, &r2) - p1 := (*obj.Prog)(gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TFLOAT64]), nil, +1)) - gins(ppc64.AFSUB, &r2, &r1) - gc.Patch(p1, gc.Pc) - gc.Regfree(&r2) - } - - gc.Regalloc(&r2, gc.Types[gc.TFLOAT64], nil) - var r3 gc.Node - gc.Regalloc(&r3, gc.Types[gc.TINT64], t) - gins(ppc64.AFCTIDZ, &r1, &r2) - p1 := (*obj.Prog)(gins(ppc64.AFMOVD, &r2, nil)) - p1.To.Type = obj.TYPE_MEM - p1.To.Reg = ppc64.REGSP - p1.To.Offset = -8 - p1 = gins(ppc64.AMOVD, nil, &r3) - p1.From.Type = obj.TYPE_MEM - p1.From.Reg = ppc64.REGSP - p1.From.Offset = -8 - gc.Regfree(&r2) - gc.Regfree(&r1) - if tt == gc.TUINT64 { - p1 := (*obj.Prog)(gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TFLOAT64]), nil, +1)) // use CR0 here again - gc.Nodreg(&r1, gc.Types[gc.TINT64], ppc64.REGTMP) - gins(ppc64.AMOVD, &bigi, &r1) - gins(ppc64.AADD, &r1, &r3) - gc.Patch(p1, gc.Pc) - } - - gmove(&r3, t) - gc.Regfree(&r3) - return - - //warn("gmove: convert int to float not implemented: %N -> %N\n", f, t); - //return; - // algorithm is: - // if small enough, use native int64 -> uint64 conversion. - // otherwise, halve (rounding to odd?), convert, and double. - /* - * integer to float - */ - case gc.TINT32<<16 | gc.TFLOAT32, - gc.TINT32<<16 | gc.TFLOAT64, - gc.TINT64<<16 | gc.TFLOAT32, - gc.TINT64<<16 | gc.TFLOAT64, - gc.TINT16<<16 | gc.TFLOAT32, - gc.TINT16<<16 | gc.TFLOAT64, - gc.TINT8<<16 | gc.TFLOAT32, - gc.TINT8<<16 | gc.TFLOAT64, - gc.TUINT16<<16 | gc.TFLOAT32, - gc.TUINT16<<16 | gc.TFLOAT64, - gc.TUINT8<<16 | gc.TFLOAT32, - gc.TUINT8<<16 | gc.TFLOAT64, - gc.TUINT32<<16 | gc.TFLOAT32, - gc.TUINT32<<16 | gc.TFLOAT64, - gc.TUINT64<<16 | gc.TFLOAT32, - gc.TUINT64<<16 | gc.TFLOAT64: - bignodes() - - var r1 gc.Node - gc.Regalloc(&r1, gc.Types[gc.TINT64], nil) - gmove(f, &r1) - if ft == gc.TUINT64 { - gc.Nodreg(&r2, gc.Types[gc.TUINT64], ppc64.REGTMP) - gmove(&bigi, &r2) - gins(ppc64.ACMPU, &r1, &r2) - p1 := (*obj.Prog)(gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT64]), nil, +1)) - p2 := (*obj.Prog)(gins(ppc64.ASRD, nil, &r1)) - p2.From.Type = obj.TYPE_CONST - p2.From.Offset = 1 - gc.Patch(p1, gc.Pc) - } - - gc.Regalloc(&r2, gc.Types[gc.TFLOAT64], t) - p1 := (*obj.Prog)(gins(ppc64.AMOVD, &r1, nil)) - p1.To.Type = obj.TYPE_MEM - p1.To.Reg = ppc64.REGSP - p1.To.Offset = -8 - p1 = gins(ppc64.AFMOVD, nil, &r2) - p1.From.Type = obj.TYPE_MEM - p1.From.Reg = ppc64.REGSP - p1.From.Offset = -8 - gins(ppc64.AFCFID, &r2, &r2) - gc.Regfree(&r1) - if ft == gc.TUINT64 { - p1 := (*obj.Prog)(gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT64]), nil, +1)) // use CR0 here again - gc.Nodreg(&r1, gc.Types[gc.TFLOAT64], ppc64.FREGTWO) - gins(ppc64.AFMUL, &r1, &r2) - gc.Patch(p1, gc.Pc) - } - - gmove(&r2, t) - gc.Regfree(&r2) - return - - /* - * float to float - */ - case gc.TFLOAT32<<16 | gc.TFLOAT32: - a = ppc64.AFMOVS - - case gc.TFLOAT64<<16 | gc.TFLOAT64: - a = ppc64.AFMOVD - - case gc.TFLOAT32<<16 | gc.TFLOAT64: - a = ppc64.AFMOVS - goto rdst - - case gc.TFLOAT64<<16 | gc.TFLOAT32: - a = ppc64.AFRSP - goto rdst - } - - gins(a, f, t) - return - - // requires register destination -rdst: - { - gc.Regalloc(&r1, t.Type, t) - - gins(a, f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - } - - // requires register intermediate -hard: - gc.Regalloc(&r1, cvt, t) - - gmove(f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return -} - -func intLiteral(n *gc.Node) (x int64, ok bool) { - switch { - case n == nil: - return - case gc.Isconst(n, gc.CTINT): - return n.Int(), true - case gc.Isconst(n, gc.CTBOOL): - return int64(obj.Bool2int(n.Bool())), true - } - return -} - -// gins is called by the front end. -// It synthesizes some multiple-instruction sequences -// so the front end can stay simpler. -func gins(as int, f, t *gc.Node) *obj.Prog { - if as >= obj.A_ARCHSPECIFIC { - if x, ok := intLiteral(f); ok { - ginscon(as, x, t) - return nil // caller must not use - } - } - if as == ppc64.ACMP || as == ppc64.ACMPU { - if x, ok := intLiteral(t); ok { - ginscon2(as, f, x) - return nil // caller must not use - } - } - return rawgins(as, f, t) -} - -/* - * generate one instruction: - * as f, t - */ -func rawgins(as int, f *gc.Node, t *gc.Node) *obj.Prog { - // TODO(austin): Add self-move test like in 6g (but be careful - // of truncation moves) - - p := gc.Prog(as) - gc.Naddr(&p.From, f) - gc.Naddr(&p.To, t) - - switch as { - case obj.ACALL: - if p.To.Type == obj.TYPE_REG && p.To.Reg != ppc64.REG_CTR { - // Allow front end to emit CALL REG, and rewrite into MOV REG, CTR; CALL CTR. - pp := gc.Prog(as) - pp.From = p.From - pp.To.Type = obj.TYPE_REG - pp.To.Reg = ppc64.REG_CTR - - p.As = ppc64.AMOVD - p.From = p.To - p.To.Type = obj.TYPE_REG - p.To.Reg = ppc64.REG_CTR - - if gc.Debug['g'] != 0 { - fmt.Printf("%v\n", p) - fmt.Printf("%v\n", pp) - } - - return pp - } - - // Bad things the front end has done to us. Crash to find call stack. - case ppc64.AAND, ppc64.AMULLD: - if p.From.Type == obj.TYPE_CONST { - gc.Debug['h'] = 1 - gc.Fatal("bad inst: %v", p) - } - case ppc64.ACMP, ppc64.ACMPU: - if p.From.Type == obj.TYPE_MEM || p.To.Type == obj.TYPE_MEM { - gc.Debug['h'] = 1 - gc.Fatal("bad inst: %v", p) - } - } - - if gc.Debug['g'] != 0 { - fmt.Printf("%v\n", p) - } - - w := int32(0) - switch as { - case ppc64.AMOVB, - ppc64.AMOVBU, - ppc64.AMOVBZ, - ppc64.AMOVBZU: - w = 1 - - case ppc64.AMOVH, - ppc64.AMOVHU, - ppc64.AMOVHZ, - ppc64.AMOVHZU: - w = 2 - - case ppc64.AMOVW, - ppc64.AMOVWU, - ppc64.AMOVWZ, - ppc64.AMOVWZU: - w = 4 - - case ppc64.AMOVD, - ppc64.AMOVDU: - if p.From.Type == obj.TYPE_CONST || p.From.Type == obj.TYPE_ADDR { - break - } - w = 8 - } - - if w != 0 && ((f != nil && p.From.Width < int64(w)) || (t != nil && p.To.Type != obj.TYPE_REG && p.To.Width > int64(w))) { - gc.Dump("f", f) - gc.Dump("t", t) - gc.Fatal("bad width: %v (%d, %d)\n", p, p.From.Width, p.To.Width) - } - - return p -} - -/* - * return Axxx for Oxxx on type t. - */ -func optoas(op int, t *gc.Type) int { - if t == nil { - gc.Fatal("optoas: t is nil") - } - - a := int(obj.AXXX) - switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) { - default: - gc.Fatal("optoas: no entry for op=%v type=%v", gc.Oconv(int(op), 0), t) - - case gc.OEQ<<16 | gc.TBOOL, - gc.OEQ<<16 | gc.TINT8, - gc.OEQ<<16 | gc.TUINT8, - gc.OEQ<<16 | gc.TINT16, - gc.OEQ<<16 | gc.TUINT16, - gc.OEQ<<16 | gc.TINT32, - gc.OEQ<<16 | gc.TUINT32, - gc.OEQ<<16 | gc.TINT64, - gc.OEQ<<16 | gc.TUINT64, - gc.OEQ<<16 | gc.TPTR32, - gc.OEQ<<16 | gc.TPTR64, - gc.OEQ<<16 | gc.TFLOAT32, - gc.OEQ<<16 | gc.TFLOAT64: - a = ppc64.ABEQ - - case gc.ONE<<16 | gc.TBOOL, - gc.ONE<<16 | gc.TINT8, - gc.ONE<<16 | gc.TUINT8, - gc.ONE<<16 | gc.TINT16, - gc.ONE<<16 | gc.TUINT16, - gc.ONE<<16 | gc.TINT32, - gc.ONE<<16 | gc.TUINT32, - gc.ONE<<16 | gc.TINT64, - gc.ONE<<16 | gc.TUINT64, - gc.ONE<<16 | gc.TPTR32, - gc.ONE<<16 | gc.TPTR64, - gc.ONE<<16 | gc.TFLOAT32, - gc.ONE<<16 | gc.TFLOAT64: - a = ppc64.ABNE - - case gc.OLT<<16 | gc.TINT8, // ACMP - gc.OLT<<16 | gc.TINT16, - gc.OLT<<16 | gc.TINT32, - gc.OLT<<16 | gc.TINT64, - gc.OLT<<16 | gc.TUINT8, - // ACMPU - gc.OLT<<16 | gc.TUINT16, - gc.OLT<<16 | gc.TUINT32, - gc.OLT<<16 | gc.TUINT64, - gc.OLT<<16 | gc.TFLOAT32, - // AFCMPU - gc.OLT<<16 | gc.TFLOAT64: - a = ppc64.ABLT - - case gc.OLE<<16 | gc.TINT8, // ACMP - gc.OLE<<16 | gc.TINT16, - gc.OLE<<16 | gc.TINT32, - gc.OLE<<16 | gc.TINT64, - gc.OLE<<16 | gc.TUINT8, - // ACMPU - gc.OLE<<16 | gc.TUINT16, - gc.OLE<<16 | gc.TUINT32, - gc.OLE<<16 | gc.TUINT64: - // No OLE for floats, because it mishandles NaN. - // Front end must reverse comparison or use OLT and OEQ together. - a = ppc64.ABLE - - case gc.OGT<<16 | gc.TINT8, - gc.OGT<<16 | gc.TINT16, - gc.OGT<<16 | gc.TINT32, - gc.OGT<<16 | gc.TINT64, - gc.OGT<<16 | gc.TUINT8, - gc.OGT<<16 | gc.TUINT16, - gc.OGT<<16 | gc.TUINT32, - gc.OGT<<16 | gc.TUINT64, - gc.OGT<<16 | gc.TFLOAT32, - gc.OGT<<16 | gc.TFLOAT64: - a = ppc64.ABGT - - case gc.OGE<<16 | gc.TINT8, - gc.OGE<<16 | gc.TINT16, - gc.OGE<<16 | gc.TINT32, - gc.OGE<<16 | gc.TINT64, - gc.OGE<<16 | gc.TUINT8, - gc.OGE<<16 | gc.TUINT16, - gc.OGE<<16 | gc.TUINT32, - gc.OGE<<16 | gc.TUINT64: - // No OGE for floats, because it mishandles NaN. - // Front end must reverse comparison or use OLT and OEQ together. - a = ppc64.ABGE - - case gc.OCMP<<16 | gc.TBOOL, - gc.OCMP<<16 | gc.TINT8, - gc.OCMP<<16 | gc.TINT16, - gc.OCMP<<16 | gc.TINT32, - gc.OCMP<<16 | gc.TPTR32, - gc.OCMP<<16 | gc.TINT64: - a = ppc64.ACMP - - case gc.OCMP<<16 | gc.TUINT8, - gc.OCMP<<16 | gc.TUINT16, - gc.OCMP<<16 | gc.TUINT32, - gc.OCMP<<16 | gc.TUINT64, - gc.OCMP<<16 | gc.TPTR64: - a = ppc64.ACMPU - - case gc.OCMP<<16 | gc.TFLOAT32, - gc.OCMP<<16 | gc.TFLOAT64: - a = ppc64.AFCMPU - - case gc.OAS<<16 | gc.TBOOL, - gc.OAS<<16 | gc.TINT8: - a = ppc64.AMOVB - - case gc.OAS<<16 | gc.TUINT8: - a = ppc64.AMOVBZ - - case gc.OAS<<16 | gc.TINT16: - a = ppc64.AMOVH - - case gc.OAS<<16 | gc.TUINT16: - a = ppc64.AMOVHZ - - case gc.OAS<<16 | gc.TINT32: - a = ppc64.AMOVW - - case gc.OAS<<16 | gc.TUINT32, - gc.OAS<<16 | gc.TPTR32: - a = ppc64.AMOVWZ - - case gc.OAS<<16 | gc.TINT64, - gc.OAS<<16 | gc.TUINT64, - gc.OAS<<16 | gc.TPTR64: - a = ppc64.AMOVD - - case gc.OAS<<16 | gc.TFLOAT32: - a = ppc64.AFMOVS - - case gc.OAS<<16 | gc.TFLOAT64: - a = ppc64.AFMOVD - - case gc.OADD<<16 | gc.TINT8, - gc.OADD<<16 | gc.TUINT8, - gc.OADD<<16 | gc.TINT16, - gc.OADD<<16 | gc.TUINT16, - gc.OADD<<16 | gc.TINT32, - gc.OADD<<16 | gc.TUINT32, - gc.OADD<<16 | gc.TPTR32, - gc.OADD<<16 | gc.TINT64, - gc.OADD<<16 | gc.TUINT64, - gc.OADD<<16 | gc.TPTR64: - a = ppc64.AADD - - case gc.OADD<<16 | gc.TFLOAT32: - a = ppc64.AFADDS - - case gc.OADD<<16 | gc.TFLOAT64: - a = ppc64.AFADD - - case gc.OSUB<<16 | gc.TINT8, - gc.OSUB<<16 | gc.TUINT8, - gc.OSUB<<16 | gc.TINT16, - gc.OSUB<<16 | gc.TUINT16, - gc.OSUB<<16 | gc.TINT32, - gc.OSUB<<16 | gc.TUINT32, - gc.OSUB<<16 | gc.TPTR32, - gc.OSUB<<16 | gc.TINT64, - gc.OSUB<<16 | gc.TUINT64, - gc.OSUB<<16 | gc.TPTR64: - a = ppc64.ASUB - - case gc.OSUB<<16 | gc.TFLOAT32: - a = ppc64.AFSUBS - - case gc.OSUB<<16 | gc.TFLOAT64: - a = ppc64.AFSUB - - case gc.OMINUS<<16 | gc.TINT8, - gc.OMINUS<<16 | gc.TUINT8, - gc.OMINUS<<16 | gc.TINT16, - gc.OMINUS<<16 | gc.TUINT16, - gc.OMINUS<<16 | gc.TINT32, - gc.OMINUS<<16 | gc.TUINT32, - gc.OMINUS<<16 | gc.TPTR32, - gc.OMINUS<<16 | gc.TINT64, - gc.OMINUS<<16 | gc.TUINT64, - gc.OMINUS<<16 | gc.TPTR64: - a = ppc64.ANEG - - case gc.OAND<<16 | gc.TINT8, - gc.OAND<<16 | gc.TUINT8, - gc.OAND<<16 | gc.TINT16, - gc.OAND<<16 | gc.TUINT16, - gc.OAND<<16 | gc.TINT32, - gc.OAND<<16 | gc.TUINT32, - gc.OAND<<16 | gc.TPTR32, - gc.OAND<<16 | gc.TINT64, - gc.OAND<<16 | gc.TUINT64, - gc.OAND<<16 | gc.TPTR64: - a = ppc64.AAND - - case gc.OOR<<16 | gc.TINT8, - gc.OOR<<16 | gc.TUINT8, - gc.OOR<<16 | gc.TINT16, - gc.OOR<<16 | gc.TUINT16, - gc.OOR<<16 | gc.TINT32, - gc.OOR<<16 | gc.TUINT32, - gc.OOR<<16 | gc.TPTR32, - gc.OOR<<16 | gc.TINT64, - gc.OOR<<16 | gc.TUINT64, - gc.OOR<<16 | gc.TPTR64: - a = ppc64.AOR - - case gc.OXOR<<16 | gc.TINT8, - gc.OXOR<<16 | gc.TUINT8, - gc.OXOR<<16 | gc.TINT16, - gc.OXOR<<16 | gc.TUINT16, - gc.OXOR<<16 | gc.TINT32, - gc.OXOR<<16 | gc.TUINT32, - gc.OXOR<<16 | gc.TPTR32, - gc.OXOR<<16 | gc.TINT64, - gc.OXOR<<16 | gc.TUINT64, - gc.OXOR<<16 | gc.TPTR64: - a = ppc64.AXOR - - // TODO(minux): handle rotates - //case CASE(OLROT, TINT8): - //case CASE(OLROT, TUINT8): - //case CASE(OLROT, TINT16): - //case CASE(OLROT, TUINT16): - //case CASE(OLROT, TINT32): - //case CASE(OLROT, TUINT32): - //case CASE(OLROT, TPTR32): - //case CASE(OLROT, TINT64): - //case CASE(OLROT, TUINT64): - //case CASE(OLROT, TPTR64): - // a = 0//???; RLDC? - // break; - - case gc.OLSH<<16 | gc.TINT8, - gc.OLSH<<16 | gc.TUINT8, - gc.OLSH<<16 | gc.TINT16, - gc.OLSH<<16 | gc.TUINT16, - gc.OLSH<<16 | gc.TINT32, - gc.OLSH<<16 | gc.TUINT32, - gc.OLSH<<16 | gc.TPTR32, - gc.OLSH<<16 | gc.TINT64, - gc.OLSH<<16 | gc.TUINT64, - gc.OLSH<<16 | gc.TPTR64: - a = ppc64.ASLD - - case gc.ORSH<<16 | gc.TUINT8, - gc.ORSH<<16 | gc.TUINT16, - gc.ORSH<<16 | gc.TUINT32, - gc.ORSH<<16 | gc.TPTR32, - gc.ORSH<<16 | gc.TUINT64, - gc.ORSH<<16 | gc.TPTR64: - a = ppc64.ASRD - - case gc.ORSH<<16 | gc.TINT8, - gc.ORSH<<16 | gc.TINT16, - gc.ORSH<<16 | gc.TINT32, - gc.ORSH<<16 | gc.TINT64: - a = ppc64.ASRAD - - // TODO(minux): handle rotates - //case CASE(ORROTC, TINT8): - //case CASE(ORROTC, TUINT8): - //case CASE(ORROTC, TINT16): - //case CASE(ORROTC, TUINT16): - //case CASE(ORROTC, TINT32): - //case CASE(ORROTC, TUINT32): - //case CASE(ORROTC, TINT64): - //case CASE(ORROTC, TUINT64): - // a = 0//??? RLDC?? - // break; - - case gc.OHMUL<<16 | gc.TINT64: - a = ppc64.AMULHD - - case gc.OHMUL<<16 | gc.TUINT64, - gc.OHMUL<<16 | gc.TPTR64: - a = ppc64.AMULHDU - - case gc.OMUL<<16 | gc.TINT8, - gc.OMUL<<16 | gc.TINT16, - gc.OMUL<<16 | gc.TINT32, - gc.OMUL<<16 | gc.TINT64: - a = ppc64.AMULLD - - case gc.OMUL<<16 | gc.TUINT8, - gc.OMUL<<16 | gc.TUINT16, - gc.OMUL<<16 | gc.TUINT32, - gc.OMUL<<16 | gc.TPTR32, - // don't use word multiply, the high 32-bit are undefined. - gc.OMUL<<16 | gc.TUINT64, - gc.OMUL<<16 | gc.TPTR64: - // for 64-bit multiplies, signedness doesn't matter. - a = ppc64.AMULLD - - case gc.OMUL<<16 | gc.TFLOAT32: - a = ppc64.AFMULS - - case gc.OMUL<<16 | gc.TFLOAT64: - a = ppc64.AFMUL - - case gc.ODIV<<16 | gc.TINT8, - gc.ODIV<<16 | gc.TINT16, - gc.ODIV<<16 | gc.TINT32, - gc.ODIV<<16 | gc.TINT64: - a = ppc64.ADIVD - - case gc.ODIV<<16 | gc.TUINT8, - gc.ODIV<<16 | gc.TUINT16, - gc.ODIV<<16 | gc.TUINT32, - gc.ODIV<<16 | gc.TPTR32, - gc.ODIV<<16 | gc.TUINT64, - gc.ODIV<<16 | gc.TPTR64: - a = ppc64.ADIVDU - - case gc.ODIV<<16 | gc.TFLOAT32: - a = ppc64.AFDIVS - - case gc.ODIV<<16 | gc.TFLOAT64: - a = ppc64.AFDIV - } - - return a -} - -const ( - ODynam = 1 << 0 - OAddable = 1 << 1 -) - -func xgen(n *gc.Node, a *gc.Node, o int) bool { - // TODO(minux) - - return -1 != 0 /*TypeKind(100016)*/ -} - -func sudoclean() { - return -} - -/* - * generate code to compute address of n, - * a reference to a (perhaps nested) field inside - * an array or struct. - * return 0 on failure, 1 on success. - * on success, leaves usable address in a. - * - * caller is responsible for calling sudoclean - * after successful sudoaddable, - * to release the register used for a. - */ -func sudoaddable(as int, n *gc.Node, a *obj.Addr) bool { - // TODO(minux) - - *a = obj.Addr{} - return false -} diff --git a/src/cmd/9g/opt.go b/src/cmd/9g/opt.go deleted file mode 100644 index 4a134f134f..0000000000 --- a/src/cmd/9g/opt.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2014 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 - -// Many Power ISA arithmetic and logical instructions come in four -// standard variants. These bits let us map between variants. -const ( - V_CC = 1 << 0 // xCC (affect CR field 0 flags) - V_V = 1 << 1 // xV (affect SO and OV flags) -) diff --git a/src/cmd/9g/peep.go b/src/cmd/9g/peep.go deleted file mode 100644 index 94c9b1554b..0000000000 --- a/src/cmd/9g/peep.go +++ /dev/null @@ -1,1051 +0,0 @@ -// Derived from Inferno utils/6c/peep.c -// http://code.google.com/p/inferno-os/source/browse/utils/6c/peep.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package main - -import ( - "cmd/internal/gc" - "cmd/internal/obj" - "cmd/internal/obj/ppc64" - "fmt" -) - -var gactive uint32 - -func peep(firstp *obj.Prog) { - g := (*gc.Graph)(gc.Flowstart(firstp, nil)) - if g == nil { - return - } - gactive = 0 - - var p *obj.Prog - var r *gc.Flow - var t int -loop1: - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - gc.Dumpit("loop1", g.Start, 0) - } - - t = 0 - for r = g.Start; r != nil; r = r.Link { - p = r.Prog - - // TODO(austin) Handle smaller moves. arm and amd64 - // distinguish between moves that moves that *must* - // sign/zero extend and moves that don't care so they - // can eliminate moves that don't care without - // breaking moves that do care. This might let us - // simplify or remove the next peep loop, too. - if p.As == ppc64.AMOVD || p.As == ppc64.AFMOVD { - if regtyp(&p.To) { - // Try to eliminate reg->reg moves - if regtyp(&p.From) { - if p.From.Type == p.To.Type { - if copyprop(r) { - excise(r) - t++ - } else if subprop(r) && copyprop(r) { - excise(r) - t++ - } - } - } - - // Convert uses to $0 to uses of R0 and - // propagate R0 - if regzer(&p.From) != 0 { - if p.To.Type == obj.TYPE_REG { - p.From.Type = obj.TYPE_REG - p.From.Reg = ppc64.REGZERO - if copyprop(r) { - excise(r) - t++ - } else if subprop(r) && copyprop(r) { - excise(r) - t++ - } - } - } - } - } - } - - if t != 0 { - goto loop1 - } - - /* - * look for MOVB x,R; MOVB R,R (for small MOVs not handled above) - */ - var p1 *obj.Prog - var r1 *gc.Flow - for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { - p = r.Prog - switch p.As { - default: - continue - - case ppc64.AMOVH, - ppc64.AMOVHZ, - ppc64.AMOVB, - ppc64.AMOVBZ, - ppc64.AMOVW, - ppc64.AMOVWZ: - if p.To.Type != obj.TYPE_REG { - continue - } - } - - r1 = r.Link - if r1 == nil { - continue - } - p1 = r1.Prog - if p1.As != p.As { - continue - } - if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg { - continue - } - if p1.To.Type != obj.TYPE_REG || p1.To.Reg != p.To.Reg { - continue - } - excise(r1) - } - - if gc.Debug['D'] > 1 { - goto ret /* allow following code improvement to be suppressed */ - } - - /* - * look for OP x,y,R; CMP R, $0 -> OPCC x,y,R - * when OP can set condition codes correctly - */ - for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { - p = r.Prog - switch p.As { - case ppc64.ACMP, - ppc64.ACMPW: /* always safe? */ - if regzer(&p.To) == 0 { - continue - } - r1 = r.S1 - if r1 == nil { - continue - } - switch r1.Prog.As { - default: - continue - - /* the conditions can be complex and these are currently little used */ - case ppc64.ABCL, - ppc64.ABC: - continue - - case ppc64.ABEQ, - ppc64.ABGE, - ppc64.ABGT, - ppc64.ABLE, - ppc64.ABLT, - ppc64.ABNE, - ppc64.ABVC, - ppc64.ABVS: - break - } - - r1 = r - for { - r1 = gc.Uniqp(r1) - if r1 == nil || r1.Prog.As != obj.ANOP { - break - } - } - - if r1 == nil { - continue - } - p1 = r1.Prog - if p1.To.Type != obj.TYPE_REG || p1.To.Reg != p.From.Reg { - continue - } - switch p1.As { - /* irregular instructions */ - case ppc64.ASUB, - ppc64.AADD, - ppc64.AXOR, - ppc64.AOR: - if p1.From.Type == obj.TYPE_CONST || p1.From.Type == obj.TYPE_ADDR { - continue - } - } - - switch p1.As { - default: - continue - - case ppc64.AMOVW, - ppc64.AMOVD: - if p1.From.Type != obj.TYPE_REG { - continue - } - continue - - case ppc64.AANDCC, - ppc64.AANDNCC, - ppc64.AORCC, - ppc64.AORNCC, - ppc64.AXORCC, - ppc64.ASUBCC, - ppc64.ASUBECC, - ppc64.ASUBMECC, - ppc64.ASUBZECC, - ppc64.AADDCC, - ppc64.AADDCCC, - ppc64.AADDECC, - ppc64.AADDMECC, - ppc64.AADDZECC, - ppc64.ARLWMICC, - ppc64.ARLWNMCC, - /* don't deal with floating point instructions for now */ - /* - case AFABS: - case AFADD: - case AFADDS: - case AFCTIW: - case AFCTIWZ: - case AFDIV: - case AFDIVS: - case AFMADD: - case AFMADDS: - case AFMOVD: - case AFMSUB: - case AFMSUBS: - case AFMUL: - case AFMULS: - case AFNABS: - case AFNEG: - case AFNMADD: - case AFNMADDS: - case AFNMSUB: - case AFNMSUBS: - case AFRSP: - case AFSUB: - case AFSUBS: - case ACNTLZW: - case AMTFSB0: - case AMTFSB1: - */ - ppc64.AADD, - ppc64.AADDV, - ppc64.AADDC, - ppc64.AADDCV, - ppc64.AADDME, - ppc64.AADDMEV, - ppc64.AADDE, - ppc64.AADDEV, - ppc64.AADDZE, - ppc64.AADDZEV, - ppc64.AAND, - ppc64.AANDN, - ppc64.ADIVW, - ppc64.ADIVWV, - ppc64.ADIVWU, - ppc64.ADIVWUV, - ppc64.ADIVD, - ppc64.ADIVDV, - ppc64.ADIVDU, - ppc64.ADIVDUV, - ppc64.AEQV, - ppc64.AEXTSB, - ppc64.AEXTSH, - ppc64.AEXTSW, - ppc64.AMULHW, - ppc64.AMULHWU, - ppc64.AMULLW, - ppc64.AMULLWV, - ppc64.AMULHD, - ppc64.AMULHDU, - ppc64.AMULLD, - ppc64.AMULLDV, - ppc64.ANAND, - ppc64.ANEG, - ppc64.ANEGV, - ppc64.ANOR, - ppc64.AOR, - ppc64.AORN, - ppc64.AREM, - ppc64.AREMV, - ppc64.AREMU, - ppc64.AREMUV, - ppc64.AREMD, - ppc64.AREMDV, - ppc64.AREMDU, - ppc64.AREMDUV, - ppc64.ARLWMI, - ppc64.ARLWNM, - ppc64.ASLW, - ppc64.ASRAW, - ppc64.ASRW, - ppc64.ASLD, - ppc64.ASRAD, - ppc64.ASRD, - ppc64.ASUB, - ppc64.ASUBV, - ppc64.ASUBC, - ppc64.ASUBCV, - ppc64.ASUBME, - ppc64.ASUBMEV, - ppc64.ASUBE, - ppc64.ASUBEV, - ppc64.ASUBZE, - ppc64.ASUBZEV, - ppc64.AXOR: - t = variant2as(int(p1.As), as2variant(int(p1.As))|V_CC) - } - - if gc.Debug['D'] != 0 { - fmt.Printf("cmp %v; %v -> ", p1, p) - } - p1.As = int16(t) - if gc.Debug['D'] != 0 { - fmt.Printf("%v\n", p1) - } - excise(r) - continue - } - } - -ret: - gc.Flowend(g) -} - -func excise(r *gc.Flow) { - p := (*obj.Prog)(r.Prog) - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("%v ===delete===\n", p) - } - obj.Nopout(p) - gc.Ostats.Ndelmov++ -} - -/* - * regzer returns 1 if a's value is 0 (a is R0 or $0) - */ -func regzer(a *obj.Addr) int { - if a.Type == obj.TYPE_CONST || a.Type == obj.TYPE_ADDR { - if a.Sym == nil && a.Reg == 0 { - if a.Offset == 0 { - return 1 - } - } - } - if a.Type == obj.TYPE_REG { - if a.Reg == ppc64.REGZERO { - return 1 - } - } - return 0 -} - -func regtyp(a *obj.Addr) bool { - // TODO(rsc): Floating point register exclusions? - return a.Type == obj.TYPE_REG && ppc64.REG_R0 <= a.Reg && a.Reg <= ppc64.REG_F31 && a.Reg != ppc64.REGZERO -} - -/* - * the idea is to substitute - * one register for another - * from one MOV to another - * MOV a, R1 - * ADD b, R1 / no use of R2 - * MOV R1, R2 - * would be converted to - * MOV a, R2 - * ADD b, R2 - * MOV R2, R1 - * hopefully, then the former or latter MOV - * will be eliminated by copy propagation. - * - * r0 (the argument, not the register) is the MOV at the end of the - * above sequences. This returns 1 if it modified any instructions. - */ -func subprop(r0 *gc.Flow) bool { - p := (*obj.Prog)(r0.Prog) - v1 := (*obj.Addr)(&p.From) - if !regtyp(v1) { - return false - } - v2 := (*obj.Addr)(&p.To) - if !regtyp(v2) { - return false - } - for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { - if gc.Uniqs(r) == nil { - break - } - p = r.Prog - if p.As == obj.AVARDEF || p.As == obj.AVARKILL { - continue - } - if p.Info.Flags&gc.Call != 0 { - return false - } - - if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightWrite { - if p.To.Type == v1.Type { - if p.To.Reg == v1.Reg { - copysub(&p.To, v1, v2, 1) - if gc.Debug['P'] != 0 { - fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog) - if p.From.Type == v2.Type { - fmt.Printf(" excise") - } - fmt.Printf("\n") - } - - for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { - p = r.Prog - copysub(&p.From, v1, v2, 1) - copysub1(p, v1, v2, 1) - copysub(&p.To, v1, v2, 1) - if gc.Debug['P'] != 0 { - fmt.Printf("%v\n", r.Prog) - } - } - - t := int(int(v1.Reg)) - v1.Reg = v2.Reg - v2.Reg = int16(t) - if gc.Debug['P'] != 0 { - fmt.Printf("%v last\n", r.Prog) - } - return true - } - } - } - - if copyau(&p.From, v2) || copyau1(p, v2) || copyau(&p.To, v2) { - break - } - if copysub(&p.From, v1, v2, 0) != 0 || copysub1(p, v1, v2, 0) != 0 || copysub(&p.To, v1, v2, 0) != 0 { - break - } - } - - return false -} - -/* - * The idea is to remove redundant copies. - * v1->v2 F=0 - * (use v2 s/v2/v1/)* - * set v1 F=1 - * use v2 return fail (v1->v2 move must remain) - * ----------------- - * v1->v2 F=0 - * (use v2 s/v2/v1/)* - * set v1 F=1 - * set v2 return success (caller can remove v1->v2 move) - */ -func copyprop(r0 *gc.Flow) bool { - p := (*obj.Prog)(r0.Prog) - v1 := (*obj.Addr)(&p.From) - v2 := (*obj.Addr)(&p.To) - if copyas(v1, v2) { - if gc.Debug['P'] != 0 { - fmt.Printf("eliminating self-move: %v\n", r0.Prog) - } - return true - } - - gactive++ - if gc.Debug['P'] != 0 { - fmt.Printf("trying to eliminate %v->%v move from:\n%v\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r0.Prog) - } - return copy1(v1, v2, r0.S1, 0) -} - -// copy1 replaces uses of v2 with v1 starting at r and returns 1 if -// all uses were rewritten. -func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f int) bool { - if uint32(r.Active) == gactive { - if gc.Debug['P'] != 0 { - fmt.Printf("act set; return 1\n") - } - return true - } - - r.Active = int32(gactive) - if gc.Debug['P'] != 0 { - fmt.Printf("copy1 replace %v with %v f=%d\n", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), f) - } - var t int - var p *obj.Prog - for ; r != nil; r = r.S1 { - p = r.Prog - if gc.Debug['P'] != 0 { - fmt.Printf("%v", p) - } - if f == 0 && gc.Uniqp(r) == nil { - // Multiple predecessors; conservatively - // assume v1 was set on other path - f = 1 - - if gc.Debug['P'] != 0 { - fmt.Printf("; merge; f=%d", f) - } - } - - t = copyu(p, v2, nil) - switch t { - case 2: /* rar, can't split */ - if gc.Debug['P'] != 0 { - fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2)) - } - return false - - case 3: /* set */ - if gc.Debug['P'] != 0 { - fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2)) - } - return true - - case 1, /* used, substitute */ - 4: /* use and set */ - if f != 0 { - if gc.Debug['P'] == 0 { - return false - } - if t == 4 { - fmt.Printf("; %v used+set and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f) - } else { - fmt.Printf("; %v used and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f) - } - return false - } - - if copyu(p, v2, v1) != 0 { - if gc.Debug['P'] != 0 { - fmt.Printf("; sub fail; return 0\n") - } - return false - } - - if gc.Debug['P'] != 0 { - fmt.Printf("; sub %v->%v\n => %v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), p) - } - if t == 4 { - if gc.Debug['P'] != 0 { - fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2)) - } - return true - } - } - - if f == 0 { - t = copyu(p, v1, nil) - if f == 0 && (t == 2 || t == 3 || t == 4) { - f = 1 - if gc.Debug['P'] != 0 { - fmt.Printf("; %v set and !f; f=%d", gc.Ctxt.Dconv(v1), f) - } - } - } - - if gc.Debug['P'] != 0 { - fmt.Printf("\n") - } - if r.S2 != nil { - if !copy1(v1, v2, r.S2, f) { - return false - } - } - } - - return true -} - -// If s==nil, copyu returns the set/use of v in p; otherwise, it -// modifies p to replace reads of v with reads of s and returns 0 for -// success or non-zero for failure. -// -// If s==nil, copy returns one of the following values: -// 1 if v only used -// 2 if v is set and used in one address (read-alter-rewrite; -// can't substitute) -// 3 if v is only set -// 4 if v is set in one address and used in another (so addresses -// can be rewritten independently) -// 0 otherwise (not touched) -func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int { - if p.From3.Type != obj.TYPE_NONE { - // 9g never generates a from3 - fmt.Printf("copyu: from3 (%v) not implemented\n", gc.Ctxt.Dconv(&p.From3)) - } - - switch p.As { - default: - fmt.Printf("copyu: can't find %v\n", obj.Aconv(int(p.As))) - return 2 - - case obj.ANOP, /* read p->from, write p->to */ - ppc64.AMOVH, - ppc64.AMOVHZ, - ppc64.AMOVB, - ppc64.AMOVBZ, - ppc64.AMOVW, - ppc64.AMOVWZ, - ppc64.AMOVD, - ppc64.ANEG, - ppc64.ANEGCC, - ppc64.AADDME, - ppc64.AADDMECC, - ppc64.AADDZE, - ppc64.AADDZECC, - ppc64.ASUBME, - ppc64.ASUBMECC, - ppc64.ASUBZE, - ppc64.ASUBZECC, - ppc64.AFCTIW, - ppc64.AFCTIWZ, - ppc64.AFCTID, - ppc64.AFCTIDZ, - ppc64.AFCFID, - ppc64.AFCFIDCC, - ppc64.AFMOVS, - ppc64.AFMOVD, - ppc64.AFRSP, - ppc64.AFNEG, - ppc64.AFNEGCC: - if s != nil { - if copysub(&p.From, v, s, 1) != 0 { - return 1 - } - - // Update only indirect uses of v in p->to - if !copyas(&p.To, v) { - if copysub(&p.To, v, s, 1) != 0 { - return 1 - } - } - return 0 - } - - if copyas(&p.To, v) { - // Fix up implicit from - if p.From.Type == obj.TYPE_NONE { - p.From = p.To - } - if copyau(&p.From, v) { - return 4 - } - return 3 - } - - if copyau(&p.From, v) { - return 1 - } - if copyau(&p.To, v) { - // p->to only indirectly uses v - return 1 - } - - return 0 - - case ppc64.AMOVBU, /* rar p->from, write p->to or read p->from, rar p->to */ - ppc64.AMOVBZU, - ppc64.AMOVHU, - ppc64.AMOVHZU, - ppc64.AMOVWZU, - ppc64.AMOVDU: - if p.From.Type == obj.TYPE_MEM { - if copyas(&p.From, v) { - // No s!=nil check; need to fail - // anyway in that case - return 2 - } - - if s != nil { - if copysub(&p.To, v, s, 1) != 0 { - return 1 - } - return 0 - } - - if copyas(&p.To, v) { - return 3 - } - } else if p.To.Type == obj.TYPE_MEM { - if copyas(&p.To, v) { - return 2 - } - if s != nil { - if copysub(&p.From, v, s, 1) != 0 { - return 1 - } - return 0 - } - - if copyau(&p.From, v) { - return 1 - } - } else { - fmt.Printf("copyu: bad %v\n", p) - } - - return 0 - - case ppc64.ARLWMI, /* read p->from, read p->reg, rar p->to */ - ppc64.ARLWMICC: - if copyas(&p.To, v) { - return 2 - } - fallthrough - - /* fall through */ - case ppc64.AADD, - /* read p->from, read p->reg, write p->to */ - ppc64.AADDC, - ppc64.AADDE, - ppc64.ASUB, - ppc64.ASLW, - ppc64.ASRW, - ppc64.ASRAW, - ppc64.ASLD, - ppc64.ASRD, - ppc64.ASRAD, - ppc64.AOR, - ppc64.AORCC, - ppc64.AORN, - ppc64.AORNCC, - ppc64.AAND, - ppc64.AANDCC, - ppc64.AANDN, - ppc64.AANDNCC, - ppc64.ANAND, - ppc64.ANANDCC, - ppc64.ANOR, - ppc64.ANORCC, - ppc64.AXOR, - ppc64.AMULHW, - ppc64.AMULHWU, - ppc64.AMULLW, - ppc64.AMULLD, - ppc64.ADIVW, - ppc64.ADIVD, - ppc64.ADIVWU, - ppc64.ADIVDU, - ppc64.AREM, - ppc64.AREMU, - ppc64.AREMD, - ppc64.AREMDU, - ppc64.ARLWNM, - ppc64.ARLWNMCC, - ppc64.AFADDS, - ppc64.AFADD, - ppc64.AFSUBS, - ppc64.AFSUB, - ppc64.AFMULS, - ppc64.AFMUL, - ppc64.AFDIVS, - ppc64.AFDIV: - if s != nil { - if copysub(&p.From, v, s, 1) != 0 { - return 1 - } - if copysub1(p, v, s, 1) != 0 { - return 1 - } - - // Update only indirect uses of v in p->to - if !copyas(&p.To, v) { - if copysub(&p.To, v, s, 1) != 0 { - return 1 - } - } - return 0 - } - - if copyas(&p.To, v) { - if p.Reg == 0 { - // Fix up implicit reg (e.g., ADD - // R3,R4 -> ADD R3,R4,R4) so we can - // update reg and to separately. - p.Reg = p.To.Reg - } - - if copyau(&p.From, v) { - return 4 - } - if copyau1(p, v) { - return 4 - } - return 3 - } - - if copyau(&p.From, v) { - return 1 - } - if copyau1(p, v) { - return 1 - } - if copyau(&p.To, v) { - return 1 - } - return 0 - - case ppc64.ABEQ, - ppc64.ABGT, - ppc64.ABGE, - ppc64.ABLT, - ppc64.ABLE, - ppc64.ABNE, - ppc64.ABVC, - ppc64.ABVS: - return 0 - - case obj.ACHECKNIL, /* read p->from */ - ppc64.ACMP, /* read p->from, read p->to */ - ppc64.ACMPU, - ppc64.ACMPW, - ppc64.ACMPWU, - ppc64.AFCMPO, - ppc64.AFCMPU: - if s != nil { - if copysub(&p.From, v, s, 1) != 0 { - return 1 - } - return copysub(&p.To, v, s, 1) - } - - if copyau(&p.From, v) { - return 1 - } - if copyau(&p.To, v) { - return 1 - } - return 0 - - // 9g never generates a branch to a GPR (this isn't - // even a normal instruction; liblink turns it in to a - // mov and a branch). - case ppc64.ABR: /* read p->to */ - if s != nil { - if copysub(&p.To, v, s, 1) != 0 { - return 1 - } - return 0 - } - - if copyau(&p.To, v) { - return 1 - } - return 0 - - case ppc64.ARETURN: /* funny */ - if s != nil { - return 0 - } - - // All registers die at this point, so claim - // everything is set (and not used). - return 3 - - case ppc64.ABL: /* funny */ - if v.Type == obj.TYPE_REG { - // TODO(rsc): REG_R0 and REG_F0 used to be - // (when register numbers started at 0) exregoffset and exfregoffset, - // which are unset entirely. - // It's strange that this handles R0 and F0 differently from the other - // registers. Possible failure to optimize? - if ppc64.REG_R0 < v.Reg && v.Reg <= ppc64.REGEXT { - return 2 - } - if v.Reg == ppc64.REGARG { - return 2 - } - if ppc64.REG_F0 < v.Reg && v.Reg <= ppc64.FREGEXT { - return 2 - } - } - - if p.From.Type == obj.TYPE_REG && v.Type == obj.TYPE_REG && p.From.Reg == v.Reg { - return 2 - } - - if s != nil { - if copysub(&p.To, v, s, 1) != 0 { - return 1 - } - return 0 - } - - if copyau(&p.To, v) { - return 4 - } - return 3 - - // R0 is zero, used by DUFFZERO, cannot be substituted. - // R3 is ptr to memory, used and set, cannot be substituted. - case obj.ADUFFZERO: - if v.Type == obj.TYPE_REG { - if v.Reg == 0 { - return 1 - } - if v.Reg == 3 { - return 2 - } - } - - return 0 - - // R3, R4 are ptr to src, dst, used and set, cannot be substituted. - // R5 is scratch, set by DUFFCOPY, cannot be substituted. - case obj.ADUFFCOPY: - if v.Type == obj.TYPE_REG { - if v.Reg == 3 || v.Reg == 4 { - return 2 - } - if v.Reg == 5 { - return 3 - } - } - - return 0 - - case obj.ATEXT: /* funny */ - if v.Type == obj.TYPE_REG { - if v.Reg == ppc64.REGARG { - return 3 - } - } - return 0 - - case obj.APCDATA, - obj.AFUNCDATA, - obj.AVARDEF, - obj.AVARKILL: - return 0 - } -} - -// copyas returns 1 if a and v address the same register. -// -// If a is the from operand, this means this operation reads the -// register in v. If a is the to operand, this means this operation -// writes the register in v. -func copyas(a *obj.Addr, v *obj.Addr) bool { - if regtyp(v) { - if a.Type == v.Type { - if a.Reg == v.Reg { - return true - } - } - } - return false -} - -// copyau returns 1 if a either directly or indirectly addresses the -// same register as v. -// -// If a is the from operand, this means this operation reads the -// register in v. If a is the to operand, this means the operation -// either reads or writes the register in v (if !copyas(a, v), then -// the operation reads the register in v). -func copyau(a *obj.Addr, v *obj.Addr) bool { - if copyas(a, v) { - return true - } - if v.Type == obj.TYPE_REG { - if a.Type == obj.TYPE_MEM || (a.Type == obj.TYPE_ADDR && a.Reg != 0) { - if v.Reg == a.Reg { - return true - } - } - } - return false -} - -// copyau1 returns 1 if p->reg references the same register as v and v -// is a direct reference. -func copyau1(p *obj.Prog, v *obj.Addr) bool { - if regtyp(v) && v.Reg != 0 { - if p.Reg == v.Reg { - return true - } - } - return false -} - -// copysub replaces v with s in a if f!=0 or indicates it if could if f==0. -// Returns 1 on failure to substitute (it always succeeds on ppc64). -func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f int) int { - if f != 0 { - if copyau(a, v) { - a.Reg = s.Reg - } - } - return 0 -} - -// copysub1 replaces v with s in p1->reg if f!=0 or indicates if it could if f==0. -// Returns 1 on failure to substitute (it always succeeds on ppc64). -func copysub1(p1 *obj.Prog, v *obj.Addr, s *obj.Addr, f int) int { - if f != 0 { - if copyau1(p1, v) { - p1.Reg = s.Reg - } - } - return 0 -} - -func sameaddr(a *obj.Addr, v *obj.Addr) bool { - if a.Type != v.Type { - return false - } - if regtyp(v) && a.Reg == v.Reg { - return true - } - if v.Type == obj.NAME_AUTO || v.Type == obj.NAME_PARAM { - if v.Offset == a.Offset { - return true - } - } - return false -} - -func smallindir(a *obj.Addr, reg *obj.Addr) bool { - return reg.Type == obj.TYPE_REG && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && 0 <= a.Offset && a.Offset < 4096 -} - -func stackaddr(a *obj.Addr) bool { - return a.Type == obj.TYPE_REG && a.Reg == ppc64.REGSP -} diff --git a/src/cmd/9g/prog.go b/src/cmd/9g/prog.go deleted file mode 100644 index e28e389fac..0000000000 --- a/src/cmd/9g/prog.go +++ /dev/null @@ -1,314 +0,0 @@ -// Copyright 2014 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/gc" - "cmd/internal/obj" - "cmd/internal/obj/ppc64" -) - -const ( - LeftRdwr uint32 = gc.LeftRead | gc.LeftWrite - RightRdwr uint32 = gc.RightRead | gc.RightWrite -) - -// This table gives the basic information about instruction -// generated by the compiler and processed in the optimizer. -// See opt.h for bit definitions. -// -// Instructions not generated need not be listed. -// As an exception to that rule, we typically write down all the -// size variants of an operation even if we just use a subset. -// -// The table is formatted for 8-space tabs. -var progtable = [ppc64.ALAST]obj.ProgInfo{ - obj.ATYPE: {gc.Pseudo | gc.Skip, 0, 0, 0}, - obj.ATEXT: {gc.Pseudo, 0, 0, 0}, - obj.AFUNCDATA: {gc.Pseudo, 0, 0, 0}, - obj.APCDATA: {gc.Pseudo, 0, 0, 0}, - obj.AUNDEF: {gc.Break, 0, 0, 0}, - obj.AUSEFIELD: {gc.OK, 0, 0, 0}, - obj.ACHECKNIL: {gc.LeftRead, 0, 0, 0}, - obj.AVARDEF: {gc.Pseudo | gc.RightWrite, 0, 0, 0}, - obj.AVARKILL: {gc.Pseudo | gc.RightWrite, 0, 0, 0}, - - // NOP is an internal no-op that also stands - // for USED and SET annotations, not the Power opcode. - obj.ANOP: {gc.LeftRead | gc.RightWrite, 0, 0, 0}, - - // Integer - ppc64.AADD: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - ppc64.ASUB: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - ppc64.ANEG: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - ppc64.AAND: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - ppc64.AOR: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - ppc64.AXOR: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - ppc64.AMULLD: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - ppc64.AMULLW: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - ppc64.AMULHD: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - ppc64.AMULHDU: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - ppc64.ADIVD: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - ppc64.ADIVDU: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - ppc64.ASLD: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - ppc64.ASRD: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - ppc64.ASRAD: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - ppc64.ACMP: {gc.SizeQ | gc.LeftRead | gc.RightRead, 0, 0, 0}, - ppc64.ACMPU: {gc.SizeQ | gc.LeftRead | gc.RightRead, 0, 0, 0}, - ppc64.ATD: {gc.SizeQ | gc.RightRead, 0, 0, 0}, - - // Floating point. - ppc64.AFADD: {gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - ppc64.AFADDS: {gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - ppc64.AFSUB: {gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - ppc64.AFSUBS: {gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - ppc64.AFMUL: {gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - ppc64.AFMULS: {gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - ppc64.AFDIV: {gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - ppc64.AFDIVS: {gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - ppc64.AFCTIDZ: {gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - ppc64.AFCFID: {gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, - ppc64.AFCMPU: {gc.SizeD | gc.LeftRead | gc.RightRead, 0, 0, 0}, - ppc64.AFRSP: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, - - // Moves - ppc64.AMOVB: {gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv, 0, 0, 0}, - ppc64.AMOVBU: {gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv | gc.PostInc, 0, 0, 0}, - ppc64.AMOVBZ: {gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv, 0, 0, 0}, - ppc64.AMOVH: {gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv, 0, 0, 0}, - ppc64.AMOVHU: {gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv | gc.PostInc, 0, 0, 0}, - ppc64.AMOVHZ: {gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv, 0, 0, 0}, - ppc64.AMOVW: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv, 0, 0, 0}, - - // there is no AMOVWU. - ppc64.AMOVWZU: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv | gc.PostInc, 0, 0, 0}, - ppc64.AMOVWZ: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv, 0, 0, 0}, - ppc64.AMOVD: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, - ppc64.AMOVDU: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move | gc.PostInc, 0, 0, 0}, - ppc64.AFMOVS: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv, 0, 0, 0}, - ppc64.AFMOVD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, - - // Jumps - ppc64.ABR: {gc.Jump | gc.Break, 0, 0, 0}, - ppc64.ABL: {gc.Call, 0, 0, 0}, - ppc64.ABEQ: {gc.Cjmp, 0, 0, 0}, - ppc64.ABNE: {gc.Cjmp, 0, 0, 0}, - ppc64.ABGE: {gc.Cjmp, 0, 0, 0}, - ppc64.ABLT: {gc.Cjmp, 0, 0, 0}, - ppc64.ABGT: {gc.Cjmp, 0, 0, 0}, - ppc64.ABLE: {gc.Cjmp, 0, 0, 0}, - ppc64.ARETURN: {gc.Break, 0, 0, 0}, - obj.ADUFFZERO: {gc.Call, 0, 0, 0}, - obj.ADUFFCOPY: {gc.Call, 0, 0, 0}, -} - -var initproginfo_initialized int - -func initproginfo() { - var addvariant = []int{V_CC, V_V, V_CC | V_V} - - if initproginfo_initialized != 0 { - return - } - initproginfo_initialized = 1 - - // Perform one-time expansion of instructions in progtable to - // their CC, V, and VCC variants - var as2 int - var i int - var variant int - for as := int(0); as < len(progtable); as++ { - if progtable[as].Flags == 0 { - continue - } - variant = as2variant(as) - for i = 0; i < len(addvariant); i++ { - as2 = variant2as(as, variant|addvariant[i]) - if as2 != 0 && progtable[as2].Flags == 0 { - progtable[as2] = progtable[as] - } - } - } -} - -func proginfo(p *obj.Prog) { - initproginfo() - - info := &p.Info - *info = progtable[p.As] - if info.Flags == 0 { - gc.Fatal("proginfo: unknown instruction %v", p) - } - - if (info.Flags&gc.RegRead != 0) && p.Reg == 0 { - info.Flags &^= gc.RegRead - info.Flags |= gc.RightRead /*CanRegRead |*/ - } - - if (p.From.Type == obj.TYPE_MEM || p.From.Type == obj.TYPE_ADDR) && p.From.Reg != 0 { - info.Regindex |= RtoB(int(p.From.Reg)) - if info.Flags&gc.PostInc != 0 { - info.Regset |= RtoB(int(p.From.Reg)) - } - } - - if (p.To.Type == obj.TYPE_MEM || p.To.Type == obj.TYPE_ADDR) && p.To.Reg != 0 { - info.Regindex |= RtoB(int(p.To.Reg)) - if info.Flags&gc.PostInc != 0 { - info.Regset |= RtoB(int(p.To.Reg)) - } - } - - if p.From.Type == obj.TYPE_ADDR && p.From.Sym != nil && (info.Flags&gc.LeftRead != 0) { - info.Flags &^= gc.LeftRead - info.Flags |= gc.LeftAddr - } - - if p.As == obj.ADUFFZERO { - info.Reguse |= 1<<0 | RtoB(ppc64.REG_R3) - info.Regset |= RtoB(ppc64.REG_R3) - } - - if p.As == obj.ADUFFCOPY { - // TODO(austin) Revisit when duffcopy is implemented - info.Reguse |= RtoB(ppc64.REG_R3) | RtoB(ppc64.REG_R4) | RtoB(ppc64.REG_R5) - - info.Regset |= RtoB(ppc64.REG_R3) | RtoB(ppc64.REG_R4) - } -} - -// Instruction variants table. Initially this contains entries only -// for the "base" form of each instruction. On the first call to -// as2variant or variant2as, we'll add the variants to the table. -var varianttable = [ppc64.ALAST][4]int{ - ppc64.AADD: [4]int{ppc64.AADD, ppc64.AADDCC, ppc64.AADDV, ppc64.AADDVCC}, - ppc64.AADDC: [4]int{ppc64.AADDC, ppc64.AADDCCC, ppc64.AADDCV, ppc64.AADDCVCC}, - ppc64.AADDE: [4]int{ppc64.AADDE, ppc64.AADDECC, ppc64.AADDEV, ppc64.AADDEVCC}, - ppc64.AADDME: [4]int{ppc64.AADDME, ppc64.AADDMECC, ppc64.AADDMEV, ppc64.AADDMEVCC}, - ppc64.AADDZE: [4]int{ppc64.AADDZE, ppc64.AADDZECC, ppc64.AADDZEV, ppc64.AADDZEVCC}, - ppc64.AAND: [4]int{ppc64.AAND, ppc64.AANDCC, 0, 0}, - ppc64.AANDN: [4]int{ppc64.AANDN, ppc64.AANDNCC, 0, 0}, - ppc64.ACNTLZD: [4]int{ppc64.ACNTLZD, ppc64.ACNTLZDCC, 0, 0}, - ppc64.ACNTLZW: [4]int{ppc64.ACNTLZW, ppc64.ACNTLZWCC, 0, 0}, - ppc64.ADIVD: [4]int{ppc64.ADIVD, ppc64.ADIVDCC, ppc64.ADIVDV, ppc64.ADIVDVCC}, - ppc64.ADIVDU: [4]int{ppc64.ADIVDU, ppc64.ADIVDUCC, ppc64.ADIVDUV, ppc64.ADIVDUVCC}, - ppc64.ADIVW: [4]int{ppc64.ADIVW, ppc64.ADIVWCC, ppc64.ADIVWV, ppc64.ADIVWVCC}, - ppc64.ADIVWU: [4]int{ppc64.ADIVWU, ppc64.ADIVWUCC, ppc64.ADIVWUV, ppc64.ADIVWUVCC}, - ppc64.AEQV: [4]int{ppc64.AEQV, ppc64.AEQVCC, 0, 0}, - ppc64.AEXTSB: [4]int{ppc64.AEXTSB, ppc64.AEXTSBCC, 0, 0}, - ppc64.AEXTSH: [4]int{ppc64.AEXTSH, ppc64.AEXTSHCC, 0, 0}, - ppc64.AEXTSW: [4]int{ppc64.AEXTSW, ppc64.AEXTSWCC, 0, 0}, - ppc64.AFABS: [4]int{ppc64.AFABS, ppc64.AFABSCC, 0, 0}, - ppc64.AFADD: [4]int{ppc64.AFADD, ppc64.AFADDCC, 0, 0}, - ppc64.AFADDS: [4]int{ppc64.AFADDS, ppc64.AFADDSCC, 0, 0}, - ppc64.AFCFID: [4]int{ppc64.AFCFID, ppc64.AFCFIDCC, 0, 0}, - ppc64.AFCTID: [4]int{ppc64.AFCTID, ppc64.AFCTIDCC, 0, 0}, - ppc64.AFCTIDZ: [4]int{ppc64.AFCTIDZ, ppc64.AFCTIDZCC, 0, 0}, - ppc64.AFCTIW: [4]int{ppc64.AFCTIW, ppc64.AFCTIWCC, 0, 0}, - ppc64.AFCTIWZ: [4]int{ppc64.AFCTIWZ, ppc64.AFCTIWZCC, 0, 0}, - ppc64.AFDIV: [4]int{ppc64.AFDIV, ppc64.AFDIVCC, 0, 0}, - ppc64.AFDIVS: [4]int{ppc64.AFDIVS, ppc64.AFDIVSCC, 0, 0}, - ppc64.AFMADD: [4]int{ppc64.AFMADD, ppc64.AFMADDCC, 0, 0}, - ppc64.AFMADDS: [4]int{ppc64.AFMADDS, ppc64.AFMADDSCC, 0, 0}, - ppc64.AFMOVD: [4]int{ppc64.AFMOVD, ppc64.AFMOVDCC, 0, 0}, - ppc64.AFMSUB: [4]int{ppc64.AFMSUB, ppc64.AFMSUBCC, 0, 0}, - ppc64.AFMSUBS: [4]int{ppc64.AFMSUBS, ppc64.AFMSUBSCC, 0, 0}, - ppc64.AFMUL: [4]int{ppc64.AFMUL, ppc64.AFMULCC, 0, 0}, - ppc64.AFMULS: [4]int{ppc64.AFMULS, ppc64.AFMULSCC, 0, 0}, - ppc64.AFNABS: [4]int{ppc64.AFNABS, ppc64.AFNABSCC, 0, 0}, - ppc64.AFNEG: [4]int{ppc64.AFNEG, ppc64.AFNEGCC, 0, 0}, - ppc64.AFNMADD: [4]int{ppc64.AFNMADD, ppc64.AFNMADDCC, 0, 0}, - ppc64.AFNMADDS: [4]int{ppc64.AFNMADDS, ppc64.AFNMADDSCC, 0, 0}, - ppc64.AFNMSUB: [4]int{ppc64.AFNMSUB, ppc64.AFNMSUBCC, 0, 0}, - ppc64.AFNMSUBS: [4]int{ppc64.AFNMSUBS, ppc64.AFNMSUBSCC, 0, 0}, - ppc64.AFRES: [4]int{ppc64.AFRES, ppc64.AFRESCC, 0, 0}, - ppc64.AFRSP: [4]int{ppc64.AFRSP, ppc64.AFRSPCC, 0, 0}, - ppc64.AFRSQRTE: [4]int{ppc64.AFRSQRTE, ppc64.AFRSQRTECC, 0, 0}, - ppc64.AFSEL: [4]int{ppc64.AFSEL, ppc64.AFSELCC, 0, 0}, - ppc64.AFSQRT: [4]int{ppc64.AFSQRT, ppc64.AFSQRTCC, 0, 0}, - ppc64.AFSQRTS: [4]int{ppc64.AFSQRTS, ppc64.AFSQRTSCC, 0, 0}, - ppc64.AFSUB: [4]int{ppc64.AFSUB, ppc64.AFSUBCC, 0, 0}, - ppc64.AFSUBS: [4]int{ppc64.AFSUBS, ppc64.AFSUBSCC, 0, 0}, - ppc64.AMTFSB0: [4]int{ppc64.AMTFSB0, ppc64.AMTFSB0CC, 0, 0}, - ppc64.AMTFSB1: [4]int{ppc64.AMTFSB1, ppc64.AMTFSB1CC, 0, 0}, - ppc64.AMULHD: [4]int{ppc64.AMULHD, ppc64.AMULHDCC, 0, 0}, - ppc64.AMULHDU: [4]int{ppc64.AMULHDU, ppc64.AMULHDUCC, 0, 0}, - ppc64.AMULHW: [4]int{ppc64.AMULHW, ppc64.AMULHWCC, 0, 0}, - ppc64.AMULHWU: [4]int{ppc64.AMULHWU, ppc64.AMULHWUCC, 0, 0}, - ppc64.AMULLD: [4]int{ppc64.AMULLD, ppc64.AMULLDCC, ppc64.AMULLDV, ppc64.AMULLDVCC}, - ppc64.AMULLW: [4]int{ppc64.AMULLW, ppc64.AMULLWCC, ppc64.AMULLWV, ppc64.AMULLWVCC}, - ppc64.ANAND: [4]int{ppc64.ANAND, ppc64.ANANDCC, 0, 0}, - ppc64.ANEG: [4]int{ppc64.ANEG, ppc64.ANEGCC, ppc64.ANEGV, ppc64.ANEGVCC}, - ppc64.ANOR: [4]int{ppc64.ANOR, ppc64.ANORCC, 0, 0}, - ppc64.AOR: [4]int{ppc64.AOR, ppc64.AORCC, 0, 0}, - ppc64.AORN: [4]int{ppc64.AORN, ppc64.AORNCC, 0, 0}, - ppc64.AREM: [4]int{ppc64.AREM, ppc64.AREMCC, ppc64.AREMV, ppc64.AREMVCC}, - ppc64.AREMD: [4]int{ppc64.AREMD, ppc64.AREMDCC, ppc64.AREMDV, ppc64.AREMDVCC}, - ppc64.AREMDU: [4]int{ppc64.AREMDU, ppc64.AREMDUCC, ppc64.AREMDUV, ppc64.AREMDUVCC}, - ppc64.AREMU: [4]int{ppc64.AREMU, ppc64.AREMUCC, ppc64.AREMUV, ppc64.AREMUVCC}, - ppc64.ARLDC: [4]int{ppc64.ARLDC, ppc64.ARLDCCC, 0, 0}, - ppc64.ARLDCL: [4]int{ppc64.ARLDCL, ppc64.ARLDCLCC, 0, 0}, - ppc64.ARLDCR: [4]int{ppc64.ARLDCR, ppc64.ARLDCRCC, 0, 0}, - ppc64.ARLDMI: [4]int{ppc64.ARLDMI, ppc64.ARLDMICC, 0, 0}, - ppc64.ARLWMI: [4]int{ppc64.ARLWMI, ppc64.ARLWMICC, 0, 0}, - ppc64.ARLWNM: [4]int{ppc64.ARLWNM, ppc64.ARLWNMCC, 0, 0}, - ppc64.ASLD: [4]int{ppc64.ASLD, ppc64.ASLDCC, 0, 0}, - ppc64.ASLW: [4]int{ppc64.ASLW, ppc64.ASLWCC, 0, 0}, - ppc64.ASRAD: [4]int{ppc64.ASRAD, ppc64.ASRADCC, 0, 0}, - ppc64.ASRAW: [4]int{ppc64.ASRAW, ppc64.ASRAWCC, 0, 0}, - ppc64.ASRD: [4]int{ppc64.ASRD, ppc64.ASRDCC, 0, 0}, - ppc64.ASRW: [4]int{ppc64.ASRW, ppc64.ASRWCC, 0, 0}, - ppc64.ASUB: [4]int{ppc64.ASUB, ppc64.ASUBCC, ppc64.ASUBV, ppc64.ASUBVCC}, - ppc64.ASUBC: [4]int{ppc64.ASUBC, ppc64.ASUBCCC, ppc64.ASUBCV, ppc64.ASUBCVCC}, - ppc64.ASUBE: [4]int{ppc64.ASUBE, ppc64.ASUBECC, ppc64.ASUBEV, ppc64.ASUBEVCC}, - ppc64.ASUBME: [4]int{ppc64.ASUBME, ppc64.ASUBMECC, ppc64.ASUBMEV, ppc64.ASUBMEVCC}, - ppc64.ASUBZE: [4]int{ppc64.ASUBZE, ppc64.ASUBZECC, ppc64.ASUBZEV, ppc64.ASUBZEVCC}, - ppc64.AXOR: [4]int{ppc64.AXOR, ppc64.AXORCC, 0, 0}, -} - -var initvariants_initialized int - -func initvariants() { - if initvariants_initialized != 0 { - return - } - initvariants_initialized = 1 - - var j int - for i := int(0); i < len(varianttable); i++ { - if varianttable[i][0] == 0 { - // Instruction has no variants - varianttable[i][0] = i - - continue - } - - // Copy base form to other variants - if varianttable[i][0] == i { - for j = 0; j < len(varianttable[i]); j++ { - varianttable[varianttable[i][j]] = varianttable[i] - } - } - } -} - -// as2variant returns the variant (V_*) flags of instruction as. -func as2variant(as int) int { - initvariants() - for i := int(0); i < len(varianttable[as]); i++ { - if varianttable[as][i] == as { - return i - } - } - gc.Fatal("as2variant: instruction %v is not a variant of itself", obj.Aconv(as)) - return 0 -} - -// variant2as returns the instruction as with the given variant (V_*) flags. -// If no such variant exists, this returns 0. -func variant2as(as int, flags int) int { - initvariants() - return varianttable[as][flags] -} diff --git a/src/cmd/9g/reg.go b/src/cmd/9g/reg.go deleted file mode 100644 index fb0c2e37ec..0000000000 --- a/src/cmd/9g/reg.go +++ /dev/null @@ -1,162 +0,0 @@ -// Derived from Inferno utils/6c/reg.c -// http://code.google.com/p/inferno-os/source/browse/utils/6c/reg.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package main - -import "cmd/internal/obj/ppc64" -import "cmd/internal/gc" - -const ( - NREGVAR = 64 /* 32 general + 32 floating */ -) - -var regname = []string{ - ".R0", - ".R1", - ".R2", - ".R3", - ".R4", - ".R5", - ".R6", - ".R7", - ".R8", - ".R9", - ".R10", - ".R11", - ".R12", - ".R13", - ".R14", - ".R15", - ".R16", - ".R17", - ".R18", - ".R19", - ".R20", - ".R21", - ".R22", - ".R23", - ".R24", - ".R25", - ".R26", - ".R27", - ".R28", - ".R29", - ".R30", - ".R31", - ".F0", - ".F1", - ".F2", - ".F3", - ".F4", - ".F5", - ".F6", - ".F7", - ".F8", - ".F9", - ".F10", - ".F11", - ".F12", - ".F13", - ".F14", - ".F15", - ".F16", - ".F17", - ".F18", - ".F19", - ".F20", - ".F21", - ".F22", - ".F23", - ".F24", - ".F25", - ".F26", - ".F27", - ".F28", - ".F29", - ".F30", - ".F31", -} - -func regnames(n *int) []string { - *n = NREGVAR - return regname -} - -func excludedregs() uint64 { - // Exclude registers with fixed functions - regbits := uint64(1<<0 | RtoB(ppc64.REGSP) | RtoB(ppc64.REGG) | RtoB(ppc64.REGTLS)) - - // Also exclude floating point registers with fixed constants - regbits |= RtoB(ppc64.REG_F27) | RtoB(ppc64.REG_F28) | RtoB(ppc64.REG_F29) | RtoB(ppc64.REG_F30) | RtoB(ppc64.REG_F31) - - return regbits -} - -func doregbits(r int) uint64 { - return 0 -} - -/* - * track register variables including external registers: - * bit reg - * 0 R0 - * 1 R1 - * ... ... - * 31 R31 - * 32+0 F0 - * 32+1 F1 - * ... ... - * 32+31 F31 - */ -func RtoB(r int) uint64 { - if r > ppc64.REG_R0 && r <= ppc64.REG_R31 { - return 1 << uint(r-ppc64.REG_R0) - } - if r >= ppc64.REG_F0 && r <= ppc64.REG_F31 { - return 1 << uint(32+r-ppc64.REG_F0) - } - return 0 -} - -func BtoR(b uint64) int { - b &= 0xffffffff - if b == 0 { - return 0 - } - return gc.Bitno(b) + ppc64.REG_R0 -} - -func BtoF(b uint64) int { - b >>= 32 - if b == 0 { - return 0 - } - return gc.Bitno(b) + ppc64.REG_F0 -} diff --git a/src/cmd/9l/asm.go b/src/cmd/9l/asm.go deleted file mode 100644 index 45aa3f84c2..0000000000 --- a/src/cmd/9l/asm.go +++ /dev/null @@ -1,737 +0,0 @@ -// Inferno utils/5l/asm.c -// http://code.google.com/p/inferno-os/source/browse/utils/5l/asm.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package main - -import ( - "cmd/internal/ld" - "cmd/internal/obj" - "encoding/binary" - "fmt" - "log" -) - -func gentext() { - var s *ld.LSym - var stub *ld.LSym - var pprevtextp **ld.LSym - var r *ld.Reloc - var n string - var o1 uint32 - var i int - - // The ppc64 ABI PLT has similar concepts to other - // architectures, but is laid out quite differently. When we - // see an R_PPC64_REL24 relocation to a dynamic symbol - // (indicating that the call needs to go through the PLT), we - // generate up to three stubs and reserve a PLT slot. - // - // 1) The call site will be bl x; nop (where the relocation - // applies to the bl). We rewrite this to bl x_stub; ld - // r2,24(r1). The ld is necessary because x_stub will save - // r2 (the TOC pointer) at 24(r1) (the "TOC save slot"). - // - // 2) We reserve space for a pointer in the .plt section (once - // per referenced dynamic function). .plt is a data - // section filled solely by the dynamic linker (more like - // .plt.got on other architectures). Initially, the - // dynamic linker will fill each slot with a pointer to the - // corresponding x@plt entry point. - // - // 3) We generate the "call stub" x_stub (once per dynamic - // function/object file pair). This saves the TOC in the - // TOC save slot, reads the function pointer from x's .plt - // slot and calls it like any other global entry point - // (including setting r12 to the function address). - // - // 4) We generate the "symbol resolver stub" x@plt (once per - // dynamic function). This is solely a branch to the glink - // resolver stub. - // - // 5) We generate the glink resolver stub (only once). This - // computes which symbol resolver stub we came through and - // invokes the dynamic resolver via a pointer provided by - // the dynamic linker. This will patch up the .plt slot to - // point directly at the function so future calls go - // straight from the call stub to the real function, and - // then call the function. - - // NOTE: It's possible we could make ppc64 closer to other - // architectures: ppc64's .plt is like .plt.got on other - // platforms and ppc64's .glink is like .plt on other - // platforms. - - // Find all R_PPC64_REL24 relocations that reference dynamic - // imports. Reserve PLT entries for these symbols and - // generate call stubs. The call stubs need to live in .text, - // which is why we need to do this pass this early. - // - // This assumes "case 1" from the ABI, where the caller needs - // us to save and restore the TOC pointer. - pprevtextp = &ld.Ctxt.Textp - - for s = *pprevtextp; s != nil; pprevtextp, s = &s.Next, s.Next { - for i = range s.R { - r = &s.R[i] - if r.Type != 256+ld.R_PPC64_REL24 || r.Sym.Type != obj.SDYNIMPORT { - continue - } - - // Reserve PLT entry and generate symbol - // resolver - addpltsym(ld.Ctxt, r.Sym) - - // Generate call stub - n = fmt.Sprintf("%s.%s", s.Name, r.Sym.Name) - - stub = ld.Linklookup(ld.Ctxt, n, 0) - stub.Reachable = stub.Reachable || s.Reachable - if stub.Size == 0 { - // Need outer to resolve .TOC. - stub.Outer = s - - // Link in to textp before s (we could - // do it after, but would have to skip - // the subsymbols) - *pprevtextp = stub - - stub.Next = s - pprevtextp = &stub.Next - - gencallstub(1, stub, r.Sym) - } - - // Update the relocation to use the call stub - r.Sym = stub - - // Restore TOC after bl. The compiler put a - // nop here for us to overwrite. - o1 = 0xe8410018 // ld r2,24(r1) - ld.Ctxt.Arch.ByteOrder.PutUint32(s.P[r.Off+4:], o1) - } - } -} - -// Construct a call stub in stub that calls symbol targ via its PLT -// entry. -func gencallstub(abicase int, stub *ld.LSym, targ *ld.LSym) { - if abicase != 1 { - // If we see R_PPC64_TOCSAVE or R_PPC64_REL24_NOTOC - // relocations, we'll need to implement cases 2 and 3. - log.Fatalf("gencallstub only implements case 1 calls") - } - - plt := ld.Linklookup(ld.Ctxt, ".plt", 0) - - stub.Type = obj.STEXT - - // Save TOC pointer in TOC save slot - ld.Adduint32(ld.Ctxt, stub, 0xf8410018) // std r2,24(r1) - - // Load the function pointer from the PLT. - r := ld.Addrel(stub) - - r.Off = int32(stub.Size) - r.Sym = plt - r.Add = int64(targ.Plt) - r.Siz = 2 - if ld.Ctxt.Arch.ByteOrder == binary.BigEndian { - r.Off += int32(r.Siz) - } - r.Type = obj.R_POWER_TOC - r.Variant = ld.RV_POWER_HA - ld.Adduint32(ld.Ctxt, stub, 0x3d820000) // addis r12,r2,targ@plt@toc@ha - r = ld.Addrel(stub) - r.Off = int32(stub.Size) - r.Sym = plt - r.Add = int64(targ.Plt) - r.Siz = 2 - if ld.Ctxt.Arch.ByteOrder == binary.BigEndian { - r.Off += int32(r.Siz) - } - r.Type = obj.R_POWER_TOC - r.Variant = ld.RV_POWER_LO - ld.Adduint32(ld.Ctxt, stub, 0xe98c0000) // ld r12,targ@plt@toc@l(r12) - - // Jump to the loaded pointer - ld.Adduint32(ld.Ctxt, stub, 0x7d8903a6) // mtctr r12 - ld.Adduint32(ld.Ctxt, stub, 0x4e800420) // bctr -} - -func adddynrela(rel *ld.LSym, s *ld.LSym, r *ld.Reloc) { - log.Fatalf("adddynrela not implemented") -} - -func adddynrel(s *ld.LSym, r *ld.Reloc) { - targ := r.Sym - ld.Ctxt.Cursym = s - - switch r.Type { - default: - if r.Type >= 256 { - ld.Diag("unexpected relocation type %d", r.Type) - return - } - - // Handle relocations found in ELF object files. - case 256 + ld.R_PPC64_REL24: - r.Type = obj.R_CALLPOWER - - // This is a local call, so the caller isn't setting - // up r12 and r2 is the same for the caller and - // callee. Hence, we need to go to the local entry - // point. (If we don't do this, the callee will try - // to use r12 to compute r2.) - r.Add += int64(r.Sym.Localentry) * 4 - - if targ.Type == obj.SDYNIMPORT { - // Should have been handled in elfsetupplt - ld.Diag("unexpected R_PPC64_REL24 for dyn import") - } - - return - - case 256 + ld.R_PPC64_ADDR64: - r.Type = obj.R_ADDR - if targ.Type == obj.SDYNIMPORT { - // These happen in .toc sections - ld.Adddynsym(ld.Ctxt, targ) - - rela := ld.Linklookup(ld.Ctxt, ".rela", 0) - ld.Addaddrplus(ld.Ctxt, rela, s, int64(r.Off)) - ld.Adduint64(ld.Ctxt, rela, ld.ELF64_R_INFO(uint32(targ.Dynid), ld.R_PPC64_ADDR64)) - ld.Adduint64(ld.Ctxt, rela, uint64(r.Add)) - r.Type = 256 // ignore during relocsym - } - - return - - case 256 + ld.R_PPC64_TOC16: - r.Type = obj.R_POWER_TOC - r.Variant = ld.RV_POWER_LO | ld.RV_CHECK_OVERFLOW - return - - case 256 + ld.R_PPC64_TOC16_LO: - r.Type = obj.R_POWER_TOC - r.Variant = ld.RV_POWER_LO - return - - case 256 + ld.R_PPC64_TOC16_HA: - r.Type = obj.R_POWER_TOC - r.Variant = ld.RV_POWER_HA | ld.RV_CHECK_OVERFLOW - return - - case 256 + ld.R_PPC64_TOC16_HI: - r.Type = obj.R_POWER_TOC - r.Variant = ld.RV_POWER_HI | ld.RV_CHECK_OVERFLOW - return - - case 256 + ld.R_PPC64_TOC16_DS: - r.Type = obj.R_POWER_TOC - r.Variant = ld.RV_POWER_DS | ld.RV_CHECK_OVERFLOW - return - - case 256 + ld.R_PPC64_TOC16_LO_DS: - r.Type = obj.R_POWER_TOC - r.Variant = ld.RV_POWER_DS - return - - case 256 + ld.R_PPC64_REL16_LO: - r.Type = obj.R_PCREL - r.Variant = ld.RV_POWER_LO - r.Add += 2 // Compensate for relocation size of 2 - return - - case 256 + ld.R_PPC64_REL16_HI: - r.Type = obj.R_PCREL - r.Variant = ld.RV_POWER_HI | ld.RV_CHECK_OVERFLOW - r.Add += 2 - return - - case 256 + ld.R_PPC64_REL16_HA: - r.Type = obj.R_PCREL - r.Variant = ld.RV_POWER_HA | ld.RV_CHECK_OVERFLOW - r.Add += 2 - return - } - - // Handle references to ELF symbols from our own object files. - if targ.Type != obj.SDYNIMPORT { - return - } - - // TODO(austin): Translate our relocations to ELF - - ld.Diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ.Name, r.Type, targ.Type) -} - -func elfreloc1(r *ld.Reloc, sectoff int64) int { - // TODO(minux) - return -1 -} - -func elfsetupplt() { - plt := ld.Linklookup(ld.Ctxt, ".plt", 0) - if plt.Size == 0 { - // The dynamic linker stores the address of the - // dynamic resolver and the DSO identifier in the two - // doublewords at the beginning of the .plt section - // before the PLT array. Reserve space for these. - plt.Size = 16 - } -} - -func machoreloc1(r *ld.Reloc, sectoff int64) int { - return -1 -} - -// Return the value of .TOC. for symbol s -func symtoc(s *ld.LSym) int64 { - var toc *ld.LSym - - if s.Outer != nil { - toc = ld.Linkrlookup(ld.Ctxt, ".TOC.", int(s.Outer.Version)) - } else { - toc = ld.Linkrlookup(ld.Ctxt, ".TOC.", int(s.Version)) - } - - if toc == nil { - ld.Diag("TOC-relative relocation in object without .TOC.") - return 0 - } - - return toc.Value -} - -func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int { - if ld.Linkmode == ld.LinkExternal { - // TODO(minux): translate R_ADDRPOWER and R_CALLPOWER into standard ELF relocations. - // R_ADDRPOWER corresponds to R_PPC_ADDR16_HA and R_PPC_ADDR16_LO. - // R_CALLPOWER corresponds to R_PPC_REL24. - return -1 - } - - switch r.Type { - case obj.R_CONST: - *val = r.Add - return 0 - - case obj.R_GOTOFF: - *val = ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ld.Linklookup(ld.Ctxt, ".got", 0)) - return 0 - - case obj.R_ADDRPOWER: - // r->add is two ppc64 instructions holding an immediate 32-bit constant. - // We want to add r->sym's address to that constant. - // The encoding of the immediate x<<16 + y, - // where x is the low 16 bits of the first instruction and y is the low 16 - // bits of the second. Both x and y are signed (int16, not uint16). - o1 := uint32(r.Add >> 32) - o2 := uint32(r.Add) - t := ld.Symaddr(r.Sym) - if t < 0 { - ld.Ctxt.Diag("relocation for %s is too big (>=2G): %d", s.Name, ld.Symaddr(r.Sym)) - } - - t += int64((o1&0xffff)<<16 + uint32(int32(o2)<<16>>16)) - if t&0x8000 != 0 { - t += 0x10000 - } - o1 = o1&0xffff0000 | (uint32(t)>>16)&0xffff - o2 = o2&0xffff0000 | uint32(t)&0xffff - - // when laid out, the instruction order must always be o1, o2. - if ld.Ctxt.Arch.ByteOrder == binary.BigEndian { - *val = int64(o1)<<32 | int64(o2) - } else { - *val = int64(o2)<<32 | int64(o1) - } - return 0 - - case obj.R_CALLPOWER: - // Bits 6 through 29 = (S + A - P) >> 2 - var o1 uint32 - if ld.Ctxt.Arch.ByteOrder == binary.BigEndian { - o1 = ld.Be32(s.P[r.Off:]) - } else { - o1 = ld.Le32(s.P[r.Off:]) - } - - t := ld.Symaddr(r.Sym) + r.Add - (s.Value + int64(r.Off)) - if t&3 != 0 { - ld.Ctxt.Diag("relocation for %s+%d is not aligned: %d", r.Sym.Name, r.Off, t) - } - if int64(int32(t<<6)>>6) != t { - // TODO(austin) This can happen if text > 32M. - // Add a call trampoline to .text in that case. - ld.Ctxt.Diag("relocation for %s+%d is too big: %d", r.Sym.Name, r.Off, t) - } - - *val = int64(o1&0xfc000003 | uint32(t)&^0xfc000003) - return 0 - - case obj.R_POWER_TOC: // S + A - .TOC. - *val = ld.Symaddr(r.Sym) + r.Add - symtoc(s) - - return 0 - } - - return -1 -} - -func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 { - switch r.Variant & ld.RV_TYPE_MASK { - default: - ld.Diag("unexpected relocation variant %d", r.Variant) - fallthrough - - case ld.RV_NONE: - return t - - case ld.RV_POWER_LO: - if r.Variant&ld.RV_CHECK_OVERFLOW != 0 { - // Whether to check for signed or unsigned - // overflow depends on the instruction - var o1 uint32 - if ld.Ctxt.Arch.ByteOrder == binary.BigEndian { - o1 = ld.Be32(s.P[r.Off-2:]) - } else { - o1 = ld.Le32(s.P[r.Off:]) - } - switch o1 >> 26 { - case 24, // ori - 26, // xori - 28: // andi - if t>>16 != 0 { - goto overflow - } - - default: - if int64(int16(t)) != t { - goto overflow - } - } - } - - return int64(int16(t)) - - case ld.RV_POWER_HA: - t += 0x8000 - fallthrough - - // Fallthrough - case ld.RV_POWER_HI: - t >>= 16 - - if r.Variant&ld.RV_CHECK_OVERFLOW != 0 { - // Whether to check for signed or unsigned - // overflow depends on the instruction - var o1 uint32 - if ld.Ctxt.Arch.ByteOrder == binary.BigEndian { - o1 = ld.Be32(s.P[r.Off-2:]) - } else { - o1 = ld.Le32(s.P[r.Off:]) - } - switch o1 >> 26 { - case 25, // oris - 27, // xoris - 29: // andis - if t>>16 != 0 { - goto overflow - } - - default: - if int64(int16(t)) != t { - goto overflow - } - } - } - - return int64(int16(t)) - - case ld.RV_POWER_DS: - var o1 uint32 - if ld.Ctxt.Arch.ByteOrder == binary.BigEndian { - o1 = uint32(ld.Be16(s.P[r.Off:])) - } else { - o1 = uint32(ld.Le16(s.P[r.Off:])) - } - if t&3 != 0 { - ld.Diag("relocation for %s+%d is not aligned: %d", r.Sym.Name, r.Off, t) - } - if (r.Variant&ld.RV_CHECK_OVERFLOW != 0) && int64(int16(t)) != t { - goto overflow - } - return int64(o1)&0x3 | int64(int16(t)) - } - -overflow: - ld.Diag("relocation for %s+%d is too big: %d", r.Sym.Name, r.Off, t) - return t -} - -func addpltsym(ctxt *ld.Link, s *ld.LSym) { - if s.Plt >= 0 { - return - } - - ld.Adddynsym(ctxt, s) - - if ld.Iself { - plt := ld.Linklookup(ctxt, ".plt", 0) - rela := ld.Linklookup(ctxt, ".rela.plt", 0) - if plt.Size == 0 { - elfsetupplt() - } - - // Create the glink resolver if necessary - glink := ensureglinkresolver() - - // Write symbol resolver stub (just a branch to the - // glink resolver stub) - r := ld.Addrel(glink) - - r.Sym = glink - r.Off = int32(glink.Size) - r.Siz = 4 - r.Type = obj.R_CALLPOWER - ld.Adduint32(ctxt, glink, 0x48000000) // b .glink - - // In the ppc64 ABI, the dynamic linker is responsible - // for writing the entire PLT. We just need to - // reserve 8 bytes for each PLT entry and generate a - // JMP_SLOT dynamic relocation for it. - // - // TODO(austin): ABI v1 is different - s.Plt = int32(plt.Size) - - plt.Size += 8 - - ld.Addaddrplus(ctxt, rela, plt, int64(s.Plt)) - ld.Adduint64(ctxt, rela, ld.ELF64_R_INFO(uint32(s.Dynid), ld.R_PPC64_JMP_SLOT)) - ld.Adduint64(ctxt, rela, 0) - } else { - ld.Diag("addpltsym: unsupported binary format") - } -} - -// Generate the glink resolver stub if necessary and return the .glink section -func ensureglinkresolver() *ld.LSym { - glink := ld.Linklookup(ld.Ctxt, ".glink", 0) - if glink.Size != 0 { - return glink - } - - // This is essentially the resolver from the ppc64 ELF ABI. - // At entry, r12 holds the address of the symbol resolver stub - // for the target routine and the argument registers hold the - // arguments for the target routine. - // - // This stub is PIC, so first get the PC of label 1 into r11. - // Other things will be relative to this. - ld.Adduint32(ld.Ctxt, glink, 0x7c0802a6) // mflr r0 - ld.Adduint32(ld.Ctxt, glink, 0x429f0005) // bcl 20,31,1f - ld.Adduint32(ld.Ctxt, glink, 0x7d6802a6) // 1: mflr r11 - ld.Adduint32(ld.Ctxt, glink, 0x7c0803a6) // mtlf r0 - - // Compute the .plt array index from the entry point address. - // Because this is PIC, everything is relative to label 1b (in - // r11): - // r0 = ((r12 - r11) - (res_0 - r11)) / 4 = (r12 - res_0) / 4 - ld.Adduint32(ld.Ctxt, glink, 0x3800ffd0) // li r0,-(res_0-1b)=-48 - ld.Adduint32(ld.Ctxt, glink, 0x7c006214) // add r0,r0,r12 - ld.Adduint32(ld.Ctxt, glink, 0x7c0b0050) // sub r0,r0,r11 - ld.Adduint32(ld.Ctxt, glink, 0x7800f082) // srdi r0,r0,2 - - // r11 = address of the first byte of the PLT - r := ld.Addrel(glink) - - r.Off = int32(glink.Size) - r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) - r.Siz = 8 - r.Type = obj.R_ADDRPOWER - - // addis r11,0,.plt@ha; addi r11,r11,.plt@l - r.Add = 0x3d600000<<32 | 0x396b0000 - - glink.Size += 8 - - // Load r12 = dynamic resolver address and r11 = DSO - // identifier from the first two doublewords of the PLT. - ld.Adduint32(ld.Ctxt, glink, 0xe98b0000) // ld r12,0(r11) - ld.Adduint32(ld.Ctxt, glink, 0xe96b0008) // ld r11,8(r11) - - // Jump to the dynamic resolver - ld.Adduint32(ld.Ctxt, glink, 0x7d8903a6) // mtctr r12 - ld.Adduint32(ld.Ctxt, glink, 0x4e800420) // bctr - - // The symbol resolvers must immediately follow. - // res_0: - - // Add DT_PPC64_GLINK .dynamic entry, which points to 32 bytes - // before the first symbol resolver stub. - s := ld.Linklookup(ld.Ctxt, ".dynamic", 0) - - ld.Elfwritedynentsymplus(s, ld.DT_PPC64_GLINK, glink, glink.Size-32) - - return glink -} - -func asmb() { - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime()) - } - ld.Bso.Flush() - - if ld.Iself { - ld.Asmbelfsetup() - } - - sect := ld.Segtext.Sect - ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) - ld.Codeblk(int64(sect.Vaddr), int64(sect.Length)) - for sect = sect.Next; sect != nil; sect = sect.Next { - ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) - ld.Datblk(int64(sect.Vaddr), int64(sect.Length)) - } - - if ld.Segrodata.Filelen > 0 { - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) - } - ld.Bso.Flush() - - ld.Cseek(int64(ld.Segrodata.Fileoff)) - ld.Datblk(int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) - } - - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f datblk\n", obj.Cputime()) - } - ld.Bso.Flush() - - ld.Cseek(int64(ld.Segdata.Fileoff)) - ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) - - /* output symbol table */ - ld.Symsize = 0 - - ld.Lcsize = 0 - symo := uint32(0) - if ld.Debug['s'] == 0 { - // TODO: rationalize - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime()) - } - ld.Bso.Flush() - switch ld.HEADTYPE { - default: - if ld.Iself { - symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) - symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND))) - } - - case obj.Hplan9: - symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) - } - - ld.Cseek(int64(symo)) - switch ld.HEADTYPE { - default: - if ld.Iself { - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f elfsym\n", obj.Cputime()) - } - ld.Asmelfsym() - ld.Cflush() - ld.Cwrite(ld.Elfstrdat) - - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) - } - ld.Dwarfemitdebugsections() - - if ld.Linkmode == ld.LinkExternal { - ld.Elfemitreloc() - } - } - - case obj.Hplan9: - ld.Asmplan9sym() - ld.Cflush() - - sym := ld.Linklookup(ld.Ctxt, "pclntab", 0) - if sym != nil { - ld.Lcsize = int32(len(sym.P)) - for i := 0; int32(i) < ld.Lcsize; i++ { - ld.Cput(uint8(sym.P[i])) - } - - ld.Cflush() - } - } - } - - ld.Ctxt.Cursym = nil - if ld.Debug['v'] != 0 { - fmt.Fprintf(&ld.Bso, "%5.2f header\n", obj.Cputime()) - } - ld.Bso.Flush() - ld.Cseek(0) - switch ld.HEADTYPE { - default: - case obj.Hplan9: /* plan 9 */ - ld.Thearch.Lput(0x647) /* magic */ - ld.Thearch.Lput(uint32(ld.Segtext.Filelen)) /* sizes */ - ld.Thearch.Lput(uint32(ld.Segdata.Filelen)) - ld.Thearch.Lput(uint32(ld.Segdata.Length - ld.Segdata.Filelen)) - ld.Thearch.Lput(uint32(ld.Symsize)) /* nsyms */ - ld.Thearch.Lput(uint32(ld.Entryvalue())) /* va of entry */ - ld.Thearch.Lput(0) - ld.Thearch.Lput(uint32(ld.Lcsize)) - - case obj.Hlinux, - obj.Hfreebsd, - obj.Hnetbsd, - obj.Hopenbsd, - obj.Hnacl: - ld.Asmbelf(int64(symo)) - } - - ld.Cflush() - if ld.Debug['c'] != 0 { - fmt.Printf("textsize=%d\n", ld.Segtext.Filelen) - fmt.Printf("datsize=%d\n", ld.Segdata.Filelen) - fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen) - fmt.Printf("symsize=%d\n", ld.Symsize) - fmt.Printf("lcsize=%d\n", ld.Lcsize) - fmt.Printf("total=%d\n", ld.Segtext.Filelen+ld.Segdata.Length+uint64(ld.Symsize)+uint64(ld.Lcsize)) - } -} diff --git a/src/cmd/9l/l.go b/src/cmd/9l/l.go deleted file mode 100644 index 8723eaeca4..0000000000 --- a/src/cmd/9l/l.go +++ /dev/null @@ -1,78 +0,0 @@ -// Inferno utils/5l/asm.c -// http://code.google.com/p/inferno-os/source/browse/utils/5l/asm.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package main - -// Writing object files. - -// cmd/9l/l.h from Vita Nuova. -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -const ( - thechar = '9' - PtrSize = 8 - IntSize = 8 - RegSize = 8 - MaxAlign = 32 // max data alignment - FuncAlign = 8 - MINLC = 4 -) - -/* 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 deleted file mode 100644 index 011f290298..0000000000 --- a/src/cmd/9l/obj.go +++ /dev/null @@ -1,162 +0,0 @@ -// Inferno utils/5l/obj.c -// http://code.google.com/p/inferno-os/source/browse/utils/5l/obj.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package main - -import ( - "cmd/internal/ld" - "cmd/internal/obj" - "fmt" - "log" -) - -// Reading object files. - -func main() { - linkarchinit() - ld.Ldmain() -} - -func linkarchinit() { - ld.Thestring = obj.Getgoarch() - if ld.Thestring == "ppc64le" { - ld.Thelinkarch = &ld.Linkppc64le - } else { - ld.Thelinkarch = &ld.Linkppc64 - } - - ld.Thearch.Thechar = thechar - ld.Thearch.Ptrsize = ld.Thelinkarch.Ptrsize - ld.Thearch.Intsize = ld.Thelinkarch.Ptrsize - ld.Thearch.Regsize = ld.Thelinkarch.Regsize - ld.Thearch.Funcalign = FuncAlign - ld.Thearch.Maxalign = MaxAlign - ld.Thearch.Minlc = MINLC - ld.Thearch.Dwarfregsp = DWARFREGSP - ld.Thearch.Dwarfreglr = DWARFREGLR - - ld.Thearch.Adddynrel = adddynrel - ld.Thearch.Archinit = archinit - ld.Thearch.Archreloc = archreloc - ld.Thearch.Archrelocvariant = archrelocvariant - ld.Thearch.Asmb = asmb - ld.Thearch.Elfreloc1 = elfreloc1 - ld.Thearch.Elfsetupplt = elfsetupplt - ld.Thearch.Gentext = gentext - ld.Thearch.Machoreloc1 = machoreloc1 - if ld.Thelinkarch == &ld.Linkppc64le { - ld.Thearch.Lput = ld.Lputl - ld.Thearch.Wput = ld.Wputl - ld.Thearch.Vput = ld.Vputl - } else { - ld.Thearch.Lput = ld.Lputb - ld.Thearch.Wput = ld.Wputb - ld.Thearch.Vput = ld.Vputb - } - - // TODO(austin): ABI v1 uses /usr/lib/ld.so.1 - ld.Thearch.Linuxdynld = "/lib64/ld64.so.1" - - ld.Thearch.Freebsddynld = "XXX" - ld.Thearch.Openbsddynld = "XXX" - ld.Thearch.Netbsddynld = "XXX" - ld.Thearch.Dragonflydynld = "XXX" - ld.Thearch.Solarisdynld = "XXX" -} - -func archinit() { - // getgoextlinkenabled is based on GO_EXTLINK_ENABLED when - // Go was built; see ../../make.bash. - if ld.Linkmode == ld.LinkAuto && obj.Getgoextlinkenabled() == "0" { - ld.Linkmode = ld.LinkInternal - } - - switch ld.HEADTYPE { - default: - if ld.Linkmode == ld.LinkAuto { - ld.Linkmode = ld.LinkInternal - } - if ld.Linkmode == ld.LinkExternal && obj.Getgoextlinkenabled() != "1" { - log.Fatalf("cannot use -linkmode=external with -H %s", ld.Headstr(int(ld.HEADTYPE))) - } - } - - switch ld.HEADTYPE { - default: - ld.Exitf("unknown -H option: %v", ld.HEADTYPE) - - case obj.Hplan9: /* plan 9 */ - ld.HEADR = 32 - - if ld.INITTEXT == -1 { - ld.INITTEXT = 4128 - } - if ld.INITDAT == -1 { - ld.INITDAT = 0 - } - if ld.INITRND == -1 { - ld.INITRND = 4096 - } - - case obj.Hlinux: /* ppc64 elf */ - if ld.Thestring == "ppc64" { - ld.Debug['d'] = 1 // TODO(austin): ELF ABI v1 not supported yet - } - ld.Elfinit() - ld.HEADR = ld.ELFRESERVE - if ld.INITTEXT == -1 { - ld.INITTEXT = 0x10000 + int64(ld.HEADR) - } - if ld.INITDAT == -1 { - ld.INITDAT = 0 - } - if ld.INITRND == -1 { - ld.INITRND = 0x10000 - } - - case obj.Hnacl: - ld.Elfinit() - ld.HEADR = 0x10000 - ld.Funcalign = 16 - if ld.INITTEXT == -1 { - ld.INITTEXT = 0x20000 - } - if ld.INITDAT == -1 { - ld.INITDAT = 0 - } - if ld.INITRND == -1 { - ld.INITRND = 0x10000 - } - } - - if ld.INITDAT != 0 && ld.INITRND != 0 { - fmt.Printf("warning: -D0x%x is ignored because of -R0x%x\n", uint64(ld.INITDAT), uint32(ld.INITRND)) - } -} diff --git a/src/cmd/compile/internal/amd64/cgen.go b/src/cmd/compile/internal/amd64/cgen.go new file mode 100644 index 0000000000..71f8f88322 --- /dev/null +++ b/src/cmd/compile/internal/amd64/cgen.go @@ -0,0 +1,151 @@ +// Copyright 2009 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 amd64 + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/x86" +) + +func blockcopy(n, ns *gc.Node, osrc, odst, w int64) { + var noddi gc.Node + gc.Nodreg(&noddi, gc.Types[gc.Tptr], x86.REG_DI) + var nodsi gc.Node + gc.Nodreg(&nodsi, gc.Types[gc.Tptr], x86.REG_SI) + + var nodl gc.Node + var nodr gc.Node + if n.Ullman >= ns.Ullman { + gc.Agenr(n, &nodr, &nodsi) + if ns.Op == gc.ONAME { + gc.Gvardef(ns) + } + gc.Agenr(ns, &nodl, &noddi) + } else { + if ns.Op == gc.ONAME { + gc.Gvardef(ns) + } + gc.Agenr(ns, &nodl, &noddi) + gc.Agenr(n, &nodr, &nodsi) + } + + if nodl.Reg != x86.REG_DI { + gmove(&nodl, &noddi) + } + if nodr.Reg != x86.REG_SI { + gmove(&nodr, &nodsi) + } + gc.Regfree(&nodl) + gc.Regfree(&nodr) + + c := w % 8 // bytes + q := w / 8 // quads + + var oldcx gc.Node + var cx gc.Node + savex(x86.REG_CX, &cx, &oldcx, nil, gc.Types[gc.TINT64]) + + // if we are copying forward on the stack and + // the src and dst overlap, then reverse direction + if osrc < odst && odst < osrc+w { + // reverse direction + gins(x86.ASTD, nil, nil) // set direction flag + if c > 0 { + gconreg(addptr, w-1, x86.REG_SI) + gconreg(addptr, w-1, x86.REG_DI) + + gconreg(movptr, c, x86.REG_CX) + gins(x86.AREP, nil, nil) // repeat + gins(x86.AMOVSB, nil, nil) // MOVB *(SI)-,*(DI)- + } + + if q > 0 { + if c > 0 { + gconreg(addptr, -7, x86.REG_SI) + gconreg(addptr, -7, x86.REG_DI) + } else { + gconreg(addptr, w-8, x86.REG_SI) + gconreg(addptr, w-8, x86.REG_DI) + } + + gconreg(movptr, q, x86.REG_CX) + gins(x86.AREP, nil, nil) // repeat + gins(x86.AMOVSQ, nil, nil) // MOVQ *(SI)-,*(DI)- + } + + // we leave with the flag clear + gins(x86.ACLD, nil, nil) + } else { + // normal direction + if q > 128 || (gc.Nacl && q >= 4) { + gconreg(movptr, q, x86.REG_CX) + gins(x86.AREP, nil, nil) // repeat + gins(x86.AMOVSQ, nil, nil) // MOVQ *(SI)+,*(DI)+ + } else if q >= 4 { + p := gins(obj.ADUFFCOPY, nil, nil) + p.To.Type = obj.TYPE_ADDR + p.To.Sym = gc.Linksym(gc.Pkglookup("duffcopy", gc.Runtimepkg)) + + // 14 and 128 = magic constants: see ../../runtime/asm_amd64.s + p.To.Offset = 14 * (128 - q) + } else if !gc.Nacl && c == 0 { + // We don't need the MOVSQ side-effect of updating SI and DI, + // and issuing a sequence of MOVQs directly is faster. + nodsi.Op = gc.OINDREG + + noddi.Op = gc.OINDREG + for q > 0 { + gmove(&nodsi, &cx) // MOVQ x+(SI),CX + gmove(&cx, &noddi) // MOVQ CX,x+(DI) + nodsi.Xoffset += 8 + noddi.Xoffset += 8 + q-- + } + } else { + for q > 0 { + gins(x86.AMOVSQ, nil, nil) // MOVQ *(SI)+,*(DI)+ + q-- + } + } + + // copy the remaining c bytes + if w < 4 || c <= 1 || (odst < osrc && osrc < odst+w) { + for c > 0 { + gins(x86.AMOVSB, nil, nil) // MOVB *(SI)+,*(DI)+ + c-- + } + } else if w < 8 || c <= 4 { + nodsi.Op = gc.OINDREG + noddi.Op = gc.OINDREG + cx.Type = gc.Types[gc.TINT32] + nodsi.Type = gc.Types[gc.TINT32] + noddi.Type = gc.Types[gc.TINT32] + if c > 4 { + nodsi.Xoffset = 0 + noddi.Xoffset = 0 + gmove(&nodsi, &cx) + gmove(&cx, &noddi) + } + + nodsi.Xoffset = c - 4 + noddi.Xoffset = c - 4 + gmove(&nodsi, &cx) + gmove(&cx, &noddi) + } else { + nodsi.Op = gc.OINDREG + noddi.Op = gc.OINDREG + cx.Type = gc.Types[gc.TINT64] + nodsi.Type = gc.Types[gc.TINT64] + noddi.Type = gc.Types[gc.TINT64] + nodsi.Xoffset = c - 8 + noddi.Xoffset = c - 8 + gmove(&nodsi, &cx) + gmove(&cx, &noddi) + } + } + + restx(&cx, &oldcx) +} diff --git a/src/cmd/compile/internal/amd64/galign.go b/src/cmd/compile/internal/amd64/galign.go new file mode 100644 index 0000000000..79bf94a075 --- /dev/null +++ b/src/cmd/compile/internal/amd64/galign.go @@ -0,0 +1,129 @@ +// Copyright 2009 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 amd64 + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/x86" +) + +var ( + thechar int = '6' + thestring string = "amd64" + thelinkarch *obj.LinkArch = &x86.Linkamd64 +) + +func linkarchinit() { + if obj.Getgoarch() == "amd64p32" { + thelinkarch = &x86.Linkamd64p32 + gc.Thearch.Thelinkarch = thelinkarch + thestring = "amd64p32" + gc.Thearch.Thestring = "amd64p32" + } +} + +var MAXWIDTH int64 = 1 << 50 + +var ( + addptr int = x86.AADDQ + movptr int = x86.AMOVQ + leaptr int = x86.ALEAQ + cmpptr int = x86.ACMPQ +) + +/* + * go declares several platform-specific type aliases: + * int, uint, and uintptr + */ +var typedefs = []gc.Typedef{ + gc.Typedef{"int", gc.TINT, gc.TINT64}, + gc.Typedef{"uint", gc.TUINT, gc.TUINT64}, + gc.Typedef{"uintptr", gc.TUINTPTR, gc.TUINT64}, +} + +func betypeinit() { + gc.Widthptr = 8 + gc.Widthint = 8 + gc.Widthreg = 8 + if obj.Getgoarch() == "amd64p32" { + gc.Widthptr = 4 + gc.Widthint = 4 + addptr = x86.AADDL + movptr = x86.AMOVL + leaptr = x86.ALEAL + cmpptr = x86.ACMPL + typedefs[0].Sameas = gc.TINT32 + typedefs[1].Sameas = gc.TUINT32 + typedefs[2].Sameas = gc.TUINT32 + } + + if gc.Ctxt.Flag_dynlink { + gc.Thearch.ReservedRegs = append(gc.Thearch.ReservedRegs, x86.REG_R15) + } +} + +func Main() { + if obj.Getgoos() == "nacl" { + resvd = append(resvd, x86.REG_BP, x86.REG_R15) + } else if obj.Framepointer_enabled != 0 { + resvd = append(resvd, x86.REG_BP) + } + + gc.Thearch.Thechar = thechar + gc.Thearch.Thestring = thestring + gc.Thearch.Thelinkarch = thelinkarch + gc.Thearch.Typedefs = typedefs + gc.Thearch.REGSP = x86.REGSP + gc.Thearch.REGCTXT = x86.REGCTXT + gc.Thearch.REGCALLX = x86.REG_BX + gc.Thearch.REGCALLX2 = x86.REG_AX + gc.Thearch.REGRETURN = x86.REG_AX + gc.Thearch.REGMIN = x86.REG_AX + gc.Thearch.REGMAX = x86.REG_R15 + gc.Thearch.FREGMIN = x86.REG_X0 + gc.Thearch.FREGMAX = x86.REG_X15 + gc.Thearch.MAXWIDTH = MAXWIDTH + gc.Thearch.ReservedRegs = resvd + + gc.Thearch.AddIndex = addindex + gc.Thearch.Betypeinit = betypeinit + gc.Thearch.Cgen_bmul = cgen_bmul + gc.Thearch.Cgen_hmul = cgen_hmul + gc.Thearch.Cgen_shift = cgen_shift + gc.Thearch.Clearfat = clearfat + gc.Thearch.Defframe = defframe + gc.Thearch.Dodiv = dodiv + gc.Thearch.Excise = excise + gc.Thearch.Expandchecks = expandchecks + 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 + gc.Thearch.Linkarchinit = linkarchinit + gc.Thearch.Peep = peep + gc.Thearch.Proginfo = proginfo + gc.Thearch.Regtyp = regtyp + gc.Thearch.Sameaddr = sameaddr + gc.Thearch.Smallindir = smallindir + gc.Thearch.Stackaddr = stackaddr + gc.Thearch.Blockcopy = blockcopy + gc.Thearch.Sudoaddable = sudoaddable + gc.Thearch.Sudoclean = sudoclean + gc.Thearch.Excludedregs = excludedregs + gc.Thearch.RtoB = RtoB + gc.Thearch.FtoB = FtoB + gc.Thearch.BtoR = BtoR + gc.Thearch.BtoF = BtoF + gc.Thearch.Optoas = optoas + gc.Thearch.Doregbits = doregbits + gc.Thearch.Regnames = regnames + + gc.Main() + gc.Exit(0) +} diff --git a/src/cmd/compile/internal/amd64/ggen.go b/src/cmd/compile/internal/amd64/ggen.go new file mode 100644 index 0000000000..6425633818 --- /dev/null +++ b/src/cmd/compile/internal/amd64/ggen.go @@ -0,0 +1,743 @@ +// Copyright 2009 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 amd64 + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/x86" +) + +func defframe(ptxt *obj.Prog) { + var n *gc.Node + + // fill in argument size, stack size + ptxt.To.Type = obj.TYPE_TEXTSIZE + + ptxt.To.Val = int32(gc.Rnd(gc.Curfn.Type.Argwid, int64(gc.Widthptr))) + frame := uint32(gc.Rnd(gc.Stksize+gc.Maxarg, int64(gc.Widthreg))) + ptxt.To.Offset = int64(frame) + + // insert code to zero ambiguously live variables + // so that the garbage collector only sees initialized values + // when it looks for pointers. + p := ptxt + + hi := int64(0) + lo := hi + ax := uint32(0) + + // 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.Name.Needzero { + continue + } + if n.Class != gc.PAUTO { + gc.Fatal("needzero class %d", n.Class) + } + if n.Type.Width%int64(gc.Widthptr) != 0 || n.Xoffset%int64(gc.Widthptr) != 0 || n.Type.Width == 0 { + gc.Fatal("var %v has size %d offset %d", gc.Nconv(n, obj.FmtLong), int(n.Type.Width), int(n.Xoffset)) + } + + if lo != hi && n.Xoffset+n.Type.Width >= lo-int64(2*gc.Widthreg) { + // merge with range we already have + lo = n.Xoffset + + continue + } + + // zero old range + p = zerorange(p, int64(frame), lo, hi, &ax) + + // set new range + hi = n.Xoffset + n.Type.Width + + lo = n.Xoffset + } + + // zero final range + zerorange(p, int64(frame), lo, hi, &ax) +} + +// DUFFZERO consists of repeated blocks of 4 MOVs + ADD, +// with 4 STOSQs at the very end. +// The trailing STOSQs prevent the need for a DI preadjustment +// for small numbers of words to clear. +// See runtime/mkduff.go. +const ( + dzBlocks = 31 // number of MOV/ADD blocks + dzBlockLen = 4 // number of clears per block + dzBlockSize = 19 // size of instructions in a single block + dzMovSize = 4 // size of single MOV instruction w/ offset + dzAddSize = 4 // size of single ADD instruction + dzDIStep = 8 // number of bytes cleared by each MOV instruction + + dzTailLen = 4 // number of final STOSQ instructions + dzTailSize = 2 // size of single STOSQ instruction + + dzSize = dzBlocks*dzBlockSize + dzTailLen*dzTailSize // total size of DUFFZERO routine +) + +// duffzeroDI returns the pre-adjustment to DI for a call to DUFFZERO. +// q is the number of words to zero. +func dzDI(q int64) int64 { + if q < dzTailLen { + return 0 + } + q -= dzTailLen + if q%dzBlockLen == 0 { + return 0 + } + return -dzDIStep * (dzBlockLen - q%dzBlockLen) +} + +// dzOff returns the offset for a jump into DUFFZERO. +// q is the number of words to zero. +func dzOff(q int64) int64 { + off := int64(dzSize) + if q < dzTailLen { + return off - q*dzTailSize + } + off -= dzTailLen * dzTailSize + q -= dzTailLen + blocks, steps := q/dzBlockLen, q%dzBlockLen + off -= dzBlockSize * blocks + if steps > 0 { + off -= dzAddSize + dzMovSize*steps + } + return off +} + +func zerorange(p *obj.Prog, frame int64, lo int64, hi int64, ax *uint32) *obj.Prog { + cnt := hi - lo + if cnt == 0 { + return p + } + if *ax == 0 { + p = appendpp(p, x86.AMOVQ, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, x86.REG_AX, 0) + *ax = 1 + } + + if cnt%int64(gc.Widthreg) != 0 { + // should only happen with nacl + if cnt%int64(gc.Widthptr) != 0 { + gc.Fatal("zerorange count not a multiple of widthptr %d", cnt) + } + p = appendpp(p, x86.AMOVL, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, frame+lo) + lo += int64(gc.Widthptr) + cnt -= int64(gc.Widthptr) + } + + if cnt <= int64(4*gc.Widthreg) { + for i := int64(0); i < cnt; i += int64(gc.Widthreg) { + p = appendpp(p, x86.AMOVQ, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, frame+lo+i) + } + } else if !gc.Nacl && (cnt <= int64(128*gc.Widthreg)) { + q := cnt / int64(gc.Widthreg) + p = appendpp(p, leaptr, obj.TYPE_MEM, x86.REG_SP, frame+lo+dzDI(q), obj.TYPE_REG, x86.REG_DI, 0) + p = appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_ADDR, 0, dzOff(q)) + p.To.Sym = gc.Linksym(gc.Pkglookup("duffzero", gc.Runtimepkg)) + } else { + p = appendpp(p, x86.AMOVQ, obj.TYPE_CONST, 0, cnt/int64(gc.Widthreg), obj.TYPE_REG, x86.REG_CX, 0) + p = appendpp(p, leaptr, obj.TYPE_MEM, x86.REG_SP, frame+lo, obj.TYPE_REG, x86.REG_DI, 0) + p = appendpp(p, x86.AREP, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0) + p = appendpp(p, x86.ASTOSQ, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0) + } + + return p +} + +func appendpp(p *obj.Prog, as int, ftype int, freg int, foffset int64, ttype int, treg int, toffset int64) *obj.Prog { + q := gc.Ctxt.NewProg() + gc.Clearp(q) + q.As = int16(as) + q.Lineno = p.Lineno + q.From.Type = int16(ftype) + q.From.Reg = int16(freg) + q.From.Offset = foffset + q.To.Type = int16(ttype) + q.To.Reg = int16(treg) + q.To.Offset = toffset + q.Link = p.Link + p.Link = q + return q +} + +var panicdiv *gc.Node + +/* + * generate division. + * generates one of: + * res = nl / nr + * res = nl % nr + * according to op. + */ +func dodiv(op int, nl *gc.Node, nr *gc.Node, res *gc.Node) { + // Have to be careful about handling + // most negative int divided by -1 correctly. + // The hardware will trap. + // Also the byte divide instruction needs AH, + // which we otherwise don't have to deal with. + // Easiest way to avoid for int8, int16: use int32. + // For int32 and int64, use explicit test. + // Could use int64 hw for int32. + t := nl.Type + + t0 := t + check := 0 + if gc.Issigned[t.Etype] { + check = 1 + if gc.Isconst(nl, gc.CTINT) && nl.Int() != -(1<= nr.Ullman { + savex(x86.REG_AX, &ax, &oldax, res, t0) + gc.Cgen(nl, &ax) + gc.Regalloc(&ax, t0, &ax) // mark ax live during cgen + gc.Cgen(nr, &n3) + gc.Regfree(&ax) + } else { + gc.Cgen(nr, &n3) + savex(x86.REG_AX, &ax, &oldax, res, t0) + gc.Cgen(nl, &ax) + } + + if t != t0 { + // Convert + ax1 := ax + + n31 := n3 + ax.Type = t + n3.Type = t + gmove(&ax1, &ax) + gmove(&n31, &n3) + } + + var n4 gc.Node + if gc.Nacl { + // Native Client does not relay the divide-by-zero trap + // to the executing program, so we must insert a check + // for ourselves. + gc.Nodconst(&n4, t, 0) + + gins(optoas(gc.OCMP, t), &n3, &n4) + p1 := gc.Gbranch(optoas(gc.ONE, t), nil, +1) + if panicdiv == nil { + panicdiv = gc.Sysfunc("panicdivide") + } + gc.Ginscall(panicdiv, -1) + gc.Patch(p1, gc.Pc) + } + + var p2 *obj.Prog + if check != 0 { + gc.Nodconst(&n4, t, -1) + gins(optoas(gc.OCMP, t), &n3, &n4) + p1 := gc.Gbranch(optoas(gc.ONE, t), nil, +1) + if op == gc.ODIV { + // a / (-1) is -a. + gins(optoas(gc.OMINUS, t), nil, &ax) + + gmove(&ax, res) + } else { + // a % (-1) is 0. + gc.Nodconst(&n4, t, 0) + + gmove(&n4, res) + } + + p2 = gc.Gbranch(obj.AJMP, nil, 0) + gc.Patch(p1, gc.Pc) + } + + var olddx gc.Node + var dx gc.Node + savex(x86.REG_DX, &dx, &olddx, res, t) + if !gc.Issigned[t.Etype] { + gc.Nodconst(&n4, t, 0) + gmove(&n4, &dx) + } else { + gins(optoas(gc.OEXTEND, t), nil, nil) + } + gins(a, &n3, nil) + gc.Regfree(&n3) + if op == gc.ODIV { + gmove(&ax, res) + } else { + gmove(&dx, res) + } + restx(&dx, &olddx) + if check != 0 { + gc.Patch(p2, gc.Pc) + } + restx(&ax, &oldax) +} + +/* + * register dr is one of the special ones (AX, CX, DI, SI, etc.). + * we need to use it. if it is already allocated as a temporary + * (r > 1; can only happen if a routine like sgen passed a + * special as cgen's res and then cgen used regalloc to reuse + * it as its own temporary), then move it for now to another + * register. caller must call restx to move it back. + * the move is not necessary if dr == res, because res is + * known to be dead. + */ +func savex(dr int, x *gc.Node, oldx *gc.Node, res *gc.Node, t *gc.Type) { + r := reg[dr] + + // save current ax and dx if they are live + // and not the destination + *oldx = gc.Node{} + + gc.Nodreg(x, t, dr) + if r > 1 && !gc.Samereg(x, res) { + gc.Regalloc(oldx, gc.Types[gc.TINT64], nil) + x.Type = gc.Types[gc.TINT64] + gmove(x, oldx) + x.Type = t + oldx.Etype = r // squirrel away old r value + reg[dr] = 1 + } +} + +func restx(x *gc.Node, oldx *gc.Node) { + if oldx.Op != 0 { + x.Type = gc.Types[gc.TINT64] + reg[x.Reg] = oldx.Etype + gmove(oldx, x) + gc.Regfree(oldx) + } +} + +/* + * generate high multiply: + * res = (nl*nr) >> width + */ +func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) { + t := nl.Type + a := optoas(gc.OHMUL, t) + if nl.Ullman < nr.Ullman { + tmp := nl + nl = nr + nr = tmp + } + + var n1 gc.Node + gc.Cgenr(nl, &n1, res) + var n2 gc.Node + gc.Cgenr(nr, &n2, nil) + var ax gc.Node + gc.Nodreg(&ax, t, x86.REG_AX) + gmove(&n1, &ax) + gins(a, &n2, nil) + gc.Regfree(&n2) + gc.Regfree(&n1) + + var dx gc.Node + if t.Width == 1 { + // byte multiply behaves differently. + gc.Nodreg(&ax, t, x86.REG_AH) + + gc.Nodreg(&dx, t, x86.REG_DX) + gmove(&ax, &dx) + } + + gc.Nodreg(&dx, t, x86.REG_DX) + gmove(&dx, res) +} + +/* + * generate shift according to op, one of: + * res = nl << nr + * res = nl >> nr + */ +func cgen_shift(op int, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) { + a := optoas(op, nl.Type) + + if nr.Op == gc.OLITERAL { + var n1 gc.Node + gc.Regalloc(&n1, nl.Type, res) + gc.Cgen(nl, &n1) + sc := uint64(nr.Int()) + if sc >= uint64(nl.Type.Width*8) { + // large shift gets 2 shifts by width-1 + var n3 gc.Node + gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1) + + gins(a, &n3, &n1) + gins(a, &n3, &n1) + } else { + gins(a, nr, &n1) + } + gmove(&n1, res) + gc.Regfree(&n1) + return + } + + if nl.Ullman >= gc.UINF { + var n4 gc.Node + gc.Tempname(&n4, nl.Type) + gc.Cgen(nl, &n4) + nl = &n4 + } + + if nr.Ullman >= gc.UINF { + var n5 gc.Node + gc.Tempname(&n5, nr.Type) + gc.Cgen(nr, &n5) + nr = &n5 + } + + rcx := int(reg[x86.REG_CX]) + var n1 gc.Node + gc.Nodreg(&n1, gc.Types[gc.TUINT32], x86.REG_CX) + + // Allow either uint32 or uint64 as shift type, + // to avoid unnecessary conversion from uint32 to uint64 + // just to do the comparison. + tcount := gc.Types[gc.Simtype[nr.Type.Etype]] + + if tcount.Etype < gc.TUINT32 { + tcount = gc.Types[gc.TUINT32] + } + + gc.Regalloc(&n1, nr.Type, &n1) // to hold the shift type in CX + var n3 gc.Node + gc.Regalloc(&n3, tcount, &n1) // to clear high bits of CX + + var cx gc.Node + gc.Nodreg(&cx, gc.Types[gc.TUINT64], x86.REG_CX) + + var oldcx gc.Node + if rcx > 0 && !gc.Samereg(&cx, res) { + gc.Regalloc(&oldcx, gc.Types[gc.TUINT64], nil) + gmove(&cx, &oldcx) + } + + cx.Type = tcount + + var n2 gc.Node + if gc.Samereg(&cx, res) { + gc.Regalloc(&n2, nl.Type, nil) + } else { + gc.Regalloc(&n2, nl.Type, res) + } + if nl.Ullman >= nr.Ullman { + gc.Cgen(nl, &n2) + gc.Cgen(nr, &n1) + gmove(&n1, &n3) + } else { + gc.Cgen(nr, &n1) + gmove(&n1, &n3) + gc.Cgen(nl, &n2) + } + + gc.Regfree(&n3) + + // test and fix up large shifts + if !bounded { + gc.Nodconst(&n3, tcount, nl.Type.Width*8) + gins(optoas(gc.OCMP, tcount), &n1, &n3) + p1 := gc.Gbranch(optoas(gc.OLT, tcount), nil, +1) + if op == gc.ORSH && gc.Issigned[nl.Type.Etype] { + gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1) + gins(a, &n3, &n2) + } else { + gc.Nodconst(&n3, nl.Type, 0) + gmove(&n3, &n2) + } + + gc.Patch(p1, gc.Pc) + } + + gins(a, &n1, &n2) + + if oldcx.Op != 0 { + cx.Type = gc.Types[gc.TUINT64] + gmove(&oldcx, &cx) + gc.Regfree(&oldcx) + } + + gmove(&n2, res) + + gc.Regfree(&n1) + gc.Regfree(&n2) +} + +/* + * generate byte multiply: + * res = nl * nr + * there is no 2-operand byte multiply instruction so + * we do a full-width multiplication and truncate afterwards. + */ +func cgen_bmul(op int, nl *gc.Node, nr *gc.Node, res *gc.Node) bool { + if optoas(op, nl.Type) != x86.AIMULB { + return false + } + + // largest ullman on left. + if nl.Ullman < nr.Ullman { + tmp := nl + nl = nr + nr = tmp + } + + // generate operands in "8-bit" registers. + var n1b gc.Node + gc.Regalloc(&n1b, nl.Type, res) + + gc.Cgen(nl, &n1b) + var n2b gc.Node + gc.Regalloc(&n2b, nr.Type, nil) + gc.Cgen(nr, &n2b) + + // perform full-width multiplication. + t := gc.Types[gc.TUINT64] + + if gc.Issigned[nl.Type.Etype] { + t = gc.Types[gc.TINT64] + } + var n1 gc.Node + gc.Nodreg(&n1, t, int(n1b.Reg)) + var n2 gc.Node + gc.Nodreg(&n2, t, int(n2b.Reg)) + a := optoas(op, t) + gins(a, &n2, &n1) + + // truncate. + gmove(&n1, res) + + gc.Regfree(&n1b) + gc.Regfree(&n2b) + return true +} + +func clearfat(nl *gc.Node) { + /* clear a fat object */ + if gc.Debug['g'] != 0 { + gc.Dump("\nclearfat", nl) + } + + w := nl.Type.Width + + // Avoid taking the address for simple enough types. + if gc.Componentgen(nil, nl) { + return + } + + c := w % 8 // bytes + q := w / 8 // quads + + if q < 4 { + // Write sequence of MOV 0, off(base) instead of using STOSQ. + // The hope is that although the code will be slightly longer, + // the MOVs will have no dependencies and pipeline better + // than the unrolled STOSQ loop. + // NOTE: Must use agen, not igen, so that optimizer sees address + // being taken. We are not writing on field boundaries. + var n1 gc.Node + gc.Agenr(nl, &n1, nil) + + n1.Op = gc.OINDREG + var z gc.Node + gc.Nodconst(&z, gc.Types[gc.TUINT64], 0) + for { + tmp14 := q + q-- + if tmp14 <= 0 { + break + } + n1.Type = z.Type + gins(x86.AMOVQ, &z, &n1) + n1.Xoffset += 8 + } + + if c >= 4 { + gc.Nodconst(&z, gc.Types[gc.TUINT32], 0) + n1.Type = z.Type + gins(x86.AMOVL, &z, &n1) + n1.Xoffset += 4 + c -= 4 + } + + gc.Nodconst(&z, gc.Types[gc.TUINT8], 0) + for { + tmp15 := c + c-- + if tmp15 <= 0 { + break + } + n1.Type = z.Type + gins(x86.AMOVB, &z, &n1) + n1.Xoffset++ + } + + gc.Regfree(&n1) + return + } + + var oldn1 gc.Node + var n1 gc.Node + savex(x86.REG_DI, &n1, &oldn1, nil, gc.Types[gc.Tptr]) + gc.Agen(nl, &n1) + + var ax gc.Node + var oldax gc.Node + savex(x86.REG_AX, &ax, &oldax, nil, gc.Types[gc.Tptr]) + gconreg(x86.AMOVL, 0, x86.REG_AX) + + if q > 128 || gc.Nacl { + gconreg(movptr, q, x86.REG_CX) + gins(x86.AREP, nil, nil) // repeat + gins(x86.ASTOSQ, nil, nil) // STOQ AL,*(DI)+ + } else { + if di := dzDI(q); di != 0 { + gconreg(addptr, di, x86.REG_DI) + } + p := gins(obj.ADUFFZERO, nil, nil) + p.To.Type = obj.TYPE_ADDR + p.To.Sym = gc.Linksym(gc.Pkglookup("duffzero", gc.Runtimepkg)) + p.To.Offset = dzOff(q) + } + + z := ax + di := n1 + if w >= 8 && c >= 4 { + di.Op = gc.OINDREG + z.Type = gc.Types[gc.TINT64] + di.Type = z.Type + p := gins(x86.AMOVQ, &z, &di) + p.To.Scale = 1 + p.To.Offset = c - 8 + } else if c >= 4 { + di.Op = gc.OINDREG + z.Type = gc.Types[gc.TINT32] + di.Type = z.Type + gins(x86.AMOVL, &z, &di) + if c > 4 { + p := gins(x86.AMOVL, &z, &di) + p.To.Scale = 1 + p.To.Offset = c - 4 + } + } else { + for c > 0 { + gins(x86.ASTOSB, nil, nil) // STOB AL,*(DI)+ + c-- + } + } + + restx(&n1, &oldn1) + restx(&ax, &oldax) +} + +// Called after regopt and peep have run. +// Expand CHECKNIL pseudo-op into actual nil pointer check. +func expandchecks(firstp *obj.Prog) { + var p1 *obj.Prog + var p2 *obj.Prog + + for p := firstp; p != nil; p = p.Link { + if p.As != obj.ACHECKNIL { + continue + } + if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers + gc.Warnl(int(p.Lineno), "generated nil check") + } + + // check is + // CMP arg, $0 + // JNE 2(PC) (likely) + // MOV AX, 0 + p1 = gc.Ctxt.NewProg() + + p2 = gc.Ctxt.NewProg() + gc.Clearp(p1) + gc.Clearp(p2) + p1.Link = p2 + p2.Link = p.Link + p.Link = p1 + p1.Lineno = p.Lineno + p2.Lineno = p.Lineno + p1.Pc = 9999 + p2.Pc = 9999 + p.As = int16(cmpptr) + p.To.Type = obj.TYPE_CONST + p.To.Offset = 0 + p1.As = x86.AJNE + p1.From.Type = obj.TYPE_CONST + p1.From.Offset = 1 // likely + p1.To.Type = obj.TYPE_BRANCH + p1.To.Val = p2.Link + + // crash by write to memory address 0. + // if possible, since we know arg is 0, use 0(arg), + // which will be shorter to encode than plain 0. + p2.As = x86.AMOVL + + p2.From.Type = obj.TYPE_REG + p2.From.Reg = x86.REG_AX + if regtyp(&p.From) { + p2.To.Type = obj.TYPE_MEM + p2.To.Reg = p.From.Reg + } else { + p2.To.Type = obj.TYPE_MEM + p2.To.Reg = x86.REG_NONE + } + + p2.To.Offset = 0 + } +} + +// addr += index*width if possible. +func addindex(index *gc.Node, width int64, addr *gc.Node) bool { + switch width { + case 1, 2, 4, 8: + p1 := gins(x86.ALEAQ, index, addr) + p1.From.Type = obj.TYPE_MEM + p1.From.Scale = int16(width) + p1.From.Index = p1.From.Reg + p1.From.Reg = p1.To.Reg + return true + } + return false +} + +// res = runtime.getg() +func getg(res *gc.Node) { + var n1 gc.Node + gc.Regalloc(&n1, res.Type, res) + mov := optoas(gc.OAS, gc.Types[gc.Tptr]) + p := gins(mov, nil, &n1) + p.From.Type = obj.TYPE_REG + p.From.Reg = x86.REG_TLS + p = gins(mov, nil, &n1) + p.From = p.To + p.From.Type = obj.TYPE_MEM + p.From.Index = x86.REG_TLS + p.From.Scale = 1 + gmove(&n1, res) + gc.Regfree(&n1) +} diff --git a/src/cmd/compile/internal/amd64/gsubr.go b/src/cmd/compile/internal/amd64/gsubr.go new file mode 100644 index 0000000000..a8e4170bee --- /dev/null +++ b/src/cmd/compile/internal/amd64/gsubr.go @@ -0,0 +1,1380 @@ +// Derived from Inferno utils/6c/txt.c +// http://code.google.com/p/inferno-os/source/browse/utils/6c/txt.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package amd64 + +import ( + "cmd/compile/internal/big" + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/x86" + "fmt" +) + +var resvd = []int{ + x86.REG_DI, // for movstring + x86.REG_SI, // for movstring + + x86.REG_AX, // for divide + x86.REG_CX, // for shift + x86.REG_DX, // for divide + x86.REG_SP, // for stack +} + +/* + * generate + * as $c, reg + */ +func gconreg(as int, c int64, reg int) { + var nr gc.Node + + switch as { + case x86.AADDL, + x86.AMOVL, + x86.ALEAL: + gc.Nodreg(&nr, gc.Types[gc.TINT32], reg) + + default: + gc.Nodreg(&nr, gc.Types[gc.TINT64], reg) + } + + ginscon(as, c, &nr) +} + +/* + * generate + * as $c, n + */ +func ginscon(as int, c int64, n2 *gc.Node) { + var n1 gc.Node + + switch as { + case x86.AADDL, + x86.AMOVL, + x86.ALEAL: + gc.Nodconst(&n1, gc.Types[gc.TINT32], c) + + default: + gc.Nodconst(&n1, gc.Types[gc.TINT64], c) + } + + if as != x86.AMOVQ && (c < -(1<<31) || c >= 1<<31) { + // cannot have 64-bit immediate in ADD, etc. + // instead, MOV into register first. + var ntmp gc.Node + gc.Regalloc(&ntmp, gc.Types[gc.TINT64], nil) + + gins(x86.AMOVQ, &n1, &ntmp) + gins(as, &ntmp, n2) + gc.Regfree(&ntmp) + return + } + + 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) +} + +// set up nodes representing 2^63 +var ( + bigi gc.Node + bigf gc.Node + bignodes_did bool +) + +func bignodes() { + if bignodes_did { + return + } + bignodes_did = true + + var i big.Int + i.SetInt64(1) + i.Lsh(&i, 63) + + gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0) + bigi.SetBigInt(&i) + + bigi.Convconst(&bigf, gc.Types[gc.TFLOAT64]) +} + +/* + * generate move: + * t = f + * hard part is conversions. + */ +func gmove(f *gc.Node, t *gc.Node) { + if gc.Debug['M'] != 0 { + fmt.Printf("gmove %v -> %v\n", gc.Nconv(f, obj.FmtLong), gc.Nconv(t, obj.FmtLong)) + } + + ft := gc.Simsimtype(f.Type) + tt := gc.Simsimtype(t.Type) + cvt := t.Type + + if gc.Iscomplex[ft] || gc.Iscomplex[tt] { + gc.Complexmove(f, t) + return + } + + // cannot have two memory operands + var a int + if gc.Ismem(f) && gc.Ismem(t) { + goto hard + } + + // convert constant to desired type + if f.Op == gc.OLITERAL { + var con gc.Node + f.Convconst(&con, t.Type) + f = &con + ft = tt // so big switch will choose a simple mov + + // some constants can't move directly to memory. + if gc.Ismem(t) { + // float constants come from memory. + if gc.Isfloat[tt] { + goto hard + } + + // 64-bit immediates are really 32-bit sign-extended + // unless moving into a register. + if gc.Isint[tt] { + if i := con.Int(); int64(int32(i)) != i { + goto hard + } + } + } + } + + // value -> value copy, only one memory operand. + // figure out the instruction to use. + // break out of switch for one-instruction gins. + // goto rdst for "destination must be register". + // goto hard for "convert to cvt type first". + // otherwise handle and return. + + switch uint32(ft)<<16 | uint32(tt) { + default: + gc.Fatal("gmove %v -> %v", gc.Tconv(f.Type, obj.FmtLong), gc.Tconv(t.Type, obj.FmtLong)) + + /* + * integer copy and truncate + */ + case gc.TINT8<<16 | gc.TINT8, // same size + gc.TINT8<<16 | gc.TUINT8, + gc.TUINT8<<16 | gc.TINT8, + gc.TUINT8<<16 | gc.TUINT8, + gc.TINT16<<16 | gc.TINT8, + // truncate + gc.TUINT16<<16 | gc.TINT8, + gc.TINT32<<16 | gc.TINT8, + gc.TUINT32<<16 | gc.TINT8, + gc.TINT64<<16 | gc.TINT8, + gc.TUINT64<<16 | gc.TINT8, + gc.TINT16<<16 | gc.TUINT8, + gc.TUINT16<<16 | gc.TUINT8, + gc.TINT32<<16 | gc.TUINT8, + gc.TUINT32<<16 | gc.TUINT8, + gc.TINT64<<16 | gc.TUINT8, + gc.TUINT64<<16 | gc.TUINT8: + a = x86.AMOVB + + case gc.TINT16<<16 | gc.TINT16, // same size + gc.TINT16<<16 | gc.TUINT16, + gc.TUINT16<<16 | gc.TINT16, + gc.TUINT16<<16 | gc.TUINT16, + gc.TINT32<<16 | gc.TINT16, + // truncate + gc.TUINT32<<16 | gc.TINT16, + gc.TINT64<<16 | gc.TINT16, + gc.TUINT64<<16 | gc.TINT16, + gc.TINT32<<16 | gc.TUINT16, + gc.TUINT32<<16 | gc.TUINT16, + gc.TINT64<<16 | gc.TUINT16, + gc.TUINT64<<16 | gc.TUINT16: + a = x86.AMOVW + + case gc.TINT32<<16 | gc.TINT32, // same size + gc.TINT32<<16 | gc.TUINT32, + gc.TUINT32<<16 | gc.TINT32, + gc.TUINT32<<16 | gc.TUINT32: + a = x86.AMOVL + + case gc.TINT64<<16 | gc.TINT32, // truncate + gc.TUINT64<<16 | gc.TINT32, + gc.TINT64<<16 | gc.TUINT32, + gc.TUINT64<<16 | gc.TUINT32: + a = x86.AMOVQL + + case gc.TINT64<<16 | gc.TINT64, // same size + gc.TINT64<<16 | gc.TUINT64, + gc.TUINT64<<16 | gc.TINT64, + gc.TUINT64<<16 | gc.TUINT64: + a = x86.AMOVQ + + /* + * integer up-conversions + */ + case gc.TINT8<<16 | gc.TINT16, // sign extend int8 + gc.TINT8<<16 | gc.TUINT16: + a = x86.AMOVBWSX + + goto rdst + + case gc.TINT8<<16 | gc.TINT32, + gc.TINT8<<16 | gc.TUINT32: + a = x86.AMOVBLSX + goto rdst + + case gc.TINT8<<16 | gc.TINT64, + gc.TINT8<<16 | gc.TUINT64: + a = x86.AMOVBQSX + goto rdst + + case gc.TUINT8<<16 | gc.TINT16, // zero extend uint8 + gc.TUINT8<<16 | gc.TUINT16: + a = x86.AMOVBWZX + + goto rdst + + case gc.TUINT8<<16 | gc.TINT32, + gc.TUINT8<<16 | gc.TUINT32: + a = x86.AMOVBLZX + goto rdst + + case gc.TUINT8<<16 | gc.TINT64, + gc.TUINT8<<16 | gc.TUINT64: + a = x86.AMOVBQZX + goto rdst + + case gc.TINT16<<16 | gc.TINT32, // sign extend int16 + gc.TINT16<<16 | gc.TUINT32: + a = x86.AMOVWLSX + + goto rdst + + case gc.TINT16<<16 | gc.TINT64, + gc.TINT16<<16 | gc.TUINT64: + a = x86.AMOVWQSX + goto rdst + + case gc.TUINT16<<16 | gc.TINT32, // zero extend uint16 + gc.TUINT16<<16 | gc.TUINT32: + a = x86.AMOVWLZX + + goto rdst + + case gc.TUINT16<<16 | gc.TINT64, + gc.TUINT16<<16 | gc.TUINT64: + a = x86.AMOVWQZX + goto rdst + + case gc.TINT32<<16 | gc.TINT64, // sign extend int32 + gc.TINT32<<16 | gc.TUINT64: + a = x86.AMOVLQSX + + goto rdst + + // AMOVL into a register zeros the top of the register, + // so this is not always necessary, but if we rely on AMOVL + // the optimizer is almost certain to screw with us. + case gc.TUINT32<<16 | gc.TINT64, // zero extend uint32 + gc.TUINT32<<16 | gc.TUINT64: + a = x86.AMOVLQZX + + goto rdst + + /* + * float to integer + */ + case gc.TFLOAT32<<16 | gc.TINT32: + a = x86.ACVTTSS2SL + + goto rdst + + case gc.TFLOAT64<<16 | gc.TINT32: + a = x86.ACVTTSD2SL + goto rdst + + case gc.TFLOAT32<<16 | gc.TINT64: + a = x86.ACVTTSS2SQ + goto rdst + + case gc.TFLOAT64<<16 | gc.TINT64: + a = x86.ACVTTSD2SQ + goto rdst + + // convert via int32. + case gc.TFLOAT32<<16 | gc.TINT16, + gc.TFLOAT32<<16 | gc.TINT8, + gc.TFLOAT32<<16 | gc.TUINT16, + gc.TFLOAT32<<16 | gc.TUINT8, + gc.TFLOAT64<<16 | gc.TINT16, + gc.TFLOAT64<<16 | gc.TINT8, + gc.TFLOAT64<<16 | gc.TUINT16, + gc.TFLOAT64<<16 | gc.TUINT8: + cvt = gc.Types[gc.TINT32] + + goto hard + + // convert via int64. + case gc.TFLOAT32<<16 | gc.TUINT32, + gc.TFLOAT64<<16 | gc.TUINT32: + cvt = gc.Types[gc.TINT64] + + goto hard + + // algorithm is: + // if small enough, use native float64 -> int64 conversion. + // otherwise, subtract 2^63, convert, and add it back. + case gc.TFLOAT32<<16 | gc.TUINT64, + gc.TFLOAT64<<16 | gc.TUINT64: + a := x86.ACVTTSS2SQ + + if ft == gc.TFLOAT64 { + a = x86.ACVTTSD2SQ + } + bignodes() + var r1 gc.Node + gc.Regalloc(&r1, gc.Types[ft], nil) + var r2 gc.Node + gc.Regalloc(&r2, gc.Types[tt], t) + var r3 gc.Node + gc.Regalloc(&r3, gc.Types[ft], nil) + var r4 gc.Node + gc.Regalloc(&r4, gc.Types[tt], nil) + gins(optoas(gc.OAS, f.Type), f, &r1) + gins(optoas(gc.OCMP, f.Type), &bigf, &r1) + p1 := gc.Gbranch(optoas(gc.OLE, f.Type), nil, +1) + gins(a, &r1, &r2) + p2 := gc.Gbranch(obj.AJMP, nil, 0) + gc.Patch(p1, gc.Pc) + gins(optoas(gc.OAS, f.Type), &bigf, &r3) + gins(optoas(gc.OSUB, f.Type), &r3, &r1) + gins(a, &r1, &r2) + gins(x86.AMOVQ, &bigi, &r4) + gins(x86.AXORQ, &r4, &r2) + gc.Patch(p2, gc.Pc) + gmove(&r2, t) + gc.Regfree(&r4) + gc.Regfree(&r3) + gc.Regfree(&r2) + gc.Regfree(&r1) + return + + /* + * integer to float + */ + case gc.TINT32<<16 | gc.TFLOAT32: + a = x86.ACVTSL2SS + + goto rdst + + case gc.TINT32<<16 | gc.TFLOAT64: + a = x86.ACVTSL2SD + goto rdst + + case gc.TINT64<<16 | gc.TFLOAT32: + a = x86.ACVTSQ2SS + goto rdst + + case gc.TINT64<<16 | gc.TFLOAT64: + a = x86.ACVTSQ2SD + goto rdst + + // convert via int32 + case gc.TINT16<<16 | gc.TFLOAT32, + gc.TINT16<<16 | gc.TFLOAT64, + gc.TINT8<<16 | gc.TFLOAT32, + gc.TINT8<<16 | gc.TFLOAT64, + gc.TUINT16<<16 | gc.TFLOAT32, + gc.TUINT16<<16 | gc.TFLOAT64, + gc.TUINT8<<16 | gc.TFLOAT32, + gc.TUINT8<<16 | gc.TFLOAT64: + cvt = gc.Types[gc.TINT32] + + goto hard + + // convert via int64. + case gc.TUINT32<<16 | gc.TFLOAT32, + gc.TUINT32<<16 | gc.TFLOAT64: + cvt = gc.Types[gc.TINT64] + + goto hard + + // algorithm is: + // if small enough, use native int64 -> uint64 conversion. + // otherwise, halve (rounding to odd?), convert, and double. + case gc.TUINT64<<16 | gc.TFLOAT32, + gc.TUINT64<<16 | gc.TFLOAT64: + a := x86.ACVTSQ2SS + + if tt == gc.TFLOAT64 { + a = x86.ACVTSQ2SD + } + var zero gc.Node + gc.Nodconst(&zero, gc.Types[gc.TUINT64], 0) + var one gc.Node + gc.Nodconst(&one, gc.Types[gc.TUINT64], 1) + var r1 gc.Node + gc.Regalloc(&r1, f.Type, f) + var r2 gc.Node + gc.Regalloc(&r2, t.Type, t) + var r3 gc.Node + gc.Regalloc(&r3, f.Type, nil) + var r4 gc.Node + gc.Regalloc(&r4, f.Type, nil) + gmove(f, &r1) + gins(x86.ACMPQ, &r1, &zero) + p1 := gc.Gbranch(x86.AJLT, nil, +1) + gins(a, &r1, &r2) + p2 := gc.Gbranch(obj.AJMP, nil, 0) + gc.Patch(p1, gc.Pc) + gmove(&r1, &r3) + gins(x86.ASHRQ, &one, &r3) + gmove(&r1, &r4) + gins(x86.AANDL, &one, &r4) + gins(x86.AORQ, &r4, &r3) + gins(a, &r3, &r2) + gins(optoas(gc.OADD, t.Type), &r2, &r2) + gc.Patch(p2, gc.Pc) + gmove(&r2, t) + gc.Regfree(&r4) + gc.Regfree(&r3) + gc.Regfree(&r2) + gc.Regfree(&r1) + return + + /* + * float to float + */ + case gc.TFLOAT32<<16 | gc.TFLOAT32: + a = x86.AMOVSS + + case gc.TFLOAT64<<16 | gc.TFLOAT64: + a = x86.AMOVSD + + case gc.TFLOAT32<<16 | gc.TFLOAT64: + a = x86.ACVTSS2SD + goto rdst + + case gc.TFLOAT64<<16 | gc.TFLOAT32: + a = x86.ACVTSD2SS + goto rdst + } + + gins(a, f, t) + return + + // requires register destination +rdst: + { + var r1 gc.Node + gc.Regalloc(&r1, t.Type, t) + + gins(a, f, &r1) + gmove(&r1, t) + gc.Regfree(&r1) + return + } + + // requires register intermediate +hard: + var r1 gc.Node + gc.Regalloc(&r1, cvt, t) + + gmove(f, &r1) + gmove(&r1, t) + gc.Regfree(&r1) + return +} + +func samaddr(f *gc.Node, t *gc.Node) bool { + if f.Op != t.Op { + return false + } + + switch f.Op { + case gc.OREGISTER: + if f.Reg != t.Reg { + break + } + return true + } + + return false +} + +/* + * generate one instruction: + * as f, t + */ +func gins(as int, f *gc.Node, t *gc.Node) *obj.Prog { + // Node nod; + + // if(f != N && f->op == OINDEX) { + // gc.Regalloc(&nod, ®node, Z); + // v = constnode.vconst; + // gc.Cgen(f->right, &nod); + // constnode.vconst = v; + // idx.reg = nod.reg; + // gc.Regfree(&nod); + // } + // if(t != N && t->op == OINDEX) { + // gc.Regalloc(&nod, ®node, Z); + // v = constnode.vconst; + // gc.Cgen(t->right, &nod); + // constnode.vconst = v; + // idx.reg = nod.reg; + // gc.Regfree(&nod); + // } + + if f != nil && f.Op == gc.OADDR && (as == x86.AMOVL || as == x86.AMOVQ) { + // Turn MOVL $xxx into LEAL xxx. + // These should be equivalent but most of the backend + // only expects to see LEAL, because that's what we had + // historically generated. Various hidden assumptions are baked in by now. + if as == x86.AMOVL { + as = x86.ALEAL + } else { + as = x86.ALEAQ + } + f = f.Left + } + + switch as { + case x86.AMOVB, + x86.AMOVW, + x86.AMOVL, + x86.AMOVQ, + x86.AMOVSS, + x86.AMOVSD: + if f != nil && t != nil && samaddr(f, t) { + return nil + } + + case x86.ALEAQ: + if f != nil && gc.Isconst(f, gc.CTNIL) { + gc.Fatal("gins LEAQ nil %v", f.Type) + } + } + + p := gc.Prog(as) + gc.Naddr(&p.From, f) + gc.Naddr(&p.To, t) + + if gc.Debug['g'] != 0 { + fmt.Printf("%v\n", p) + } + + w := int32(0) + switch as { + case x86.AMOVB: + w = 1 + + case x86.AMOVW: + w = 2 + + case x86.AMOVL: + w = 4 + + case x86.AMOVQ: + w = 8 + } + + if w != 0 && ((f != nil && p.From.Width < int64(w)) || (t != nil && p.To.Width > int64(w))) { + gc.Dump("f", f) + gc.Dump("t", t) + gc.Fatal("bad width: %v (%d, %d)\n", p, p.From.Width, p.To.Width) + } + + if p.To.Type == obj.TYPE_ADDR && w > 0 { + gc.Fatal("bad use of addr: %v", p) + } + + return p +} + +func ginsnop() { + // This is actually not the x86 NOP anymore, + // but at the point where it gets used, AX is dead + // so it's okay if we lose the high bits. + var reg gc.Node + gc.Nodreg(®, gc.Types[gc.TINT], x86.REG_AX) + gins(x86.AXCHGL, ®, ®) +} + +/* + * return Axxx for Oxxx on type t. + */ +func optoas(op int, t *gc.Type) int { + if t == nil { + gc.Fatal("optoas: t is nil") + } + + a := obj.AXXX + switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) { + default: + gc.Fatal("optoas: no entry %v-%v", gc.Oconv(int(op), 0), t) + + case gc.OADDR<<16 | gc.TPTR32: + a = x86.ALEAL + + case gc.OADDR<<16 | gc.TPTR64: + a = x86.ALEAQ + + case gc.OEQ<<16 | gc.TBOOL, + gc.OEQ<<16 | gc.TINT8, + gc.OEQ<<16 | gc.TUINT8, + gc.OEQ<<16 | gc.TINT16, + gc.OEQ<<16 | gc.TUINT16, + gc.OEQ<<16 | gc.TINT32, + gc.OEQ<<16 | gc.TUINT32, + gc.OEQ<<16 | gc.TINT64, + gc.OEQ<<16 | gc.TUINT64, + gc.OEQ<<16 | gc.TPTR32, + gc.OEQ<<16 | gc.TPTR64, + gc.OEQ<<16 | gc.TFLOAT32, + gc.OEQ<<16 | gc.TFLOAT64: + a = x86.AJEQ + + case gc.ONE<<16 | gc.TBOOL, + gc.ONE<<16 | gc.TINT8, + gc.ONE<<16 | gc.TUINT8, + gc.ONE<<16 | gc.TINT16, + gc.ONE<<16 | gc.TUINT16, + gc.ONE<<16 | gc.TINT32, + gc.ONE<<16 | gc.TUINT32, + gc.ONE<<16 | gc.TINT64, + gc.ONE<<16 | gc.TUINT64, + gc.ONE<<16 | gc.TPTR32, + gc.ONE<<16 | gc.TPTR64, + gc.ONE<<16 | gc.TFLOAT32, + gc.ONE<<16 | gc.TFLOAT64: + a = x86.AJNE + + case gc.OPS<<16 | gc.TBOOL, + gc.OPS<<16 | gc.TINT8, + gc.OPS<<16 | gc.TUINT8, + gc.OPS<<16 | gc.TINT16, + gc.OPS<<16 | gc.TUINT16, + gc.OPS<<16 | gc.TINT32, + gc.OPS<<16 | gc.TUINT32, + gc.OPS<<16 | gc.TINT64, + gc.OPS<<16 | gc.TUINT64, + gc.OPS<<16 | gc.TPTR32, + gc.OPS<<16 | gc.TPTR64, + gc.OPS<<16 | gc.TFLOAT32, + gc.OPS<<16 | gc.TFLOAT64: + a = x86.AJPS + + case gc.OPC<<16 | gc.TBOOL, + gc.OPC<<16 | gc.TINT8, + gc.OPC<<16 | gc.TUINT8, + gc.OPC<<16 | gc.TINT16, + gc.OPC<<16 | gc.TUINT16, + gc.OPC<<16 | gc.TINT32, + gc.OPC<<16 | gc.TUINT32, + gc.OPC<<16 | gc.TINT64, + gc.OPC<<16 | gc.TUINT64, + gc.OPC<<16 | gc.TPTR32, + gc.OPC<<16 | gc.TPTR64, + gc.OPC<<16 | gc.TFLOAT32, + gc.OPC<<16 | gc.TFLOAT64: + a = x86.AJPC + + case gc.OLT<<16 | gc.TINT8, + gc.OLT<<16 | gc.TINT16, + gc.OLT<<16 | gc.TINT32, + gc.OLT<<16 | gc.TINT64: + a = x86.AJLT + + case gc.OLT<<16 | gc.TUINT8, + gc.OLT<<16 | gc.TUINT16, + gc.OLT<<16 | gc.TUINT32, + gc.OLT<<16 | gc.TUINT64: + a = x86.AJCS + + case gc.OLE<<16 | gc.TINT8, + gc.OLE<<16 | gc.TINT16, + gc.OLE<<16 | gc.TINT32, + gc.OLE<<16 | gc.TINT64: + a = x86.AJLE + + case gc.OLE<<16 | gc.TUINT8, + gc.OLE<<16 | gc.TUINT16, + gc.OLE<<16 | gc.TUINT32, + gc.OLE<<16 | gc.TUINT64: + a = x86.AJLS + + case gc.OGT<<16 | gc.TINT8, + gc.OGT<<16 | gc.TINT16, + gc.OGT<<16 | gc.TINT32, + gc.OGT<<16 | gc.TINT64: + a = x86.AJGT + + case gc.OGT<<16 | gc.TUINT8, + gc.OGT<<16 | gc.TUINT16, + gc.OGT<<16 | gc.TUINT32, + gc.OGT<<16 | gc.TUINT64, + gc.OLT<<16 | gc.TFLOAT32, + gc.OLT<<16 | gc.TFLOAT64: + a = x86.AJHI + + case gc.OGE<<16 | gc.TINT8, + gc.OGE<<16 | gc.TINT16, + gc.OGE<<16 | gc.TINT32, + gc.OGE<<16 | gc.TINT64: + a = x86.AJGE + + case gc.OGE<<16 | gc.TUINT8, + gc.OGE<<16 | gc.TUINT16, + gc.OGE<<16 | gc.TUINT32, + gc.OGE<<16 | gc.TUINT64, + gc.OLE<<16 | gc.TFLOAT32, + gc.OLE<<16 | gc.TFLOAT64: + a = x86.AJCC + + case gc.OCMP<<16 | gc.TBOOL, + gc.OCMP<<16 | gc.TINT8, + gc.OCMP<<16 | gc.TUINT8: + a = x86.ACMPB + + case gc.OCMP<<16 | gc.TINT16, + gc.OCMP<<16 | gc.TUINT16: + a = x86.ACMPW + + case gc.OCMP<<16 | gc.TINT32, + gc.OCMP<<16 | gc.TUINT32, + gc.OCMP<<16 | gc.TPTR32: + a = x86.ACMPL + + case gc.OCMP<<16 | gc.TINT64, + gc.OCMP<<16 | gc.TUINT64, + gc.OCMP<<16 | gc.TPTR64: + a = x86.ACMPQ + + case gc.OCMP<<16 | gc.TFLOAT32: + a = x86.AUCOMISS + + case gc.OCMP<<16 | gc.TFLOAT64: + a = x86.AUCOMISD + + case gc.OAS<<16 | gc.TBOOL, + gc.OAS<<16 | gc.TINT8, + gc.OAS<<16 | gc.TUINT8: + a = x86.AMOVB + + case gc.OAS<<16 | gc.TINT16, + gc.OAS<<16 | gc.TUINT16: + a = x86.AMOVW + + case gc.OAS<<16 | gc.TINT32, + gc.OAS<<16 | gc.TUINT32, + gc.OAS<<16 | gc.TPTR32: + a = x86.AMOVL + + case gc.OAS<<16 | gc.TINT64, + gc.OAS<<16 | gc.TUINT64, + gc.OAS<<16 | gc.TPTR64: + a = x86.AMOVQ + + case gc.OAS<<16 | gc.TFLOAT32: + a = x86.AMOVSS + + case gc.OAS<<16 | gc.TFLOAT64: + a = x86.AMOVSD + + case gc.OADD<<16 | gc.TINT8, + gc.OADD<<16 | gc.TUINT8: + a = x86.AADDB + + case gc.OADD<<16 | gc.TINT16, + gc.OADD<<16 | gc.TUINT16: + a = x86.AADDW + + case gc.OADD<<16 | gc.TINT32, + gc.OADD<<16 | gc.TUINT32, + gc.OADD<<16 | gc.TPTR32: + a = x86.AADDL + + case gc.OADD<<16 | gc.TINT64, + gc.OADD<<16 | gc.TUINT64, + gc.OADD<<16 | gc.TPTR64: + a = x86.AADDQ + + case gc.OADD<<16 | gc.TFLOAT32: + a = x86.AADDSS + + case gc.OADD<<16 | gc.TFLOAT64: + a = x86.AADDSD + + case gc.OSUB<<16 | gc.TINT8, + gc.OSUB<<16 | gc.TUINT8: + a = x86.ASUBB + + case gc.OSUB<<16 | gc.TINT16, + gc.OSUB<<16 | gc.TUINT16: + a = x86.ASUBW + + case gc.OSUB<<16 | gc.TINT32, + gc.OSUB<<16 | gc.TUINT32, + gc.OSUB<<16 | gc.TPTR32: + a = x86.ASUBL + + case gc.OSUB<<16 | gc.TINT64, + gc.OSUB<<16 | gc.TUINT64, + gc.OSUB<<16 | gc.TPTR64: + a = x86.ASUBQ + + case gc.OSUB<<16 | gc.TFLOAT32: + a = x86.ASUBSS + + case gc.OSUB<<16 | gc.TFLOAT64: + a = x86.ASUBSD + + case gc.OINC<<16 | gc.TINT8, + gc.OINC<<16 | gc.TUINT8: + a = x86.AINCB + + case gc.OINC<<16 | gc.TINT16, + gc.OINC<<16 | gc.TUINT16: + a = x86.AINCW + + case gc.OINC<<16 | gc.TINT32, + gc.OINC<<16 | gc.TUINT32, + gc.OINC<<16 | gc.TPTR32: + a = x86.AINCL + + case gc.OINC<<16 | gc.TINT64, + gc.OINC<<16 | gc.TUINT64, + gc.OINC<<16 | gc.TPTR64: + a = x86.AINCQ + + case gc.ODEC<<16 | gc.TINT8, + gc.ODEC<<16 | gc.TUINT8: + a = x86.ADECB + + case gc.ODEC<<16 | gc.TINT16, + gc.ODEC<<16 | gc.TUINT16: + a = x86.ADECW + + case gc.ODEC<<16 | gc.TINT32, + gc.ODEC<<16 | gc.TUINT32, + gc.ODEC<<16 | gc.TPTR32: + a = x86.ADECL + + case gc.ODEC<<16 | gc.TINT64, + gc.ODEC<<16 | gc.TUINT64, + gc.ODEC<<16 | gc.TPTR64: + a = x86.ADECQ + + case gc.OMINUS<<16 | gc.TINT8, + gc.OMINUS<<16 | gc.TUINT8: + a = x86.ANEGB + + case gc.OMINUS<<16 | gc.TINT16, + gc.OMINUS<<16 | gc.TUINT16: + a = x86.ANEGW + + case gc.OMINUS<<16 | gc.TINT32, + gc.OMINUS<<16 | gc.TUINT32, + gc.OMINUS<<16 | gc.TPTR32: + a = x86.ANEGL + + case gc.OMINUS<<16 | gc.TINT64, + gc.OMINUS<<16 | gc.TUINT64, + gc.OMINUS<<16 | gc.TPTR64: + a = x86.ANEGQ + + case gc.OAND<<16 | gc.TBOOL, + gc.OAND<<16 | gc.TINT8, + gc.OAND<<16 | gc.TUINT8: + a = x86.AANDB + + case gc.OAND<<16 | gc.TINT16, + gc.OAND<<16 | gc.TUINT16: + a = x86.AANDW + + case gc.OAND<<16 | gc.TINT32, + gc.OAND<<16 | gc.TUINT32, + gc.OAND<<16 | gc.TPTR32: + a = x86.AANDL + + case gc.OAND<<16 | gc.TINT64, + gc.OAND<<16 | gc.TUINT64, + gc.OAND<<16 | gc.TPTR64: + a = x86.AANDQ + + case gc.OOR<<16 | gc.TBOOL, + gc.OOR<<16 | gc.TINT8, + gc.OOR<<16 | gc.TUINT8: + a = x86.AORB + + case gc.OOR<<16 | gc.TINT16, + gc.OOR<<16 | gc.TUINT16: + a = x86.AORW + + case gc.OOR<<16 | gc.TINT32, + gc.OOR<<16 | gc.TUINT32, + gc.OOR<<16 | gc.TPTR32: + a = x86.AORL + + case gc.OOR<<16 | gc.TINT64, + gc.OOR<<16 | gc.TUINT64, + gc.OOR<<16 | gc.TPTR64: + a = x86.AORQ + + case gc.OXOR<<16 | gc.TINT8, + gc.OXOR<<16 | gc.TUINT8: + a = x86.AXORB + + case gc.OXOR<<16 | gc.TINT16, + gc.OXOR<<16 | gc.TUINT16: + a = x86.AXORW + + case gc.OXOR<<16 | gc.TINT32, + gc.OXOR<<16 | gc.TUINT32, + gc.OXOR<<16 | gc.TPTR32: + a = x86.AXORL + + case gc.OXOR<<16 | gc.TINT64, + gc.OXOR<<16 | gc.TUINT64, + gc.OXOR<<16 | gc.TPTR64: + a = x86.AXORQ + + case gc.OLROT<<16 | gc.TINT8, + gc.OLROT<<16 | gc.TUINT8: + a = x86.AROLB + + case gc.OLROT<<16 | gc.TINT16, + gc.OLROT<<16 | gc.TUINT16: + a = x86.AROLW + + case gc.OLROT<<16 | gc.TINT32, + gc.OLROT<<16 | gc.TUINT32, + gc.OLROT<<16 | gc.TPTR32: + a = x86.AROLL + + case gc.OLROT<<16 | gc.TINT64, + gc.OLROT<<16 | gc.TUINT64, + gc.OLROT<<16 | gc.TPTR64: + a = x86.AROLQ + + case gc.OLSH<<16 | gc.TINT8, + gc.OLSH<<16 | gc.TUINT8: + a = x86.ASHLB + + case gc.OLSH<<16 | gc.TINT16, + gc.OLSH<<16 | gc.TUINT16: + a = x86.ASHLW + + case gc.OLSH<<16 | gc.TINT32, + gc.OLSH<<16 | gc.TUINT32, + gc.OLSH<<16 | gc.TPTR32: + a = x86.ASHLL + + case gc.OLSH<<16 | gc.TINT64, + gc.OLSH<<16 | gc.TUINT64, + gc.OLSH<<16 | gc.TPTR64: + a = x86.ASHLQ + + case gc.ORSH<<16 | gc.TUINT8: + a = x86.ASHRB + + case gc.ORSH<<16 | gc.TUINT16: + a = x86.ASHRW + + case gc.ORSH<<16 | gc.TUINT32, + gc.ORSH<<16 | gc.TPTR32: + a = x86.ASHRL + + case gc.ORSH<<16 | gc.TUINT64, + gc.ORSH<<16 | gc.TPTR64: + a = x86.ASHRQ + + case gc.ORSH<<16 | gc.TINT8: + a = x86.ASARB + + case gc.ORSH<<16 | gc.TINT16: + a = x86.ASARW + + case gc.ORSH<<16 | gc.TINT32: + a = x86.ASARL + + case gc.ORSH<<16 | gc.TINT64: + a = x86.ASARQ + + case gc.ORROTC<<16 | gc.TINT8, + gc.ORROTC<<16 | gc.TUINT8: + a = x86.ARCRB + + case gc.ORROTC<<16 | gc.TINT16, + gc.ORROTC<<16 | gc.TUINT16: + a = x86.ARCRW + + case gc.ORROTC<<16 | gc.TINT32, + gc.ORROTC<<16 | gc.TUINT32: + a = x86.ARCRL + + case gc.ORROTC<<16 | gc.TINT64, + gc.ORROTC<<16 | gc.TUINT64: + a = x86.ARCRQ + + case gc.OHMUL<<16 | gc.TINT8, + gc.OMUL<<16 | gc.TINT8, + gc.OMUL<<16 | gc.TUINT8: + a = x86.AIMULB + + case gc.OHMUL<<16 | gc.TINT16, + gc.OMUL<<16 | gc.TINT16, + gc.OMUL<<16 | gc.TUINT16: + a = x86.AIMULW + + case gc.OHMUL<<16 | gc.TINT32, + gc.OMUL<<16 | gc.TINT32, + gc.OMUL<<16 | gc.TUINT32, + gc.OMUL<<16 | gc.TPTR32: + a = x86.AIMULL + + case gc.OHMUL<<16 | gc.TINT64, + gc.OMUL<<16 | gc.TINT64, + gc.OMUL<<16 | gc.TUINT64, + gc.OMUL<<16 | gc.TPTR64: + a = x86.AIMULQ + + case gc.OHMUL<<16 | gc.TUINT8: + a = x86.AMULB + + case gc.OHMUL<<16 | gc.TUINT16: + a = x86.AMULW + + case gc.OHMUL<<16 | gc.TUINT32, + gc.OHMUL<<16 | gc.TPTR32: + a = x86.AMULL + + case gc.OHMUL<<16 | gc.TUINT64, + gc.OHMUL<<16 | gc.TPTR64: + a = x86.AMULQ + + case gc.OMUL<<16 | gc.TFLOAT32: + a = x86.AMULSS + + case gc.OMUL<<16 | gc.TFLOAT64: + a = x86.AMULSD + + case gc.ODIV<<16 | gc.TINT8, + gc.OMOD<<16 | gc.TINT8: + a = x86.AIDIVB + + case gc.ODIV<<16 | gc.TUINT8, + gc.OMOD<<16 | gc.TUINT8: + a = x86.ADIVB + + case gc.ODIV<<16 | gc.TINT16, + gc.OMOD<<16 | gc.TINT16: + a = x86.AIDIVW + + case gc.ODIV<<16 | gc.TUINT16, + gc.OMOD<<16 | gc.TUINT16: + a = x86.ADIVW + + case gc.ODIV<<16 | gc.TINT32, + gc.OMOD<<16 | gc.TINT32: + a = x86.AIDIVL + + case gc.ODIV<<16 | gc.TUINT32, + gc.ODIV<<16 | gc.TPTR32, + gc.OMOD<<16 | gc.TUINT32, + gc.OMOD<<16 | gc.TPTR32: + a = x86.ADIVL + + case gc.ODIV<<16 | gc.TINT64, + gc.OMOD<<16 | gc.TINT64: + a = x86.AIDIVQ + + case gc.ODIV<<16 | gc.TUINT64, + gc.ODIV<<16 | gc.TPTR64, + gc.OMOD<<16 | gc.TUINT64, + gc.OMOD<<16 | gc.TPTR64: + a = x86.ADIVQ + + case gc.OEXTEND<<16 | gc.TINT16: + a = x86.ACWD + + case gc.OEXTEND<<16 | gc.TINT32: + a = x86.ACDQ + + case gc.OEXTEND<<16 | gc.TINT64: + a = x86.ACQO + + case gc.ODIV<<16 | gc.TFLOAT32: + a = x86.ADIVSS + + case gc.ODIV<<16 | gc.TFLOAT64: + a = x86.ADIVSD + + case gc.OSQRT<<16 | gc.TFLOAT64: + a = x86.ASQRTSD + } + + return a +} + +// jmptoset returns ASETxx for AJxx. +func jmptoset(jmp int) int { + switch jmp { + case x86.AJEQ: + return x86.ASETEQ + case x86.AJNE: + return x86.ASETNE + case x86.AJLT: + return x86.ASETLT + case x86.AJCS: + return x86.ASETCS + case x86.AJLE: + return x86.ASETLE + case x86.AJLS: + return x86.ASETLS + case x86.AJGT: + return x86.ASETGT + case x86.AJHI: + return x86.ASETHI + case x86.AJGE: + return x86.ASETGE + case x86.AJCC: + return x86.ASETCC + case x86.AJMI: + return x86.ASETMI + case x86.AJOC: + return x86.ASETOC + case x86.AJOS: + return x86.ASETOS + case x86.AJPC: + return x86.ASETPC + case x86.AJPL: + return x86.ASETPL + case x86.AJPS: + return x86.ASETPS + } + gc.Fatal("jmptoset: no entry for %v", gc.Oconv(jmp, 0)) + panic("unreachable") +} + +const ( + ODynam = 1 << 0 + OAddable = 1 << 1 +) + +var clean [20]gc.Node + +var cleani int = 0 + +func sudoclean() { + if clean[cleani-1].Op != gc.OEMPTY { + gc.Regfree(&clean[cleani-1]) + } + if clean[cleani-2].Op != gc.OEMPTY { + gc.Regfree(&clean[cleani-2]) + } + cleani -= 2 +} + +/* + * generate code to compute address of n, + * a reference to a (perhaps nested) field inside + * an array or struct. + * return 0 on failure, 1 on success. + * on success, leaves usable address in a. + * + * caller is responsible for calling sudoclean + * after successful sudoaddable, + * to release the register used for a. + */ +func sudoaddable(as int, n *gc.Node, a *obj.Addr) bool { + if n.Type == nil { + return false + } + + *a = obj.Addr{} + + switch n.Op { + case gc.OLITERAL: + if !gc.Isconst(n, gc.CTINT) { + break + } + v := n.Int() + if v >= 32000 || v <= -32000 { + break + } + switch as { + default: + return false + + case x86.AADDB, + x86.AADDW, + x86.AADDL, + x86.AADDQ, + x86.ASUBB, + x86.ASUBW, + x86.ASUBL, + x86.ASUBQ, + x86.AANDB, + x86.AANDW, + x86.AANDL, + x86.AANDQ, + x86.AORB, + x86.AORW, + x86.AORL, + x86.AORQ, + x86.AXORB, + x86.AXORW, + x86.AXORL, + x86.AXORQ, + x86.AINCB, + x86.AINCW, + x86.AINCL, + x86.AINCQ, + x86.ADECB, + x86.ADECW, + x86.ADECL, + x86.ADECQ, + x86.AMOVB, + x86.AMOVW, + x86.AMOVL, + x86.AMOVQ: + break + } + + cleani += 2 + reg := &clean[cleani-1] + reg1 := &clean[cleani-2] + reg.Op = gc.OEMPTY + reg1.Op = gc.OEMPTY + gc.Naddr(a, n) + return true + + case gc.ODOT, + gc.ODOTPTR: + cleani += 2 + reg := &clean[cleani-1] + reg1 := &clean[cleani-2] + reg.Op = gc.OEMPTY + reg1.Op = gc.OEMPTY + var nn *gc.Node + var oary [10]int64 + o := gc.Dotoffset(n, oary[:], &nn) + if nn == nil { + sudoclean() + return false + } + + if nn.Addable && o == 1 && oary[0] >= 0 { + // directly addressable set of DOTs + n1 := *nn + + n1.Type = n.Type + n1.Xoffset += oary[0] + gc.Naddr(a, &n1) + return true + } + + gc.Regalloc(reg, gc.Types[gc.Tptr], nil) + n1 := *reg + n1.Op = gc.OINDREG + if oary[0] >= 0 { + gc.Agen(nn, reg) + n1.Xoffset = oary[0] + } else { + gc.Cgen(nn, reg) + gc.Cgen_checknil(reg) + n1.Xoffset = -(oary[0] + 1) + } + + for i := 1; i < o; i++ { + if oary[i] >= 0 { + gc.Fatal("can't happen") + } + gins(movptr, &n1, reg) + gc.Cgen_checknil(reg) + n1.Xoffset = -(oary[i] + 1) + } + + a.Type = obj.TYPE_NONE + a.Index = obj.TYPE_NONE + gc.Fixlargeoffset(&n1) + gc.Naddr(a, &n1) + return true + + case gc.OINDEX: + return false + } + + return false +} diff --git a/src/cmd/compile/internal/amd64/peep.go b/src/cmd/compile/internal/amd64/peep.go new file mode 100644 index 0000000000..19db68e944 --- /dev/null +++ b/src/cmd/compile/internal/amd64/peep.go @@ -0,0 +1,1038 @@ +// Derived from Inferno utils/6c/peep.c +// http://code.google.com/p/inferno-os/source/browse/utils/6c/peep.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package amd64 + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/x86" + "fmt" +) + +var gactive uint32 + +const ( + exregoffset = x86.REG_R15 +) + +// do we need the carry bit +func needc(p *obj.Prog) bool { + for p != nil { + flags := progcarryflags(p) + if flags&gc.UseCarry != 0 { + return true + } + if flags&(gc.SetCarry|gc.KillCarry) != 0 { + return false + } + p = p.Link + } + + return false +} + +func rnops(r *gc.Flow) *gc.Flow { + if r != nil { + var p *obj.Prog + var r1 *gc.Flow + for { + p = r.Prog + if p.As != obj.ANOP || p.From.Type != obj.TYPE_NONE || p.To.Type != obj.TYPE_NONE { + break + } + r1 = gc.Uniqs(r) + if r1 == nil { + break + } + r = r1 + } + } + + return r +} + +func peep(firstp *obj.Prog) { + g := (*gc.Graph)(gc.Flowstart(firstp, nil)) + if g == nil { + return + } + gactive = 0 + + // byte, word arithmetic elimination. + elimshortmov(g) + + // constant propagation + // find MOV $con,R followed by + // another MOV $con,R without + // setting R in the interim + var p *obj.Prog + for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { + p = r.Prog + switch p.As { + case x86.ALEAL, + x86.ALEAQ: + if regtyp(&p.To) { + if p.From.Sym != nil { + if p.From.Index == x86.REG_NONE { + conprop(r) + } + } + } + + case x86.AMOVB, + x86.AMOVW, + x86.AMOVL, + x86.AMOVQ, + x86.AMOVSS, + x86.AMOVSD: + if regtyp(&p.To) { + if p.From.Type == obj.TYPE_CONST || p.From.Type == obj.TYPE_FCONST { + conprop(r) + } + } + } + } + + var r *gc.Flow + var r1 *gc.Flow + var p1 *obj.Prog + var t int +loop1: + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + gc.Dumpit("loop1", g.Start, 0) + } + + t = 0 + for r = g.Start; r != nil; r = r.Link { + p = r.Prog + switch p.As { + case x86.AMOVL, + x86.AMOVQ, + x86.AMOVSS, + x86.AMOVSD: + if regtyp(&p.To) { + if regtyp(&p.From) { + if copyprop(g, r) { + excise(r) + t++ + } else if subprop(r) && copyprop(g, r) { + excise(r) + t++ + } + } + } + + case x86.AMOVBLZX, + x86.AMOVWLZX, + x86.AMOVBLSX, + x86.AMOVWLSX: + if regtyp(&p.To) { + r1 = rnops(gc.Uniqs(r)) + if r1 != nil { + p1 = r1.Prog + if p.As == p1.As && p.To.Type == p1.From.Type && p.To.Reg == p1.From.Reg { + p1.As = x86.AMOVL + t++ + } + } + } + + case x86.AMOVBQSX, + x86.AMOVBQZX, + x86.AMOVWQSX, + x86.AMOVWQZX, + x86.AMOVLQSX, + x86.AMOVLQZX, + x86.AMOVQL: + if regtyp(&p.To) { + r1 = rnops(gc.Uniqs(r)) + if r1 != nil { + p1 = r1.Prog + if p.As == p1.As && p.To.Type == p1.From.Type && p.To.Reg == p1.From.Reg { + p1.As = x86.AMOVQ + t++ + } + } + } + + case x86.AADDL, + x86.AADDQ, + x86.AADDW: + if p.From.Type != obj.TYPE_CONST || needc(p.Link) { + break + } + if p.From.Offset == -1 { + if p.As == x86.AADDQ { + p.As = x86.ADECQ + } else if p.As == x86.AADDL { + p.As = x86.ADECL + } else { + p.As = x86.ADECW + } + p.From = obj.Addr{} + break + } + + if p.From.Offset == 1 { + if p.As == x86.AADDQ { + p.As = x86.AINCQ + } else if p.As == x86.AADDL { + p.As = x86.AINCL + } else { + p.As = x86.AINCW + } + p.From = obj.Addr{} + break + } + + case x86.ASUBL, + x86.ASUBQ, + x86.ASUBW: + if p.From.Type != obj.TYPE_CONST || needc(p.Link) { + break + } + if p.From.Offset == -1 { + if p.As == x86.ASUBQ { + p.As = x86.AINCQ + } else if p.As == x86.ASUBL { + p.As = x86.AINCL + } else { + p.As = x86.AINCW + } + p.From = obj.Addr{} + break + } + + if p.From.Offset == 1 { + if p.As == x86.ASUBQ { + p.As = x86.ADECQ + } else if p.As == x86.ASUBL { + p.As = x86.ADECL + } else { + p.As = x86.ADECW + } + p.From = obj.Addr{} + break + } + } + } + + if t != 0 { + goto loop1 + } + + // MOVLQZX removal. + // The MOVLQZX exists to avoid being confused for a + // MOVL that is just copying 32-bit data around during + // copyprop. Now that copyprop is done, remov MOVLQZX R1, R2 + // if it is dominated by an earlier ADDL/MOVL/etc into R1 that + // will have already cleared the high bits. + // + // MOVSD removal. + // We never use packed registers, so a MOVSD between registers + // can be replaced by MOVAPD, which moves the pair of float64s + // instead of just the lower one. We only use the lower one, but + // the processor can do better if we do moves using both. + for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { + p = r.Prog + if p.As == x86.AMOVLQZX { + if regtyp(&p.From) { + if p.From.Type == p.To.Type && p.From.Reg == p.To.Reg { + if prevl(r, int(p.From.Reg)) { + excise(r) + } + } + } + } + + if p.As == x86.AMOVSD { + if regtyp(&p.From) { + if regtyp(&p.To) { + p.As = x86.AMOVAPD + } + } + } + } + + // load pipelining + // push any load from memory as early as possible + // to give it time to complete before use. + for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { + p = r.Prog + switch p.As { + case x86.AMOVB, + x86.AMOVW, + x86.AMOVL, + x86.AMOVQ, + x86.AMOVLQZX: + if regtyp(&p.To) && !regconsttyp(&p.From) { + pushback(r) + } + } + } + + gc.Flowend(g) +} + +func pushback(r0 *gc.Flow) { + var r *gc.Flow + var p *obj.Prog + + var b *gc.Flow + p0 := (*obj.Prog)(r0.Prog) + for r = gc.Uniqp(r0); r != nil && gc.Uniqs(r) != nil; r = gc.Uniqp(r) { + p = r.Prog + if p.As != obj.ANOP { + if !regconsttyp(&p.From) || !regtyp(&p.To) { + break + } + if copyu(p, &p0.To, nil) != 0 || copyu(p0, &p.To, nil) != 0 { + break + } + } + + if p.As == obj.ACALL { + break + } + b = r + } + + if b == nil { + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + fmt.Printf("no pushback: %v\n", r0.Prog) + if r != nil { + fmt.Printf("\t%v [%v]\n", r.Prog, gc.Uniqs(r) != nil) + } + } + + return + } + + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + fmt.Printf("pushback\n") + for r := (*gc.Flow)(b); ; r = r.Link { + fmt.Printf("\t%v\n", r.Prog) + if r == r0 { + break + } + } + } + + t := obj.Prog(*r0.Prog) + for r = gc.Uniqp(r0); ; r = gc.Uniqp(r) { + p0 = r.Link.Prog + p = r.Prog + p0.As = p.As + p0.Lineno = p.Lineno + p0.From = p.From + p0.To = p.To + + if r == b { + break + } + } + + p0 = r.Prog + p0.As = t.As + p0.Lineno = t.Lineno + p0.From = t.From + p0.To = t.To + + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + fmt.Printf("\tafter\n") + for r := (*gc.Flow)(b); ; r = r.Link { + fmt.Printf("\t%v\n", r.Prog) + if r == r0 { + break + } + } + } +} + +func excise(r *gc.Flow) { + p := (*obj.Prog)(r.Prog) + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + fmt.Printf("%v ===delete===\n", p) + } + + obj.Nopout(p) + + gc.Ostats.Ndelmov++ +} + +func regtyp(a *obj.Addr) bool { + return a.Type == obj.TYPE_REG && (x86.REG_AX <= a.Reg && a.Reg <= x86.REG_R15 || x86.REG_X0 <= a.Reg && a.Reg <= x86.REG_X15) +} + +// movb elimination. +// movb is simulated by the linker +// when a register other than ax, bx, cx, dx +// is used, so rewrite to other instructions +// when possible. a movb into a register +// can smash the entire 32-bit register without +// causing any trouble. +// +// TODO: Using the Q forms here instead of the L forms +// seems unnecessary, and it makes the instructions longer. +func elimshortmov(g *gc.Graph) { + var p *obj.Prog + + for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { + p = r.Prog + if regtyp(&p.To) { + switch p.As { + case x86.AINCB, + x86.AINCW: + p.As = x86.AINCQ + + case x86.ADECB, + x86.ADECW: + p.As = x86.ADECQ + + case x86.ANEGB, + x86.ANEGW: + p.As = x86.ANEGQ + + case x86.ANOTB, + x86.ANOTW: + p.As = x86.ANOTQ + } + + if regtyp(&p.From) || p.From.Type == obj.TYPE_CONST { + // move or artihmetic into partial register. + // from another register or constant can be movl. + // we don't switch to 64-bit arithmetic if it can + // change how the carry bit is set (and the carry bit is needed). + switch p.As { + case x86.AMOVB, + x86.AMOVW: + p.As = x86.AMOVQ + + case x86.AADDB, + x86.AADDW: + if !needc(p.Link) { + p.As = x86.AADDQ + } + + case x86.ASUBB, + x86.ASUBW: + if !needc(p.Link) { + p.As = x86.ASUBQ + } + + case x86.AMULB, + x86.AMULW: + p.As = x86.AMULQ + + case x86.AIMULB, + x86.AIMULW: + p.As = x86.AIMULQ + + case x86.AANDB, + x86.AANDW: + p.As = x86.AANDQ + + case x86.AORB, + x86.AORW: + p.As = x86.AORQ + + case x86.AXORB, + x86.AXORW: + p.As = x86.AXORQ + + case x86.ASHLB, + x86.ASHLW: + p.As = x86.ASHLQ + } + } else if p.From.Type != obj.TYPE_REG { + // explicit zero extension, but don't + // do that if source is a byte register + // (only AH can occur and it's forbidden). + switch p.As { + case x86.AMOVB: + p.As = x86.AMOVBQZX + + case x86.AMOVW: + p.As = x86.AMOVWQZX + } + } + } + } +} + +// is 'a' a register or constant? +func regconsttyp(a *obj.Addr) bool { + if regtyp(a) { + return true + } + switch a.Type { + case obj.TYPE_CONST, + obj.TYPE_FCONST, + obj.TYPE_SCONST, + obj.TYPE_ADDR: // TODO(rsc): Not all TYPE_ADDRs are constants. + return true + } + + return false +} + +// is reg guaranteed to be truncated by a previous L instruction? +func prevl(r0 *gc.Flow, reg int) bool { + for r := (*gc.Flow)(gc.Uniqp(r0)); r != nil; r = gc.Uniqp(r) { + p := r.Prog + if p.To.Type == obj.TYPE_REG && int(p.To.Reg) == reg { + flags := progflags(p) + if flags&gc.RightWrite != 0 { + if flags&gc.SizeL != 0 { + return true + } + return false + } + } + } + + return false +} + +/* + * the idea is to substitute + * one register for another + * from one MOV to another + * MOV a, R0 + * ADD b, R0 / no use of R1 + * MOV R0, R1 + * would be converted to + * MOV a, R1 + * ADD b, R1 + * MOV R1, R0 + * hopefully, then the former or latter MOV + * will be eliminated by copy propagation. + */ +func subprop(r0 *gc.Flow) bool { + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + fmt.Printf("subprop %v\n", r0.Prog) + } + p := (*obj.Prog)(r0.Prog) + v1 := (*obj.Addr)(&p.From) + if !regtyp(v1) { + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + fmt.Printf("\tnot regtype %v; return 0\n", gc.Ctxt.Dconv(v1)) + } + return false + } + + v2 := (*obj.Addr)(&p.To) + if !regtyp(v2) { + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + fmt.Printf("\tnot regtype %v; return 0\n", gc.Ctxt.Dconv(v2)) + } + return false + } + + for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + fmt.Printf("\t? %v\n", r.Prog) + } + if gc.Uniqs(r) == nil { + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + fmt.Printf("\tno unique successor\n") + } + break + } + + p = r.Prog + if p.As == obj.AVARDEF || p.As == obj.AVARKILL { + continue + } + if p.Info.Flags&gc.Call != 0 { + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + fmt.Printf("\tfound %v; return 0\n", p) + } + return false + } + + if p.Info.Reguse|p.Info.Regset != 0 { + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + fmt.Printf("\tfound %v; return 0\n", p) + } + return false + } + + if (p.Info.Flags&gc.Move != 0) && (p.Info.Flags&(gc.SizeL|gc.SizeQ|gc.SizeF|gc.SizeD) != 0) && p.To.Type == v1.Type && p.To.Reg == v1.Reg { + copysub(&p.To, v1, v2, 1) + if gc.Debug['P'] != 0 { + fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog) + if p.From.Type == v2.Type && p.From.Reg == v2.Reg { + fmt.Printf(" excise") + } + fmt.Printf("\n") + } + + for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { + p = r.Prog + copysub(&p.From, v1, v2, 1) + copysub(&p.To, v1, v2, 1) + if gc.Debug['P'] != 0 { + fmt.Printf("%v\n", r.Prog) + } + } + + t := int(int(v1.Reg)) + v1.Reg = v2.Reg + v2.Reg = int16(t) + if gc.Debug['P'] != 0 { + fmt.Printf("%v last\n", r.Prog) + } + return true + } + + if copyau(&p.From, v2) || copyau(&p.To, v2) { + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + fmt.Printf("\tcopyau %v failed\n", gc.Ctxt.Dconv(v2)) + } + break + } + + if copysub(&p.From, v1, v2, 0) != 0 || copysub(&p.To, v1, v2, 0) != 0 { + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + fmt.Printf("\tcopysub failed\n") + } + break + } + } + + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + fmt.Printf("\tran off end; return 0\n") + } + return false +} + +/* + * The idea is to remove redundant copies. + * v1->v2 F=0 + * (use v2 s/v2/v1/)* + * set v1 F=1 + * use v2 return fail + * ----------------- + * v1->v2 F=0 + * (use v2 s/v2/v1/)* + * set v1 F=1 + * set v2 return success + */ +func copyprop(g *gc.Graph, r0 *gc.Flow) bool { + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + fmt.Printf("copyprop %v\n", r0.Prog) + } + p := (*obj.Prog)(r0.Prog) + v1 := (*obj.Addr)(&p.From) + v2 := (*obj.Addr)(&p.To) + if copyas(v1, v2) { + return true + } + gactive++ + return copy1(v1, v2, r0.S1, 0) +} + +func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f int) bool { + if uint32(r.Active) == gactive { + if gc.Debug['P'] != 0 { + fmt.Printf("act set; return 1\n") + } + return true + } + + r.Active = int32(gactive) + if gc.Debug['P'] != 0 { + fmt.Printf("copy %v->%v f=%d\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), f) + } + var t int + var p *obj.Prog + for ; r != nil; r = r.S1 { + p = r.Prog + if gc.Debug['P'] != 0 { + fmt.Printf("%v", p) + } + if f == 0 && gc.Uniqp(r) == nil { + f = 1 + if gc.Debug['P'] != 0 { + fmt.Printf("; merge; f=%d", f) + } + } + + t = copyu(p, v2, nil) + switch t { + case 2: /* rar, can't split */ + if gc.Debug['P'] != 0 { + fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2)) + } + return false + + case 3: /* set */ + if gc.Debug['P'] != 0 { + fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2)) + } + return true + + case 1, /* used, substitute */ + 4: /* use and set */ + if f != 0 { + if gc.Debug['P'] == 0 { + return false + } + if t == 4 { + fmt.Printf("; %v used+set and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f) + } else { + fmt.Printf("; %v used and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f) + } + return false + } + + if copyu(p, v2, v1) != 0 { + if gc.Debug['P'] != 0 { + fmt.Printf("; sub fail; return 0\n") + } + return false + } + + if gc.Debug['P'] != 0 { + fmt.Printf("; sub %v/%v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1)) + } + if t == 4 { + if gc.Debug['P'] != 0 { + fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2)) + } + return true + } + } + + if f == 0 { + t = copyu(p, v1, nil) + if f == 0 && (t == 2 || t == 3 || t == 4) { + f = 1 + if gc.Debug['P'] != 0 { + fmt.Printf("; %v set and !f; f=%d", gc.Ctxt.Dconv(v1), f) + } + } + } + + if gc.Debug['P'] != 0 { + fmt.Printf("\n") + } + if r.S2 != nil { + if !copy1(v1, v2, r.S2, f) { + return false + } + } + } + + return true +} + +/* + * return + * 1 if v only used (and substitute), + * 2 if read-alter-rewrite + * 3 if set + * 4 if set and used + * 0 otherwise (not touched) + */ +func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int { + switch p.As { + case obj.AJMP: + if s != nil { + if copysub(&p.To, v, s, 1) != 0 { + return 1 + } + return 0 + } + + if copyau(&p.To, v) { + return 1 + } + return 0 + + case obj.ARET: + if s != nil { + return 1 + } + return 3 + + case obj.ACALL: + if x86.REGEXT != 0 /*TypeKind(100016)*/ && v.Type == obj.TYPE_REG && v.Reg <= x86.REGEXT && v.Reg > exregoffset { + return 2 + } + if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG { + return 2 + } + if v.Type == p.From.Type && v.Reg == p.From.Reg { + return 2 + } + + if s != nil { + if copysub(&p.To, v, s, 1) != 0 { + return 1 + } + return 0 + } + + if copyau(&p.To, v) { + return 4 + } + return 3 + + case obj.ATEXT: + if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG { + return 3 + } + return 0 + } + + if p.As == obj.AVARDEF || p.As == obj.AVARKILL { + return 0 + } + + if (p.Info.Reguse|p.Info.Regset)&RtoB(int(v.Reg)) != 0 { + return 2 + } + + if p.Info.Flags&gc.LeftAddr != 0 { + if copyas(&p.From, v) { + return 2 + } + } + + if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightRead|gc.RightWrite { + if copyas(&p.To, v) { + return 2 + } + } + + if p.Info.Flags&gc.RightWrite != 0 { + if copyas(&p.To, v) { + if s != nil { + return copysub(&p.From, v, s, 1) + } + if copyau(&p.From, v) { + return 4 + } + return 3 + } + } + + if p.Info.Flags&(gc.LeftAddr|gc.LeftRead|gc.LeftWrite|gc.RightAddr|gc.RightRead|gc.RightWrite) != 0 { + if s != nil { + if copysub(&p.From, v, s, 1) != 0 { + return 1 + } + return copysub(&p.To, v, s, 1) + } + + if copyau(&p.From, v) { + return 1 + } + if copyau(&p.To, v) { + return 1 + } + } + + return 0 +} + +/* + * direct reference, + * could be set/use depending on + * semantics + */ +func copyas(a *obj.Addr, v *obj.Addr) bool { + if x86.REG_AL <= a.Reg && a.Reg <= x86.REG_R15B { + gc.Fatal("use of byte register") + } + if x86.REG_AL <= v.Reg && v.Reg <= x86.REG_R15B { + gc.Fatal("use of byte register") + } + + if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg { + return false + } + if regtyp(v) { + return true + } + if v.Type == obj.TYPE_MEM && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) { + if v.Offset == a.Offset { + return true + } + } + return false +} + +func sameaddr(a *obj.Addr, v *obj.Addr) bool { + if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg { + return false + } + if regtyp(v) { + return true + } + if v.Type == obj.TYPE_MEM && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) { + if v.Offset == a.Offset { + return true + } + } + return false +} + +/* + * either direct or indirect + */ +func copyau(a *obj.Addr, v *obj.Addr) bool { + if copyas(a, v) { + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + fmt.Printf("\tcopyau: copyas returned 1\n") + } + return true + } + + if regtyp(v) { + if a.Type == obj.TYPE_MEM && a.Reg == v.Reg { + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + fmt.Printf("\tcopyau: found indir use - return 1\n") + } + return true + } + + if a.Index == v.Reg { + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + fmt.Printf("\tcopyau: found index use - return 1\n") + } + return true + } + } + + return false +} + +/* + * substitute s for v in a + * return failure to substitute + */ +func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f int) int { + if copyas(a, v) { + reg := int(int(s.Reg)) + if reg >= x86.REG_AX && reg <= x86.REG_R15 || reg >= x86.REG_X0 && reg <= x86.REG_X0+15 { + if f != 0 { + a.Reg = int16(reg) + } + } + + return 0 + } + + if regtyp(v) { + reg := int(int(v.Reg)) + if a.Type == obj.TYPE_MEM && int(a.Reg) == reg { + if (s.Reg == x86.REG_BP || s.Reg == x86.REG_R13) && a.Index != x86.REG_NONE { + return 1 /* can't use BP-base with index */ + } + if f != 0 { + a.Reg = s.Reg + } + } + + // return 0; + if int(a.Index) == reg { + if f != 0 { + a.Index = s.Reg + } + return 0 + } + + return 0 + } + + return 0 +} + +func conprop(r0 *gc.Flow) { + var p *obj.Prog + var t int + + p0 := (*obj.Prog)(r0.Prog) + v0 := (*obj.Addr)(&p0.To) + r := (*gc.Flow)(r0) + +loop: + r = gc.Uniqs(r) + if r == nil || r == r0 { + return + } + if gc.Uniqp(r) == nil { + return + } + + p = r.Prog + t = copyu(p, v0, nil) + switch t { + case 0, // miss + 1: // use + goto loop + + case 2, // rar + 4: // use and set + break + + case 3: // set + if p.As == p0.As { + if p.From.Type == p0.From.Type { + if p.From.Reg == p0.From.Reg { + if p.From.Node == p0.From.Node { + if p.From.Offset == p0.From.Offset { + if p.From.Scale == p0.From.Scale { + if p.From.Type == obj.TYPE_FCONST && p.From.Val.(float64) == p0.From.Val.(float64) { + if p.From.Index == p0.From.Index { + excise(r) + goto loop + } + } + } + } + } + } + } + } + } +} + +func smallindir(a *obj.Addr, reg *obj.Addr) bool { + return regtyp(reg) && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && a.Index == x86.REG_NONE && 0 <= a.Offset && a.Offset < 4096 +} + +func stackaddr(a *obj.Addr) bool { + return a.Type == obj.TYPE_REG && a.Reg == x86.REG_SP +} diff --git a/src/cmd/compile/internal/amd64/prog.go b/src/cmd/compile/internal/amd64/prog.go new file mode 100644 index 0000000000..00918c8691 --- /dev/null +++ b/src/cmd/compile/internal/amd64/prog.go @@ -0,0 +1,308 @@ +// Copyright 2013 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 amd64 + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/x86" +) + +const ( + LeftRdwr uint32 = gc.LeftRead | gc.LeftWrite + RightRdwr uint32 = gc.RightRead | gc.RightWrite +) + +// This table gives the basic information about instruction +// generated by the compiler and processed in the optimizer. +// See opt.h for bit definitions. +// +// Instructions not generated need not be listed. +// As an exception to that rule, we typically write down all the +// size variants of an operation even if we just use a subset. +// +// The table is formatted for 8-space tabs. +var progtable = [x86.ALAST]obj.ProgInfo{ + obj.ATYPE: {gc.Pseudo | gc.Skip, 0, 0, 0}, + obj.ATEXT: {gc.Pseudo, 0, 0, 0}, + obj.AFUNCDATA: {gc.Pseudo, 0, 0, 0}, + obj.APCDATA: {gc.Pseudo, 0, 0, 0}, + obj.AUNDEF: {gc.Break, 0, 0, 0}, + obj.AUSEFIELD: {gc.OK, 0, 0, 0}, + obj.ACHECKNIL: {gc.LeftRead, 0, 0, 0}, + obj.AVARDEF: {gc.Pseudo | gc.RightWrite, 0, 0, 0}, + obj.AVARKILL: {gc.Pseudo | gc.RightWrite, 0, 0, 0}, + + // NOP is an internal no-op that also stands + // for USED and SET annotations, not the Intel opcode. + obj.ANOP: {gc.LeftRead | gc.RightWrite, 0, 0, 0}, + x86.AADCL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry | gc.UseCarry, 0, 0, 0}, + x86.AADCQ: {gc.SizeQ | gc.LeftRead | RightRdwr | gc.SetCarry | gc.UseCarry, 0, 0, 0}, + x86.AADCW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry | gc.UseCarry, 0, 0, 0}, + x86.AADDB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.AADDL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.AADDW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.AADDQ: {gc.SizeQ | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.AADDSD: {gc.SizeD | gc.LeftRead | RightRdwr, 0, 0, 0}, + x86.AADDSS: {gc.SizeF | gc.LeftRead | RightRdwr, 0, 0, 0}, + x86.AANDB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.AANDL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.AANDQ: {gc.SizeQ | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.AANDW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + obj.ACALL: {gc.RightAddr | gc.Call | gc.KillCarry, 0, 0, 0}, + x86.ACDQ: {gc.OK, AX, AX | DX, 0}, + x86.ACQO: {gc.OK, AX, AX | DX, 0}, + x86.ACWD: {gc.OK, AX, AX | DX, 0}, + x86.ACLD: {gc.OK, 0, 0, 0}, + x86.ASTD: {gc.OK, 0, 0, 0}, + x86.ACMPB: {gc.SizeB | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, + x86.ACMPL: {gc.SizeL | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, + x86.ACMPQ: {gc.SizeQ | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, + x86.ACMPW: {gc.SizeW | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, + x86.ACOMISD: {gc.SizeD | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, + x86.ACOMISS: {gc.SizeF | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, + x86.ACVTSD2SL: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.ACVTSD2SQ: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.ACVTSD2SS: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.ACVTSL2SD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.ACVTSL2SS: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.ACVTSQ2SD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.ACVTSQ2SS: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.ACVTSS2SD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.ACVTSS2SL: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.ACVTSS2SQ: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.ACVTTSD2SL: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.ACVTTSD2SQ: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.ACVTTSS2SL: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.ACVTTSS2SQ: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.ADECB: {gc.SizeB | RightRdwr, 0, 0, 0}, + x86.ADECL: {gc.SizeL | RightRdwr, 0, 0, 0}, + x86.ADECQ: {gc.SizeQ | RightRdwr, 0, 0, 0}, + x86.ADECW: {gc.SizeW | RightRdwr, 0, 0, 0}, + x86.ADIVB: {gc.SizeB | gc.LeftRead | gc.SetCarry, AX, AX, 0}, + x86.ADIVL: {gc.SizeL | gc.LeftRead | gc.SetCarry, AX | DX, AX | DX, 0}, + x86.ADIVQ: {gc.SizeQ | gc.LeftRead | gc.SetCarry, AX | DX, AX | DX, 0}, + x86.ADIVW: {gc.SizeW | gc.LeftRead | gc.SetCarry, AX | DX, AX | DX, 0}, + x86.ADIVSD: {gc.SizeD | gc.LeftRead | RightRdwr, 0, 0, 0}, + x86.ADIVSS: {gc.SizeF | gc.LeftRead | RightRdwr, 0, 0, 0}, + x86.AIDIVB: {gc.SizeB | gc.LeftRead | gc.SetCarry, AX, AX, 0}, + x86.AIDIVL: {gc.SizeL | gc.LeftRead | gc.SetCarry, AX | DX, AX | DX, 0}, + x86.AIDIVQ: {gc.SizeQ | gc.LeftRead | gc.SetCarry, AX | DX, AX | DX, 0}, + x86.AIDIVW: {gc.SizeW | gc.LeftRead | gc.SetCarry, AX | DX, AX | DX, 0}, + x86.AIMULB: {gc.SizeB | gc.LeftRead | gc.SetCarry, AX, AX, 0}, + x86.AIMULL: {gc.SizeL | gc.LeftRead | gc.ImulAXDX | gc.SetCarry, 0, 0, 0}, + x86.AIMULQ: {gc.SizeQ | gc.LeftRead | gc.ImulAXDX | gc.SetCarry, 0, 0, 0}, + x86.AIMULW: {gc.SizeW | gc.LeftRead | gc.ImulAXDX | gc.SetCarry, 0, 0, 0}, + x86.AINCB: {gc.SizeB | RightRdwr, 0, 0, 0}, + x86.AINCL: {gc.SizeL | RightRdwr, 0, 0, 0}, + x86.AINCQ: {gc.SizeQ | RightRdwr, 0, 0, 0}, + x86.AINCW: {gc.SizeW | RightRdwr, 0, 0, 0}, + x86.AJCC: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + x86.AJCS: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + x86.AJEQ: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + x86.AJGE: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + x86.AJGT: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + x86.AJHI: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + x86.AJLE: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + x86.AJLS: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + x86.AJLT: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + x86.AJMI: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + x86.AJNE: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + x86.AJOC: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + x86.AJOS: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + x86.AJPC: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + x86.AJPL: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + x86.AJPS: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + obj.AJMP: {gc.Jump | gc.Break | gc.KillCarry, 0, 0, 0}, + x86.ALEAL: {gc.LeftAddr | gc.RightWrite, 0, 0, 0}, + x86.ALEAQ: {gc.LeftAddr | gc.RightWrite, 0, 0, 0}, + x86.AMOVBLSX: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.AMOVBLZX: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.AMOVBQSX: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.AMOVBQZX: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.AMOVBWSX: {gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.AMOVBWZX: {gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.AMOVLQSX: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.AMOVLQZX: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.AMOVWLSX: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.AMOVWLZX: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.AMOVWQSX: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.AMOVWQZX: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.AMOVQL: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.AMOVB: {gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, + x86.AMOVL: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, + x86.AMOVQ: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, + x86.AMOVW: {gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, + x86.AMOVSB: {gc.OK, DI | SI, DI | SI, 0}, + x86.AMOVSL: {gc.OK, DI | SI, DI | SI, 0}, + x86.AMOVSQ: {gc.OK, DI | SI, DI | SI, 0}, + x86.AMOVSW: {gc.OK, DI | SI, DI | SI, 0}, + obj.ADUFFCOPY: {gc.OK, DI | SI, DI | SI | CX, 0}, + x86.AMOVSD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, + x86.AMOVSS: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, + + // We use MOVAPD as a faster synonym for MOVSD. + x86.AMOVAPD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, + x86.AMULB: {gc.SizeB | gc.LeftRead | gc.SetCarry, AX, AX, 0}, + x86.AMULL: {gc.SizeL | gc.LeftRead | gc.SetCarry, AX, AX | DX, 0}, + x86.AMULQ: {gc.SizeQ | gc.LeftRead | gc.SetCarry, AX, AX | DX, 0}, + x86.AMULW: {gc.SizeW | gc.LeftRead | gc.SetCarry, AX, AX | DX, 0}, + x86.AMULSD: {gc.SizeD | gc.LeftRead | RightRdwr, 0, 0, 0}, + x86.AMULSS: {gc.SizeF | gc.LeftRead | RightRdwr, 0, 0, 0}, + x86.ANEGB: {gc.SizeB | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.ANEGL: {gc.SizeL | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.ANEGQ: {gc.SizeQ | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.ANEGW: {gc.SizeW | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.ANOTB: {gc.SizeB | RightRdwr, 0, 0, 0}, + x86.ANOTL: {gc.SizeL | RightRdwr, 0, 0, 0}, + x86.ANOTQ: {gc.SizeQ | RightRdwr, 0, 0, 0}, + x86.ANOTW: {gc.SizeW | RightRdwr, 0, 0, 0}, + x86.AORB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.AORL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.AORQ: {gc.SizeQ | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.AORW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.APOPQ: {gc.SizeQ | gc.RightWrite, 0, 0, 0}, + x86.APUSHQ: {gc.SizeQ | gc.LeftRead, 0, 0, 0}, + x86.ARCLB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry, 0, 0, 0}, + x86.ARCLL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry, 0, 0, 0}, + x86.ARCLQ: {gc.SizeQ | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry, 0, 0, 0}, + x86.ARCLW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry, 0, 0, 0}, + x86.ARCRB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry, 0, 0, 0}, + x86.ARCRL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry, 0, 0, 0}, + x86.ARCRQ: {gc.SizeQ | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry, 0, 0, 0}, + x86.ARCRW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry, 0, 0, 0}, + x86.AREP: {gc.OK, CX, CX, 0}, + x86.AREPN: {gc.OK, CX, CX, 0}, + obj.ARET: {gc.Break | gc.KillCarry, 0, 0, 0}, + x86.AROLB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.AROLL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.AROLQ: {gc.SizeQ | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.AROLW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ARORB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ARORL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ARORQ: {gc.SizeQ | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ARORW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ASALB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ASALL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ASALQ: {gc.SizeQ | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ASALW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ASARB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ASARL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ASARQ: {gc.SizeQ | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ASARW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ASBBB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.SetCarry | gc.UseCarry, 0, 0, 0}, + x86.ASBBL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry | gc.UseCarry, 0, 0, 0}, + x86.ASBBQ: {gc.SizeQ | gc.LeftRead | RightRdwr | gc.SetCarry | gc.UseCarry, 0, 0, 0}, + x86.ASBBW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry | gc.UseCarry, 0, 0, 0}, + x86.ASETCC: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, + x86.ASETCS: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, + x86.ASETEQ: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, + x86.ASETGE: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, + x86.ASETGT: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, + x86.ASETHI: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, + x86.ASETLE: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, + x86.ASETLS: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, + x86.ASETLT: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, + x86.ASETMI: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, + x86.ASETNE: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, + x86.ASETOC: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, + x86.ASETOS: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, + x86.ASETPC: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, + x86.ASETPL: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, + x86.ASETPS: {gc.SizeB | gc.RightWrite | gc.UseCarry, 0, 0, 0}, + x86.ASHLB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ASHLL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ASHLQ: {gc.SizeQ | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ASHLW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ASHRB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ASHRL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ASHRQ: {gc.SizeQ | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ASHRW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ASQRTSD: {gc.SizeD | gc.LeftRead | RightRdwr, 0, 0, 0}, + x86.ASTOSB: {gc.OK, AX | DI, DI, 0}, + x86.ASTOSL: {gc.OK, AX | DI, DI, 0}, + x86.ASTOSQ: {gc.OK, AX | DI, DI, 0}, + x86.ASTOSW: {gc.OK, AX | DI, DI, 0}, + obj.ADUFFZERO: {gc.OK, AX | DI, DI, 0}, + x86.ASUBB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.ASUBL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.ASUBQ: {gc.SizeQ | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.ASUBW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.ASUBSD: {gc.SizeD | gc.LeftRead | RightRdwr, 0, 0, 0}, + x86.ASUBSS: {gc.SizeF | gc.LeftRead | RightRdwr, 0, 0, 0}, + x86.ATESTB: {gc.SizeB | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, + x86.ATESTL: {gc.SizeL | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, + x86.ATESTQ: {gc.SizeQ | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, + x86.ATESTW: {gc.SizeW | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, + x86.AUCOMISD: {gc.SizeD | gc.LeftRead | gc.RightRead, 0, 0, 0}, + x86.AUCOMISS: {gc.SizeF | gc.LeftRead | gc.RightRead, 0, 0, 0}, + x86.AXCHGB: {gc.SizeB | LeftRdwr | RightRdwr, 0, 0, 0}, + x86.AXCHGL: {gc.SizeL | LeftRdwr | RightRdwr, 0, 0, 0}, + x86.AXCHGQ: {gc.SizeQ | LeftRdwr | RightRdwr, 0, 0, 0}, + x86.AXCHGW: {gc.SizeW | LeftRdwr | RightRdwr, 0, 0, 0}, + x86.AXORB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.AXORL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.AXORQ: {gc.SizeQ | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.AXORW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, +} + +func progflags(p *obj.Prog) uint32 { + flags := progtable[p.As].Flags + if flags&gc.ImulAXDX != 0 && p.To.Type != obj.TYPE_NONE { + flags |= RightRdwr + } + return flags +} + +func progcarryflags(p *obj.Prog) uint32 { + return progtable[p.As].Flags +} + +func proginfo(p *obj.Prog) { + info := &p.Info + *info = progtable[p.As] + if info.Flags == 0 { + gc.Fatal("unknown instruction %v", p) + } + + if (info.Flags&gc.ShiftCX != 0) && p.From.Type != obj.TYPE_CONST { + info.Reguse |= CX + } + + if info.Flags&gc.ImulAXDX != 0 { + if p.To.Type == obj.TYPE_NONE { + info.Reguse |= AX + info.Regset |= AX | DX + } else { + info.Flags |= RightRdwr + } + } + + // Addressing makes some registers used. + if p.From.Type == obj.TYPE_MEM && p.From.Name == obj.NAME_NONE { + info.Regindex |= RtoB(int(p.From.Reg)) + } + if p.From.Index != x86.REG_NONE { + info.Regindex |= RtoB(int(p.From.Index)) + } + if p.To.Type == obj.TYPE_MEM && p.To.Name == obj.NAME_NONE { + info.Regindex |= RtoB(int(p.To.Reg)) + } + if p.To.Index != x86.REG_NONE { + info.Regindex |= RtoB(int(p.To.Index)) + } + if gc.Ctxt.Flag_dynlink { + // When -dynlink is passed, many operations on external names (and + // also calling duffzero/duffcopy) use R15 as a scratch register. + if p.As == x86.ALEAQ || info.Flags == gc.Pseudo || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP { + return + } + if p.As == obj.ADUFFZERO || p.As == obj.ADUFFCOPY || (p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local) || (p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local) { + info.Reguse |= R15 + info.Regset |= R15 + return + } + } +} diff --git a/src/cmd/compile/internal/amd64/reg.go b/src/cmd/compile/internal/amd64/reg.go new file mode 100644 index 0000000000..7d4f40641d --- /dev/null +++ b/src/cmd/compile/internal/amd64/reg.go @@ -0,0 +1,154 @@ +// Derived from Inferno utils/6c/reg.c +// http://code.google.com/p/inferno-os/source/browse/utils/6c/reg.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package amd64 + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/x86" +) + +const ( + NREGVAR = 32 +) + +var reg [x86.MAXREG]uint8 + +var regname = []string{ + ".AX", + ".CX", + ".DX", + ".BX", + ".SP", + ".BP", + ".SI", + ".DI", + ".R8", + ".R9", + ".R10", + ".R11", + ".R12", + ".R13", + ".R14", + ".R15", + ".X0", + ".X1", + ".X2", + ".X3", + ".X4", + ".X5", + ".X6", + ".X7", + ".X8", + ".X9", + ".X10", + ".X11", + ".X12", + ".X13", + ".X14", + ".X15", +} + +func regnames(n *int) []string { + *n = NREGVAR + return regname +} + +func excludedregs() uint64 { + return RtoB(x86.REG_SP) +} + +func doregbits(r int) uint64 { + b := uint64(0) + if r >= x86.REG_AX && r <= x86.REG_R15 { + b |= RtoB(r) + } else if r >= x86.REG_AL && r <= x86.REG_R15B { + b |= RtoB(r - x86.REG_AL + x86.REG_AX) + } else if r >= x86.REG_AH && r <= x86.REG_BH { + b |= RtoB(r - x86.REG_AH + x86.REG_AX) + } else if r >= x86.REG_X0 && r <= x86.REG_X0+15 { + b |= FtoB(r) + } + return b +} + +// For ProgInfo. +const ( + AX = 1 << (x86.REG_AX - x86.REG_AX) + BX = 1 << (x86.REG_BX - x86.REG_AX) + CX = 1 << (x86.REG_CX - x86.REG_AX) + DX = 1 << (x86.REG_DX - x86.REG_AX) + DI = 1 << (x86.REG_DI - x86.REG_AX) + SI = 1 << (x86.REG_SI - x86.REG_AX) + R15 = 1 << (x86.REG_R15 - x86.REG_AX) +) + +func RtoB(r int) uint64 { + if r < x86.REG_AX || r > x86.REG_R15 { + return 0 + } + return 1 << uint(r-x86.REG_AX) +} + +func BtoR(b uint64) int { + b &= 0xffff + if gc.Nacl { + b &^= (1<<(x86.REG_BP-x86.REG_AX) | 1<<(x86.REG_R15-x86.REG_AX)) + } else if obj.Framepointer_enabled != 0 { + // BP is part of the calling convention if framepointer_enabled. + b &^= (1 << (x86.REG_BP - x86.REG_AX)) + } + if b == 0 { + return 0 + } + return gc.Bitno(b) + x86.REG_AX +} + +/* + * bit reg + * 16 X0 + * ... + * 31 X15 + */ +func FtoB(f int) uint64 { + if f < x86.REG_X0 || f > x86.REG_X15 { + return 0 + } + return 1 << uint(f-x86.REG_X0+16) +} + +func BtoF(b uint64) int { + b &= 0xFFFF0000 + if b == 0 { + return 0 + } + return gc.Bitno(b) - 16 + x86.REG_X0 +} diff --git a/src/cmd/compile/internal/arm/cgen.go b/src/cmd/compile/internal/arm/cgen.go new file mode 100644 index 0000000000..8ea6c5f3f2 --- /dev/null +++ b/src/cmd/compile/internal/arm/cgen.go @@ -0,0 +1,229 @@ +// Copyright 2009 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 arm + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/arm" +) + +/* + * generate array index into res. + * n might be any size; res is 32-bit. + * returns Prog* to patch to panic call. + */ +func cgenindex(n *gc.Node, res *gc.Node, bounded bool) *obj.Prog { + if !gc.Is64(n.Type) { + gc.Cgen(n, res) + return nil + } + + var tmp gc.Node + gc.Tempname(&tmp, gc.Types[gc.TINT64]) + gc.Cgen(n, &tmp) + var lo gc.Node + var hi gc.Node + split64(&tmp, &lo, &hi) + gmove(&lo, res) + if bounded { + splitclean() + return nil + } + + var n1 gc.Node + gc.Regalloc(&n1, gc.Types[gc.TINT32], nil) + var n2 gc.Node + gc.Regalloc(&n2, gc.Types[gc.TINT32], nil) + var zero gc.Node + gc.Nodconst(&zero, gc.Types[gc.TINT32], 0) + gmove(&hi, &n1) + gmove(&zero, &n2) + gins(arm.ACMP, &n1, &n2) + gc.Regfree(&n2) + gc.Regfree(&n1) + splitclean() + return gc.Gbranch(arm.ABNE, nil, -1) +} + +func igenindex(n *gc.Node, res *gc.Node, bounded bool) *obj.Prog { + gc.Tempname(res, n.Type) + return cgenindex(n, res, bounded) +} + +func blockcopy(n, res *gc.Node, osrc, odst, w int64) { + // determine alignment. + // want to avoid unaligned access, so have to use + // smaller operations for less aligned types. + // for example moving [4]byte must use 4 MOVB not 1 MOVW. + align := int(n.Type.Align) + + var op int + switch align { + default: + gc.Fatal("sgen: invalid alignment %d for %v", align, n.Type) + + case 1: + op = arm.AMOVB + + case 2: + op = arm.AMOVH + + case 4: + op = arm.AMOVW + } + + if w%int64(align) != 0 { + gc.Fatal("sgen: unaligned size %d (align=%d) for %v", w, align, n.Type) + } + c := int32(w / int64(align)) + + if osrc%int64(align) != 0 || odst%int64(align) != 0 { + gc.Fatal("sgen: unaligned offset src %d or dst %d (align %d)", osrc, odst, align) + } + + // if we are copying forward on the stack and + // the src and dst overlap, then reverse direction + dir := align + if osrc < odst && int64(odst) < int64(osrc)+w { + dir = -dir + } + + if op == arm.AMOVW && !gc.Nacl && dir > 0 && c >= 4 && c <= 128 { + var r0 gc.Node + r0.Op = gc.OREGISTER + r0.Reg = arm.REG_R0 + var r1 gc.Node + r1.Op = gc.OREGISTER + r1.Reg = arm.REG_R0 + 1 + var r2 gc.Node + r2.Op = gc.OREGISTER + r2.Reg = arm.REG_R0 + 2 + + var src gc.Node + gc.Regalloc(&src, gc.Types[gc.Tptr], &r1) + var dst gc.Node + gc.Regalloc(&dst, gc.Types[gc.Tptr], &r2) + if n.Ullman >= res.Ullman { + // eval n first + gc.Agen(n, &src) + + if res.Op == gc.ONAME { + gc.Gvardef(res) + } + gc.Agen(res, &dst) + } else { + // eval res first + if res.Op == gc.ONAME { + gc.Gvardef(res) + } + gc.Agen(res, &dst) + gc.Agen(n, &src) + } + + var tmp gc.Node + gc.Regalloc(&tmp, gc.Types[gc.Tptr], &r0) + f := gc.Sysfunc("duffcopy") + p := gins(obj.ADUFFCOPY, nil, f) + gc.Afunclit(&p.To, f) + + // 8 and 128 = magic constants: see ../../runtime/asm_arm.s + p.To.Offset = 8 * (128 - int64(c)) + + gc.Regfree(&tmp) + gc.Regfree(&src) + gc.Regfree(&dst) + return + } + + var dst gc.Node + var src gc.Node + if n.Ullman >= res.Ullman { + gc.Agenr(n, &dst, res) // temporarily use dst + gc.Regalloc(&src, gc.Types[gc.Tptr], nil) + gins(arm.AMOVW, &dst, &src) + if res.Op == gc.ONAME { + gc.Gvardef(res) + } + gc.Agen(res, &dst) + } else { + if res.Op == gc.ONAME { + gc.Gvardef(res) + } + gc.Agenr(res, &dst, res) + gc.Agenr(n, &src, nil) + } + + var tmp gc.Node + gc.Regalloc(&tmp, gc.Types[gc.TUINT32], nil) + + // set up end marker + var nend gc.Node + + if c >= 4 { + gc.Regalloc(&nend, gc.Types[gc.TUINT32], nil) + + p := gins(arm.AMOVW, &src, &nend) + p.From.Type = obj.TYPE_ADDR + if dir < 0 { + p.From.Offset = int64(dir) + } else { + p.From.Offset = w + } + } + + // move src and dest to the end of block if necessary + if dir < 0 { + p := gins(arm.AMOVW, &src, &src) + p.From.Type = obj.TYPE_ADDR + p.From.Offset = w + int64(dir) + + p = gins(arm.AMOVW, &dst, &dst) + p.From.Type = obj.TYPE_ADDR + p.From.Offset = w + int64(dir) + } + + // move + if c >= 4 { + p := gins(op, &src, &tmp) + p.From.Type = obj.TYPE_MEM + p.From.Offset = int64(dir) + p.Scond |= arm.C_PBIT + ploop := p + + p = gins(op, &tmp, &dst) + p.To.Type = obj.TYPE_MEM + p.To.Offset = int64(dir) + p.Scond |= arm.C_PBIT + + p = gins(arm.ACMP, &src, nil) + raddr(&nend, p) + + gc.Patch(gc.Gbranch(arm.ABNE, nil, 0), ploop) + gc.Regfree(&nend) + } else { + var p *obj.Prog + for { + tmp14 := c + c-- + if tmp14 <= 0 { + break + } + p = gins(op, &src, &tmp) + p.From.Type = obj.TYPE_MEM + p.From.Offset = int64(dir) + p.Scond |= arm.C_PBIT + + p = gins(op, &tmp, &dst) + p.To.Type = obj.TYPE_MEM + p.To.Offset = int64(dir) + p.Scond |= arm.C_PBIT + } + } + + gc.Regfree(&dst) + gc.Regfree(&src) + gc.Regfree(&tmp) +} diff --git a/src/cmd/compile/internal/arm/cgen64.go b/src/cmd/compile/internal/arm/cgen64.go new file mode 100644 index 0000000000..6c88b76e20 --- /dev/null +++ b/src/cmd/compile/internal/arm/cgen64.go @@ -0,0 +1,859 @@ +// Copyright 2009 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 arm + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/arm" +) + +/* + * attempt to generate 64-bit + * res = n + * return 1 on success, 0 if op not handled. + */ +func cgen64(n *gc.Node, res *gc.Node) { + if res.Op != gc.OINDREG && res.Op != gc.ONAME { + gc.Dump("n", n) + gc.Dump("res", res) + gc.Fatal("cgen64 %v of %v", gc.Oconv(int(n.Op), 0), gc.Oconv(int(res.Op), 0)) + } + + l := n.Left + var t1 gc.Node + if !l.Addable { + gc.Tempname(&t1, l.Type) + gc.Cgen(l, &t1) + l = &t1 + } + + var hi1 gc.Node + var lo1 gc.Node + split64(l, &lo1, &hi1) + switch n.Op { + default: + gc.Fatal("cgen64 %v", gc.Oconv(int(n.Op), 0)) + + case gc.OMINUS: + var lo2 gc.Node + var hi2 gc.Node + split64(res, &lo2, &hi2) + + gc.Regalloc(&t1, lo1.Type, nil) + var al gc.Node + gc.Regalloc(&al, lo1.Type, nil) + var ah gc.Node + gc.Regalloc(&ah, hi1.Type, nil) + + gins(arm.AMOVW, &lo1, &al) + gins(arm.AMOVW, &hi1, &ah) + + gmove(ncon(0), &t1) + p1 := gins(arm.ASUB, &al, &t1) + p1.Scond |= arm.C_SBIT + gins(arm.AMOVW, &t1, &lo2) + + gmove(ncon(0), &t1) + gins(arm.ASBC, &ah, &t1) + gins(arm.AMOVW, &t1, &hi2) + + gc.Regfree(&t1) + gc.Regfree(&al) + gc.Regfree(&ah) + splitclean() + splitclean() + return + + case gc.OCOM: + gc.Regalloc(&t1, lo1.Type, nil) + gmove(ncon(^uint32(0)), &t1) + + var lo2 gc.Node + var hi2 gc.Node + split64(res, &lo2, &hi2) + var n1 gc.Node + gc.Regalloc(&n1, lo1.Type, nil) + + gins(arm.AMOVW, &lo1, &n1) + gins(arm.AEOR, &t1, &n1) + gins(arm.AMOVW, &n1, &lo2) + + gins(arm.AMOVW, &hi1, &n1) + gins(arm.AEOR, &t1, &n1) + gins(arm.AMOVW, &n1, &hi2) + + gc.Regfree(&t1) + gc.Regfree(&n1) + splitclean() + splitclean() + return + + // binary operators. + // common setup below. + case gc.OADD, + gc.OSUB, + gc.OMUL, + gc.OLSH, + gc.ORSH, + gc.OAND, + gc.OOR, + gc.OXOR, + gc.OLROT: + break + } + + // setup for binary operators + r := n.Right + + if r != nil && !r.Addable { + var t2 gc.Node + gc.Tempname(&t2, r.Type) + gc.Cgen(r, &t2) + r = &t2 + } + + var hi2 gc.Node + var lo2 gc.Node + if gc.Is64(r.Type) { + split64(r, &lo2, &hi2) + } + + var al gc.Node + gc.Regalloc(&al, lo1.Type, nil) + var ah gc.Node + gc.Regalloc(&ah, hi1.Type, nil) + + // Do op. Leave result in ah:al. + switch n.Op { + default: + gc.Fatal("cgen64: not implemented: %v\n", n) + + // TODO: Constants + case gc.OADD: + var bl gc.Node + gc.Regalloc(&bl, gc.Types[gc.TPTR32], nil) + + var bh gc.Node + gc.Regalloc(&bh, gc.Types[gc.TPTR32], nil) + gins(arm.AMOVW, &hi1, &ah) + gins(arm.AMOVW, &lo1, &al) + gins(arm.AMOVW, &hi2, &bh) + gins(arm.AMOVW, &lo2, &bl) + p1 := gins(arm.AADD, &bl, &al) + p1.Scond |= arm.C_SBIT + gins(arm.AADC, &bh, &ah) + gc.Regfree(&bl) + gc.Regfree(&bh) + + // TODO: Constants. + case gc.OSUB: + var bl gc.Node + gc.Regalloc(&bl, gc.Types[gc.TPTR32], nil) + + var bh gc.Node + gc.Regalloc(&bh, gc.Types[gc.TPTR32], nil) + gins(arm.AMOVW, &lo1, &al) + gins(arm.AMOVW, &hi1, &ah) + gins(arm.AMOVW, &lo2, &bl) + gins(arm.AMOVW, &hi2, &bh) + p1 := gins(arm.ASUB, &bl, &al) + p1.Scond |= arm.C_SBIT + gins(arm.ASBC, &bh, &ah) + gc.Regfree(&bl) + gc.Regfree(&bh) + + // TODO(kaib): this can be done with 4 regs and does not need 6 + case gc.OMUL: + var bl gc.Node + gc.Regalloc(&bl, gc.Types[gc.TPTR32], nil) + + var bh gc.Node + gc.Regalloc(&bh, gc.Types[gc.TPTR32], nil) + var cl gc.Node + gc.Regalloc(&cl, gc.Types[gc.TPTR32], nil) + var ch gc.Node + gc.Regalloc(&ch, gc.Types[gc.TPTR32], nil) + + // load args into bh:bl and bh:bl. + gins(arm.AMOVW, &hi1, &bh) + + gins(arm.AMOVW, &lo1, &bl) + gins(arm.AMOVW, &hi2, &ch) + gins(arm.AMOVW, &lo2, &cl) + + // bl * cl -> ah al + p1 := gins(arm.AMULLU, nil, nil) + + p1.From.Type = obj.TYPE_REG + p1.From.Reg = bl.Reg + p1.Reg = cl.Reg + p1.To.Type = obj.TYPE_REGREG + p1.To.Reg = ah.Reg + p1.To.Offset = int64(al.Reg) + + //print("%P\n", p1); + + // bl * ch + ah -> ah + p1 = gins(arm.AMULA, nil, nil) + + p1.From.Type = obj.TYPE_REG + p1.From.Reg = bl.Reg + p1.Reg = ch.Reg + p1.To.Type = obj.TYPE_REGREG2 + p1.To.Reg = ah.Reg + p1.To.Offset = int64(ah.Reg) + + //print("%P\n", p1); + + // bh * cl + ah -> ah + p1 = gins(arm.AMULA, nil, nil) + + p1.From.Type = obj.TYPE_REG + p1.From.Reg = bh.Reg + p1.Reg = cl.Reg + p1.To.Type = obj.TYPE_REGREG2 + p1.To.Reg = ah.Reg + p1.To.Offset = int64(ah.Reg) + + //print("%P\n", p1); + + gc.Regfree(&bh) + + gc.Regfree(&bl) + gc.Regfree(&ch) + gc.Regfree(&cl) + + // We only rotate by a constant c in [0,64). + // if c >= 32: + // lo, hi = hi, lo + // c -= 32 + // if c == 0: + // no-op + // else: + // t = hi + // shld hi:lo, c + // shld lo:t, c + case gc.OLROT: + v := uint64(r.Int()) + + var bl gc.Node + gc.Regalloc(&bl, lo1.Type, nil) + var bh gc.Node + gc.Regalloc(&bh, hi1.Type, nil) + if v >= 32 { + // reverse during load to do the first 32 bits of rotate + v -= 32 + + gins(arm.AMOVW, &hi1, &bl) + gins(arm.AMOVW, &lo1, &bh) + } else { + gins(arm.AMOVW, &hi1, &bh) + gins(arm.AMOVW, &lo1, &bl) + } + + if v == 0 { + gins(arm.AMOVW, &bh, &ah) + gins(arm.AMOVW, &bl, &al) + } else { + // rotate by 1 <= v <= 31 + // MOVW bl<>(32-v), ah + // OR bh>>(32-v), al + gshift(arm.AMOVW, &bl, arm.SHIFT_LL, int32(v), &al) + + gshift(arm.AMOVW, &bh, arm.SHIFT_LL, int32(v), &ah) + gshift(arm.AORR, &bl, arm.SHIFT_LR, int32(32-v), &ah) + gshift(arm.AORR, &bh, arm.SHIFT_LR, int32(32-v), &al) + } + + gc.Regfree(&bl) + gc.Regfree(&bh) + + case gc.OLSH: + var bl gc.Node + gc.Regalloc(&bl, lo1.Type, nil) + var bh gc.Node + gc.Regalloc(&bh, hi1.Type, nil) + gins(arm.AMOVW, &hi1, &bh) + gins(arm.AMOVW, &lo1, &bl) + + var p6 *obj.Prog + var s gc.Node + var n1 gc.Node + var creg gc.Node + var p1 *obj.Prog + var p2 *obj.Prog + var p3 *obj.Prog + var p4 *obj.Prog + var p5 *obj.Prog + if r.Op == gc.OLITERAL { + v := uint64(r.Int()) + if v >= 64 { + // TODO(kaib): replace with gins(AMOVW, nodintconst(0), &al) + // here and below (verify it optimizes to EOR) + gins(arm.AEOR, &al, &al) + + gins(arm.AEOR, &ah, &ah) + } else if v > 32 { + gins(arm.AEOR, &al, &al) + + // MOVW bl<<(v-32), ah + gshift(arm.AMOVW, &bl, arm.SHIFT_LL, int32(v-32), &ah) + } else if v == 32 { + gins(arm.AEOR, &al, &al) + gins(arm.AMOVW, &bl, &ah) + } else if v > 0 { + // MOVW bl<>(32-v), ah + gshift(arm.AORR, &bl, arm.SHIFT_LR, int32(32-v), &ah) + } else { + gins(arm.AMOVW, &bl, &al) + gins(arm.AMOVW, &bh, &ah) + } + + goto olsh_break + } + + gc.Regalloc(&s, gc.Types[gc.TUINT32], nil) + gc.Regalloc(&creg, gc.Types[gc.TUINT32], nil) + if gc.Is64(r.Type) { + // shift is >= 1<<32 + var cl gc.Node + var ch gc.Node + split64(r, &cl, &ch) + + gmove(&ch, &s) + gins(arm.ATST, &s, nil) + p6 = gc.Gbranch(arm.ABNE, nil, 0) + gmove(&cl, &s) + splitclean() + } else { + gmove(r, &s) + p6 = nil + } + + gins(arm.ATST, &s, nil) + + // shift == 0 + p1 = gins(arm.AMOVW, &bl, &al) + + p1.Scond = arm.C_SCOND_EQ + p1 = gins(arm.AMOVW, &bh, &ah) + p1.Scond = arm.C_SCOND_EQ + p2 = gc.Gbranch(arm.ABEQ, nil, 0) + + // shift is < 32 + gc.Nodconst(&n1, gc.Types[gc.TUINT32], 32) + + gmove(&n1, &creg) + gins(arm.ACMP, &s, &creg) + + // MOVW.LO bl<>creg, ah + p1 = gregshift(arm.AORR, &bl, arm.SHIFT_LR, &creg, &ah) + + p1.Scond = arm.C_SCOND_LO + + // BLO end + p3 = gc.Gbranch(arm.ABLO, nil, 0) + + // shift == 32 + p1 = gins(arm.AEOR, &al, &al) + + p1.Scond = arm.C_SCOND_EQ + p1 = gins(arm.AMOVW, &bl, &ah) + p1.Scond = arm.C_SCOND_EQ + p4 = gc.Gbranch(arm.ABEQ, nil, 0) + + // shift is < 64 + gc.Nodconst(&n1, gc.Types[gc.TUINT32], 64) + + gmove(&n1, &creg) + gins(arm.ACMP, &s, &creg) + + // EOR.LO al, al + p1 = gins(arm.AEOR, &al, &al) + + p1.Scond = arm.C_SCOND_LO + + // MOVW.LO creg>>1, creg + p1 = gshift(arm.AMOVW, &creg, arm.SHIFT_LR, 1, &creg) + + p1.Scond = arm.C_SCOND_LO + + // SUB.LO creg, s + p1 = gins(arm.ASUB, &creg, &s) + + p1.Scond = arm.C_SCOND_LO + + // MOVW bl<= 64 + if p6 != nil { + gc.Patch(p6, gc.Pc) + } + gins(arm.AEOR, &al, &al) + gins(arm.AEOR, &ah, &ah) + + gc.Patch(p2, gc.Pc) + gc.Patch(p3, gc.Pc) + gc.Patch(p4, gc.Pc) + gc.Patch(p5, gc.Pc) + gc.Regfree(&s) + gc.Regfree(&creg) + + olsh_break: + gc.Regfree(&bl) + gc.Regfree(&bh) + + case gc.ORSH: + var bl gc.Node + gc.Regalloc(&bl, lo1.Type, nil) + var bh gc.Node + gc.Regalloc(&bh, hi1.Type, nil) + gins(arm.AMOVW, &hi1, &bh) + gins(arm.AMOVW, &lo1, &bl) + + var p4 *obj.Prog + var p5 *obj.Prog + var n1 gc.Node + var p6 *obj.Prog + var s gc.Node + var p1 *obj.Prog + var p2 *obj.Prog + var creg gc.Node + var p3 *obj.Prog + if r.Op == gc.OLITERAL { + v := uint64(r.Int()) + if v >= 64 { + if bh.Type.Etype == gc.TINT32 { + // MOVW bh->31, al + gshift(arm.AMOVW, &bh, arm.SHIFT_AR, 31, &al) + + // MOVW bh->31, ah + gshift(arm.AMOVW, &bh, arm.SHIFT_AR, 31, &ah) + } else { + gins(arm.AEOR, &al, &al) + gins(arm.AEOR, &ah, &ah) + } + } else if v > 32 { + if bh.Type.Etype == gc.TINT32 { + // MOVW bh->(v-32), al + gshift(arm.AMOVW, &bh, arm.SHIFT_AR, int32(v-32), &al) + + // MOVW bh->31, ah + gshift(arm.AMOVW, &bh, arm.SHIFT_AR, 31, &ah) + } else { + // MOVW bh>>(v-32), al + gshift(arm.AMOVW, &bh, arm.SHIFT_LR, int32(v-32), &al) + + gins(arm.AEOR, &ah, &ah) + } + } else if v == 32 { + gins(arm.AMOVW, &bh, &al) + if bh.Type.Etype == gc.TINT32 { + // MOVW bh->31, ah + gshift(arm.AMOVW, &bh, arm.SHIFT_AR, 31, &ah) + } else { + gins(arm.AEOR, &ah, &ah) + } + } else if v > 0 { + // MOVW bl>>v, al + gshift(arm.AMOVW, &bl, arm.SHIFT_LR, int32(v), &al) + + // OR bh<<(32-v), al + gshift(arm.AORR, &bh, arm.SHIFT_LL, int32(32-v), &al) + + if bh.Type.Etype == gc.TINT32 { + // MOVW bh->v, ah + gshift(arm.AMOVW, &bh, arm.SHIFT_AR, int32(v), &ah) + } else { + // MOVW bh>>v, ah + gshift(arm.AMOVW, &bh, arm.SHIFT_LR, int32(v), &ah) + } + } else { + gins(arm.AMOVW, &bl, &al) + gins(arm.AMOVW, &bh, &ah) + } + + goto orsh_break + } + + gc.Regalloc(&s, gc.Types[gc.TUINT32], nil) + gc.Regalloc(&creg, gc.Types[gc.TUINT32], nil) + if gc.Is64(r.Type) { + // shift is >= 1<<32 + var ch gc.Node + var cl gc.Node + split64(r, &cl, &ch) + + gmove(&ch, &s) + gins(arm.ATST, &s, nil) + var p1 *obj.Prog + if bh.Type.Etype == gc.TINT32 { + p1 = gshift(arm.AMOVW, &bh, arm.SHIFT_AR, 31, &ah) + } else { + p1 = gins(arm.AEOR, &ah, &ah) + } + p1.Scond = arm.C_SCOND_NE + p6 = gc.Gbranch(arm.ABNE, nil, 0) + gmove(&cl, &s) + splitclean() + } else { + gmove(r, &s) + p6 = nil + } + + gins(arm.ATST, &s, nil) + + // shift == 0 + p1 = gins(arm.AMOVW, &bl, &al) + + p1.Scond = arm.C_SCOND_EQ + p1 = gins(arm.AMOVW, &bh, &ah) + p1.Scond = arm.C_SCOND_EQ + p2 = gc.Gbranch(arm.ABEQ, nil, 0) + + // check if shift is < 32 + gc.Nodconst(&n1, gc.Types[gc.TUINT32], 32) + + gmove(&n1, &creg) + gins(arm.ACMP, &s, &creg) + + // MOVW.LO bl>>s, al + p1 = gregshift(arm.AMOVW, &bl, arm.SHIFT_LR, &s, &al) + + p1.Scond = arm.C_SCOND_LO + + // SUB.LO s,creg + p1 = gins(arm.ASUB, &s, &creg) + + p1.Scond = arm.C_SCOND_LO + + // OR.LO bh<<(32-s), al + p1 = gregshift(arm.AORR, &bh, arm.SHIFT_LL, &creg, &al) + + p1.Scond = arm.C_SCOND_LO + + if bh.Type.Etype == gc.TINT32 { + // MOVW bh->s, ah + p1 = gregshift(arm.AMOVW, &bh, arm.SHIFT_AR, &s, &ah) + } else { + // MOVW bh>>s, ah + p1 = gregshift(arm.AMOVW, &bh, arm.SHIFT_LR, &s, &ah) + } + + p1.Scond = arm.C_SCOND_LO + + // BLO end + p3 = gc.Gbranch(arm.ABLO, nil, 0) + + // shift == 32 + p1 = gins(arm.AMOVW, &bh, &al) + + p1.Scond = arm.C_SCOND_EQ + if bh.Type.Etype == gc.TINT32 { + gshift(arm.AMOVW, &bh, arm.SHIFT_AR, 31, &ah) + } else { + gins(arm.AEOR, &ah, &ah) + } + p4 = gc.Gbranch(arm.ABEQ, nil, 0) + + // check if shift is < 64 + gc.Nodconst(&n1, gc.Types[gc.TUINT32], 64) + + gmove(&n1, &creg) + gins(arm.ACMP, &s, &creg) + + // MOVW.LO creg>>1, creg + p1 = gshift(arm.AMOVW, &creg, arm.SHIFT_LR, 1, &creg) + + p1.Scond = arm.C_SCOND_LO + + // SUB.LO creg, s + p1 = gins(arm.ASUB, &creg, &s) + + p1.Scond = arm.C_SCOND_LO + + if bh.Type.Etype == gc.TINT32 { + // MOVW bh->(s-32), al + p1 := gregshift(arm.AMOVW, &bh, arm.SHIFT_AR, &s, &al) + + p1.Scond = arm.C_SCOND_LO + } else { + // MOVW bh>>(v-32), al + p1 := gregshift(arm.AMOVW, &bh, arm.SHIFT_LR, &s, &al) + + p1.Scond = arm.C_SCOND_LO + } + + // BLO end + p5 = gc.Gbranch(arm.ABLO, nil, 0) + + // s >= 64 + if p6 != nil { + gc.Patch(p6, gc.Pc) + } + if bh.Type.Etype == gc.TINT32 { + // MOVW bh->31, al + gshift(arm.AMOVW, &bh, arm.SHIFT_AR, 31, &al) + } else { + gins(arm.AEOR, &al, &al) + } + + gc.Patch(p2, gc.Pc) + gc.Patch(p3, gc.Pc) + gc.Patch(p4, gc.Pc) + gc.Patch(p5, gc.Pc) + gc.Regfree(&s) + gc.Regfree(&creg) + + orsh_break: + gc.Regfree(&bl) + gc.Regfree(&bh) + + // TODO(kaib): literal optimizations + // make constant the right side (it usually is anyway). + // if(lo1.op == OLITERAL) { + // nswap(&lo1, &lo2); + // nswap(&hi1, &hi2); + // } + // if(lo2.op == OLITERAL) { + // // special cases for constants. + // lv = mpgetfix(lo2.val.u.xval); + // hv = mpgetfix(hi2.val.u.xval); + // splitclean(); // right side + // split64(res, &lo2, &hi2); + // switch(n->op) { + // case OXOR: + // gmove(&lo1, &lo2); + // gmove(&hi1, &hi2); + // switch(lv) { + // case 0: + // break; + // case 0xffffffffu: + // gins(ANOTL, N, &lo2); + // break; + // default: + // gins(AXORL, ncon(lv), &lo2); + // break; + // } + // switch(hv) { + // case 0: + // break; + // case 0xffffffffu: + // gins(ANOTL, N, &hi2); + // break; + // default: + // gins(AXORL, ncon(hv), &hi2); + // break; + // } + // break; + + // case OAND: + // switch(lv) { + // case 0: + // gins(AMOVL, ncon(0), &lo2); + // break; + // default: + // gmove(&lo1, &lo2); + // if(lv != 0xffffffffu) + // gins(AANDL, ncon(lv), &lo2); + // break; + // } + // switch(hv) { + // case 0: + // gins(AMOVL, ncon(0), &hi2); + // break; + // default: + // gmove(&hi1, &hi2); + // if(hv != 0xffffffffu) + // gins(AANDL, ncon(hv), &hi2); + // break; + // } + // break; + + // case OOR: + // switch(lv) { + // case 0: + // gmove(&lo1, &lo2); + // break; + // case 0xffffffffu: + // gins(AMOVL, ncon(0xffffffffu), &lo2); + // break; + // default: + // gmove(&lo1, &lo2); + // gins(AORL, ncon(lv), &lo2); + // break; + // } + // switch(hv) { + // case 0: + // gmove(&hi1, &hi2); + // break; + // case 0xffffffffu: + // gins(AMOVL, ncon(0xffffffffu), &hi2); + // break; + // default: + // gmove(&hi1, &hi2); + // gins(AORL, ncon(hv), &hi2); + // break; + // } + // break; + // } + // splitclean(); + // splitclean(); + // goto out; + // } + case gc.OXOR, + gc.OAND, + gc.OOR: + var n1 gc.Node + gc.Regalloc(&n1, lo1.Type, nil) + + gins(arm.AMOVW, &lo1, &al) + gins(arm.AMOVW, &hi1, &ah) + gins(arm.AMOVW, &lo2, &n1) + gins(optoas(int(n.Op), lo1.Type), &n1, &al) + gins(arm.AMOVW, &hi2, &n1) + gins(optoas(int(n.Op), lo1.Type), &n1, &ah) + gc.Regfree(&n1) + } + + if gc.Is64(r.Type) { + splitclean() + } + splitclean() + + split64(res, &lo1, &hi1) + gins(arm.AMOVW, &al, &lo1) + gins(arm.AMOVW, &ah, &hi1) + splitclean() + + //out: + gc.Regfree(&al) + + gc.Regfree(&ah) +} + +/* + * generate comparison of nl, nr, both 64-bit. + * nl is memory; nr is constant or memory. + */ +func cmp64(nl *gc.Node, nr *gc.Node, op int, likely int, to *obj.Prog) { + var lo1 gc.Node + var hi1 gc.Node + var lo2 gc.Node + var hi2 gc.Node + var r1 gc.Node + var r2 gc.Node + + split64(nl, &lo1, &hi1) + split64(nr, &lo2, &hi2) + + // compare most significant word; + // if they differ, we're done. + t := hi1.Type + + gc.Regalloc(&r1, gc.Types[gc.TINT32], nil) + gc.Regalloc(&r2, gc.Types[gc.TINT32], nil) + gins(arm.AMOVW, &hi1, &r1) + gins(arm.AMOVW, &hi2, &r2) + gins(arm.ACMP, &r1, &r2) + gc.Regfree(&r1) + gc.Regfree(&r2) + + var br *obj.Prog + switch op { + default: + gc.Fatal("cmp64 %v %v", gc.Oconv(int(op), 0), t) + + // cmp hi + // bne L + // cmp lo + // beq to + // L: + case gc.OEQ: + br = gc.Gbranch(arm.ABNE, nil, -likely) + + // cmp hi + // bne to + // cmp lo + // bne to + case gc.ONE: + gc.Patch(gc.Gbranch(arm.ABNE, nil, likely), to) + + // cmp hi + // bgt to + // blt L + // cmp lo + // bge to (or bgt to) + // L: + case gc.OGE, + gc.OGT: + gc.Patch(gc.Gbranch(optoas(gc.OGT, t), nil, likely), to) + + br = gc.Gbranch(optoas(gc.OLT, t), nil, -likely) + + // cmp hi + // blt to + // bgt L + // cmp lo + // ble to (or jlt to) + // L: + case gc.OLE, + gc.OLT: + gc.Patch(gc.Gbranch(optoas(gc.OLT, t), nil, likely), to) + + br = gc.Gbranch(optoas(gc.OGT, t), nil, -likely) + } + + // compare least significant word + t = lo1.Type + + gc.Regalloc(&r1, gc.Types[gc.TINT32], nil) + gc.Regalloc(&r2, gc.Types[gc.TINT32], nil) + gins(arm.AMOVW, &lo1, &r1) + gins(arm.AMOVW, &lo2, &r2) + gins(arm.ACMP, &r1, &r2) + gc.Regfree(&r1) + gc.Regfree(&r2) + + // jump again + gc.Patch(gc.Gbranch(optoas(op, t), nil, likely), to) + + // point first branch down here if appropriate + if br != nil { + gc.Patch(br, gc.Pc) + } + + splitclean() + splitclean() +} diff --git a/src/cmd/compile/internal/arm/galign.go b/src/cmd/compile/internal/arm/galign.go new file mode 100644 index 0000000000..60a39d3fe4 --- /dev/null +++ b/src/cmd/compile/internal/arm/galign.go @@ -0,0 +1,94 @@ +// Copyright 2009 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 arm + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/arm" +) + +var thechar int = '5' + +var thestring string = "arm" + +var thelinkarch *obj.LinkArch = &arm.Linkarm + +func linkarchinit() { +} + +var MAXWIDTH int64 = (1 << 32) - 1 + +/* + * go declares several platform-specific type aliases: + * int, uint, and uintptr + */ +var typedefs = []gc.Typedef{ + gc.Typedef{"int", gc.TINT, gc.TINT32}, + gc.Typedef{"uint", gc.TUINT, gc.TUINT32}, + gc.Typedef{"uintptr", gc.TUINTPTR, gc.TUINT32}, +} + +func betypeinit() { + gc.Widthptr = 4 + gc.Widthint = 4 + gc.Widthreg = 4 +} + +func Main() { + gc.Thearch.Thechar = thechar + gc.Thearch.Thestring = thestring + gc.Thearch.Thelinkarch = thelinkarch + gc.Thearch.Typedefs = typedefs + gc.Thearch.REGSP = arm.REGSP + gc.Thearch.REGCTXT = arm.REGCTXT + gc.Thearch.REGCALLX = arm.REG_R1 + gc.Thearch.REGCALLX2 = arm.REG_R2 + gc.Thearch.REGRETURN = arm.REG_R0 + gc.Thearch.REGMIN = arm.REG_R0 + gc.Thearch.REGMAX = arm.REGEXT + gc.Thearch.FREGMIN = arm.REG_F0 + gc.Thearch.FREGMAX = arm.FREGEXT + gc.Thearch.MAXWIDTH = MAXWIDTH + gc.Thearch.ReservedRegs = resvd + + gc.Thearch.Betypeinit = betypeinit + gc.Thearch.Cgen64 = cgen64 + gc.Thearch.Cgen_hmul = cgen_hmul + gc.Thearch.Cgen_shift = cgen_shift + gc.Thearch.Clearfat = clearfat + gc.Thearch.Cmp64 = cmp64 + gc.Thearch.Defframe = defframe + gc.Thearch.Excise = excise + 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 + gc.Thearch.Cgenindex = cgenindex + gc.Thearch.Linkarchinit = linkarchinit + gc.Thearch.Peep = peep + gc.Thearch.Proginfo = proginfo + gc.Thearch.Regtyp = regtyp + gc.Thearch.Sameaddr = sameaddr + gc.Thearch.Smallindir = smallindir + gc.Thearch.Stackaddr = stackaddr + gc.Thearch.Blockcopy = blockcopy + gc.Thearch.Sudoaddable = sudoaddable + gc.Thearch.Sudoclean = sudoclean + gc.Thearch.Excludedregs = excludedregs + gc.Thearch.RtoB = RtoB + gc.Thearch.FtoB = RtoB + gc.Thearch.BtoR = BtoR + gc.Thearch.BtoF = BtoF + gc.Thearch.Optoas = optoas + gc.Thearch.Doregbits = doregbits + gc.Thearch.Regnames = regnames + + gc.Main() + gc.Exit(0) +} diff --git a/src/cmd/compile/internal/arm/ggen.go b/src/cmd/compile/internal/arm/ggen.go new file mode 100644 index 0000000000..6633351032 --- /dev/null +++ b/src/cmd/compile/internal/arm/ggen.go @@ -0,0 +1,529 @@ +// Copyright 2009 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 arm + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/arm" +) + +func defframe(ptxt *obj.Prog) { + var n *gc.Node + + // fill in argument size, stack size + ptxt.To.Type = obj.TYPE_TEXTSIZE + + ptxt.To.Val = int32(gc.Rnd(gc.Curfn.Type.Argwid, int64(gc.Widthptr))) + frame := uint32(gc.Rnd(gc.Stksize+gc.Maxarg, int64(gc.Widthreg))) + ptxt.To.Offset = int64(frame) + + // insert code to contain ambiguously live variables + // so that garbage collector only sees initialized values + // when it looks for pointers. + p := ptxt + + hi := int64(0) + lo := hi + r0 := uint32(0) + for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next { + n = l.N + if !n.Name.Needzero { + continue + } + if n.Class != gc.PAUTO { + gc.Fatal("needzero class %d", n.Class) + } + if n.Type.Width%int64(gc.Widthptr) != 0 || n.Xoffset%int64(gc.Widthptr) != 0 || n.Type.Width == 0 { + gc.Fatal("var %v has size %d offset %d", gc.Nconv(n, obj.FmtLong), int(n.Type.Width), int(n.Xoffset)) + } + if lo != hi && n.Xoffset+n.Type.Width >= lo-int64(2*gc.Widthptr) { + // merge with range we already have + lo = gc.Rnd(n.Xoffset, int64(gc.Widthptr)) + + continue + } + + // zero old range + p = zerorange(p, int64(frame), lo, hi, &r0) + + // set new range + hi = n.Xoffset + n.Type.Width + + lo = n.Xoffset + } + + // zero final range + zerorange(p, int64(frame), lo, hi, &r0) +} + +func zerorange(p *obj.Prog, frame int64, lo int64, hi int64, r0 *uint32) *obj.Prog { + cnt := hi - lo + if cnt == 0 { + return p + } + if *r0 == 0 { + p = appendpp(p, arm.AMOVW, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, arm.REG_R0, 0) + *r0 = 1 + } + + if cnt < int64(4*gc.Widthptr) { + for i := int64(0); i < cnt; i += int64(gc.Widthptr) { + p = appendpp(p, arm.AMOVW, obj.TYPE_REG, arm.REG_R0, 0, obj.TYPE_MEM, arm.REGSP, int32(4+frame+lo+i)) + } + } else if !gc.Nacl && (cnt <= int64(128*gc.Widthptr)) { + p = appendpp(p, arm.AADD, obj.TYPE_CONST, 0, int32(4+frame+lo), obj.TYPE_REG, arm.REG_R1, 0) + p.Reg = arm.REGSP + p = appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0) + f := gc.Sysfunc("duffzero") + gc.Naddr(&p.To, f) + gc.Afunclit(&p.To, f) + p.To.Offset = 4 * (128 - cnt/int64(gc.Widthptr)) + } else { + p = appendpp(p, arm.AADD, obj.TYPE_CONST, 0, int32(4+frame+lo), obj.TYPE_REG, arm.REG_R1, 0) + p.Reg = arm.REGSP + p = appendpp(p, arm.AADD, obj.TYPE_CONST, 0, int32(cnt), obj.TYPE_REG, arm.REG_R2, 0) + p.Reg = arm.REG_R1 + p = appendpp(p, arm.AMOVW, obj.TYPE_REG, arm.REG_R0, 0, obj.TYPE_MEM, arm.REG_R1, 4) + p1 := p + p.Scond |= arm.C_PBIT + p = appendpp(p, arm.ACMP, obj.TYPE_REG, arm.REG_R1, 0, obj.TYPE_NONE, 0, 0) + p.Reg = arm.REG_R2 + p = appendpp(p, arm.ABNE, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0) + gc.Patch(p, p1) + } + + return p +} + +func appendpp(p *obj.Prog, as int, ftype int, freg int, foffset int32, ttype int, treg int, toffset int32) *obj.Prog { + q := gc.Ctxt.NewProg() + gc.Clearp(q) + q.As = int16(as) + q.Lineno = p.Lineno + q.From.Type = int16(ftype) + q.From.Reg = int16(freg) + q.From.Offset = int64(foffset) + q.To.Type = int16(ttype) + q.To.Reg = int16(treg) + q.To.Offset = int64(toffset) + q.Link = p.Link + p.Link = q + return q +} + +/* + * generate high multiply + * res = (nl * nr) >> wordsize + */ +func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) { + if nl.Ullman < nr.Ullman { + tmp := nl + nl = nr + nr = tmp + } + + t := nl.Type + w := int(t.Width * 8) + var n1 gc.Node + gc.Regalloc(&n1, t, res) + gc.Cgen(nl, &n1) + var n2 gc.Node + gc.Regalloc(&n2, t, nil) + gc.Cgen(nr, &n2) + switch gc.Simtype[t.Etype] { + case gc.TINT8, + gc.TINT16: + gins(optoas(gc.OMUL, t), &n2, &n1) + gshift(arm.AMOVW, &n1, arm.SHIFT_AR, int32(w), &n1) + + case gc.TUINT8, + gc.TUINT16: + gins(optoas(gc.OMUL, t), &n2, &n1) + gshift(arm.AMOVW, &n1, arm.SHIFT_LR, int32(w), &n1) + + // perform a long multiplication. + case gc.TINT32, + gc.TUINT32: + var p *obj.Prog + if gc.Issigned[t.Etype] { + p = gins(arm.AMULL, &n2, nil) + } else { + p = gins(arm.AMULLU, &n2, nil) + } + + // n2 * n1 -> (n1 n2) + p.Reg = n1.Reg + + p.To.Type = obj.TYPE_REGREG + p.To.Reg = n1.Reg + p.To.Offset = int64(n2.Reg) + + default: + gc.Fatal("cgen_hmul %v", t) + } + + gc.Cgen(&n1, res) + gc.Regfree(&n1) + gc.Regfree(&n2) +} + +/* + * generate shift according to op, one of: + * res = nl << nr + * res = nl >> nr + */ +func cgen_shift(op int, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) { + if nl.Type.Width > 4 { + gc.Fatal("cgen_shift %v", nl.Type) + } + + w := int(nl.Type.Width * 8) + + if op == gc.OLROT { + v := nr.Int() + var n1 gc.Node + gc.Regalloc(&n1, nl.Type, res) + if w == 32 { + gc.Cgen(nl, &n1) + gshift(arm.AMOVW, &n1, arm.SHIFT_RR, int32(w)-int32(v), &n1) + } else { + var n2 gc.Node + gc.Regalloc(&n2, nl.Type, nil) + gc.Cgen(nl, &n2) + gshift(arm.AMOVW, &n2, arm.SHIFT_LL, int32(v), &n1) + gshift(arm.AORR, &n2, arm.SHIFT_LR, int32(w)-int32(v), &n1) + gc.Regfree(&n2) + + // Ensure sign/zero-extended result. + gins(optoas(gc.OAS, nl.Type), &n1, &n1) + } + + gmove(&n1, res) + gc.Regfree(&n1) + return + } + + if nr.Op == gc.OLITERAL { + var n1 gc.Node + gc.Regalloc(&n1, nl.Type, res) + gc.Cgen(nl, &n1) + sc := uint64(nr.Int()) + if sc == 0 { + } else // nothing to do + if sc >= uint64(nl.Type.Width*8) { + if op == gc.ORSH && gc.Issigned[nl.Type.Etype] { + gshift(arm.AMOVW, &n1, arm.SHIFT_AR, int32(w), &n1) + } else { + gins(arm.AEOR, &n1, &n1) + } + } else { + if op == gc.ORSH && gc.Issigned[nl.Type.Etype] { + gshift(arm.AMOVW, &n1, arm.SHIFT_AR, int32(sc), &n1) + } else if op == gc.ORSH { + gshift(arm.AMOVW, &n1, arm.SHIFT_LR, int32(sc), &n1) // OLSH + } else { + gshift(arm.AMOVW, &n1, arm.SHIFT_LL, int32(sc), &n1) + } + } + + if w < 32 && op == gc.OLSH { + gins(optoas(gc.OAS, nl.Type), &n1, &n1) + } + gmove(&n1, res) + gc.Regfree(&n1) + return + } + + tr := nr.Type + var t gc.Node + var n1 gc.Node + var n2 gc.Node + var n3 gc.Node + if tr.Width > 4 { + var nt gc.Node + gc.Tempname(&nt, nr.Type) + if nl.Ullman >= nr.Ullman { + gc.Regalloc(&n2, nl.Type, res) + gc.Cgen(nl, &n2) + gc.Cgen(nr, &nt) + n1 = nt + } else { + gc.Cgen(nr, &nt) + gc.Regalloc(&n2, nl.Type, res) + gc.Cgen(nl, &n2) + } + + var hi gc.Node + var lo gc.Node + split64(&nt, &lo, &hi) + gc.Regalloc(&n1, gc.Types[gc.TUINT32], nil) + gc.Regalloc(&n3, gc.Types[gc.TUINT32], nil) + gmove(&lo, &n1) + gmove(&hi, &n3) + splitclean() + gins(arm.ATST, &n3, nil) + gc.Nodconst(&t, gc.Types[gc.TUINT32], int64(w)) + p1 := gins(arm.AMOVW, &t, &n1) + p1.Scond = arm.C_SCOND_NE + tr = gc.Types[gc.TUINT32] + gc.Regfree(&n3) + } else { + if nl.Ullman >= nr.Ullman { + gc.Regalloc(&n2, nl.Type, res) + gc.Cgen(nl, &n2) + gc.Regalloc(&n1, nr.Type, nil) + gc.Cgen(nr, &n1) + } else { + gc.Regalloc(&n1, nr.Type, nil) + gc.Cgen(nr, &n1) + gc.Regalloc(&n2, nl.Type, res) + gc.Cgen(nl, &n2) + } + } + + // test for shift being 0 + gins(arm.ATST, &n1, nil) + + p3 := gc.Gbranch(arm.ABEQ, nil, -1) + + // test and fix up large shifts + // TODO: if(!bounded), don't emit some of this. + gc.Regalloc(&n3, tr, nil) + + gc.Nodconst(&t, gc.Types[gc.TUINT32], int64(w)) + gmove(&t, &n3) + gins(arm.ACMP, &n1, &n3) + if op == gc.ORSH { + var p1 *obj.Prog + var p2 *obj.Prog + if gc.Issigned[nl.Type.Etype] { + p1 = gshift(arm.AMOVW, &n2, arm.SHIFT_AR, int32(w)-1, &n2) + p2 = gregshift(arm.AMOVW, &n2, arm.SHIFT_AR, &n1, &n2) + } else { + p1 = gins(arm.AEOR, &n2, &n2) + p2 = gregshift(arm.AMOVW, &n2, arm.SHIFT_LR, &n1, &n2) + } + + p1.Scond = arm.C_SCOND_HS + p2.Scond = arm.C_SCOND_LO + } else { + p1 := gins(arm.AEOR, &n2, &n2) + p2 := gregshift(arm.AMOVW, &n2, arm.SHIFT_LL, &n1, &n2) + p1.Scond = arm.C_SCOND_HS + p2.Scond = arm.C_SCOND_LO + } + + gc.Regfree(&n3) + + gc.Patch(p3, gc.Pc) + + // Left-shift of smaller word must be sign/zero-extended. + if w < 32 && op == gc.OLSH { + gins(optoas(gc.OAS, nl.Type), &n2, &n2) + } + gmove(&n2, res) + + gc.Regfree(&n1) + gc.Regfree(&n2) +} + +func clearfat(nl *gc.Node) { + /* clear a fat object */ + if gc.Debug['g'] != 0 { + gc.Dump("\nclearfat", nl) + } + + w := uint32(nl.Type.Width) + + // Avoid taking the address for simple enough types. + if gc.Componentgen(nil, nl) { + return + } + + c := w % 4 // bytes + q := w / 4 // quads + + var r0 gc.Node + r0.Op = gc.OREGISTER + + r0.Reg = arm.REG_R0 + var r1 gc.Node + r1.Op = gc.OREGISTER + r1.Reg = arm.REG_R1 + var dst gc.Node + gc.Regalloc(&dst, gc.Types[gc.Tptr], &r1) + gc.Agen(nl, &dst) + var nc gc.Node + gc.Nodconst(&nc, gc.Types[gc.TUINT32], 0) + var nz gc.Node + gc.Regalloc(&nz, gc.Types[gc.TUINT32], &r0) + gc.Cgen(&nc, &nz) + + if q > 128 { + var end gc.Node + gc.Regalloc(&end, gc.Types[gc.Tptr], nil) + p := gins(arm.AMOVW, &dst, &end) + p.From.Type = obj.TYPE_ADDR + p.From.Offset = int64(q) * 4 + + p = gins(arm.AMOVW, &nz, &dst) + p.To.Type = obj.TYPE_MEM + p.To.Offset = 4 + p.Scond |= arm.C_PBIT + pl := p + + p = gins(arm.ACMP, &dst, nil) + raddr(&end, p) + gc.Patch(gc.Gbranch(arm.ABNE, nil, 0), pl) + + gc.Regfree(&end) + } else if q >= 4 && !gc.Nacl { + f := gc.Sysfunc("duffzero") + p := gins(obj.ADUFFZERO, nil, f) + gc.Afunclit(&p.To, f) + + // 4 and 128 = magic constants: see ../../runtime/asm_arm.s + p.To.Offset = 4 * (128 - int64(q)) + } else { + var p *obj.Prog + for q > 0 { + p = gins(arm.AMOVW, &nz, &dst) + p.To.Type = obj.TYPE_MEM + p.To.Offset = 4 + p.Scond |= arm.C_PBIT + + //print("1. %P\n", p); + q-- + } + } + + var p *obj.Prog + for c > 0 { + p = gins(arm.AMOVB, &nz, &dst) + p.To.Type = obj.TYPE_MEM + p.To.Offset = 1 + p.Scond |= arm.C_PBIT + + //print("2. %P\n", p); + c-- + } + + gc.Regfree(&dst) + gc.Regfree(&nz) +} + +// Called after regopt and peep have run. +// Expand CHECKNIL pseudo-op into actual nil pointer check. +func expandchecks(firstp *obj.Prog) { + var reg int + var p1 *obj.Prog + + for p := firstp; p != nil; p = p.Link { + if p.As != obj.ACHECKNIL { + continue + } + if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers + gc.Warnl(int(p.Lineno), "generated nil check") + } + if p.From.Type != obj.TYPE_REG { + gc.Fatal("invalid nil check %v", p) + } + reg = int(p.From.Reg) + + // check is + // CMP arg, $0 + // MOV.EQ arg, 0(arg) + p1 = gc.Ctxt.NewProg() + + gc.Clearp(p1) + p1.Link = p.Link + p.Link = p1 + p1.Lineno = p.Lineno + p1.Pc = 9999 + p1.As = arm.AMOVW + p1.From.Type = obj.TYPE_REG + p1.From.Reg = int16(reg) + p1.To.Type = obj.TYPE_MEM + p1.To.Reg = int16(reg) + p1.To.Offset = 0 + p1.Scond = arm.C_SCOND_EQ + p.As = arm.ACMP + p.From.Type = obj.TYPE_CONST + p.From.Reg = 0 + p.From.Offset = 0 + p.Reg = int16(reg) + } +} + +func ginsnop() { + var r gc.Node + gc.Nodreg(&r, gc.Types[gc.TINT], arm.REG_R0) + p := gins(arm.AAND, &r, &r) + p.Scond = arm.C_SCOND_EQ +} + +/* + * generate + * as $c, n + */ +func ginscon(as int, c int64, n *gc.Node) { + var n1 gc.Node + gc.Nodconst(&n1, gc.Types[gc.TINT32], c) + var n2 gc.Node + gc.Regalloc(&n2, gc.Types[gc.TINT32], nil) + gmove(&n1, &n2) + gins(as, &n2, n) + gc.Regfree(&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 && n1.Int() == 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) + if gc.Isint[t.Etype] && n2.Op == gc.OLITERAL && n2.Int() == 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) + 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 { + case 2: + gshift(arm.AADD, index, arm.SHIFT_LL, 1, addr) + return true + case 4: + gshift(arm.AADD, index, arm.SHIFT_LL, 2, addr) + return true + case 8: + gshift(arm.AADD, index, arm.SHIFT_LL, 3, addr) + return true + } + return false +} + +// res = runtime.getg() +func getg(res *gc.Node) { + var n1 gc.Node + gc.Nodreg(&n1, res.Type, arm.REGG) + gmove(&n1, res) +} diff --git a/src/cmd/compile/internal/arm/gsubr.go b/src/cmd/compile/internal/arm/gsubr.go new file mode 100644 index 0000000000..5263f15ac2 --- /dev/null +++ b/src/cmd/compile/internal/arm/gsubr.go @@ -0,0 +1,1209 @@ +// Derived from Inferno utils/5c/txt.c +// http://code.google.com/p/inferno-os/source/browse/utils/5c/txt.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package arm + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/arm" + "fmt" +) + +var resvd = []int{ + arm.REG_R9, // formerly reserved for m; might be okay to reuse now; not sure about NaCl + arm.REG_R10, // reserved for g +} + +/* + * return constant i node. + * overwritten by next call, but useful in calls to gins. + */ + +var ncon_n gc.Node + +func ncon(i uint32) *gc.Node { + if ncon_n.Type == nil { + gc.Nodconst(&ncon_n, gc.Types[gc.TUINT32], 0) + } + ncon_n.SetInt(int64(i)) + return &ncon_n +} + +var sclean [10]gc.Node + +var nsclean int + +/* + * n is a 64-bit value. fill in lo and hi to refer to its 32-bit halves. + */ +func split64(n *gc.Node, lo *gc.Node, hi *gc.Node) { + if !gc.Is64(n.Type) { + gc.Fatal("split64 %v", n.Type) + } + + if nsclean >= len(sclean) { + gc.Fatal("split64 clean") + } + sclean[nsclean].Op = gc.OEMPTY + nsclean++ + switch n.Op { + default: + switch n.Op { + default: + var n1 gc.Node + if !dotaddable(n, &n1) { + gc.Igen(n, &n1, nil) + sclean[nsclean-1] = n1 + } + + n = &n1 + + case gc.ONAME: + if n.Class == gc.PPARAMREF { + var n1 gc.Node + gc.Cgen(n.Name.Heapaddr, &n1) + sclean[nsclean-1] = n1 + n = &n1 + } + + // nothing + case gc.OINDREG: + break + } + + *lo = *n + *hi = *n + lo.Type = gc.Types[gc.TUINT32] + if n.Type.Etype == gc.TINT64 { + hi.Type = gc.Types[gc.TINT32] + } else { + hi.Type = gc.Types[gc.TUINT32] + } + hi.Xoffset += 4 + + case gc.OLITERAL: + var n1 gc.Node + n.Convconst(&n1, n.Type) + i := n1.Int() + gc.Nodconst(lo, gc.Types[gc.TUINT32], int64(uint32(i))) + i >>= 32 + if n.Type.Etype == gc.TINT64 { + gc.Nodconst(hi, gc.Types[gc.TINT32], int64(int32(i))) + } else { + gc.Nodconst(hi, gc.Types[gc.TUINT32], int64(uint32(i))) + } + } +} + +func splitclean() { + if nsclean <= 0 { + gc.Fatal("splitclean") + } + nsclean-- + if sclean[nsclean].Op != gc.OEMPTY { + gc.Regfree(&sclean[nsclean]) + } +} + +func gmove(f *gc.Node, t *gc.Node) { + if gc.Debug['M'] != 0 { + fmt.Printf("gmove %v -> %v\n", f, t) + } + + ft := gc.Simsimtype(f.Type) + tt := gc.Simsimtype(t.Type) + cvt := t.Type + + if gc.Iscomplex[ft] || gc.Iscomplex[tt] { + gc.Complexmove(f, t) + return + } + + // cannot have two memory operands; + // except 64-bit, which always copies via registers anyway. + var a int + var r1 gc.Node + if !gc.Is64(f.Type) && !gc.Is64(t.Type) && gc.Ismem(f) && gc.Ismem(t) { + goto hard + } + + // convert constant to desired type + if f.Op == gc.OLITERAL { + var con gc.Node + switch tt { + default: + f.Convconst(&con, t.Type) + + case gc.TINT16, + gc.TINT8: + var con gc.Node + f.Convconst(&con, gc.Types[gc.TINT32]) + var r1 gc.Node + gc.Regalloc(&r1, con.Type, t) + gins(arm.AMOVW, &con, &r1) + gmove(&r1, t) + gc.Regfree(&r1) + return + + case gc.TUINT16, + gc.TUINT8: + var con gc.Node + f.Convconst(&con, gc.Types[gc.TUINT32]) + var r1 gc.Node + gc.Regalloc(&r1, con.Type, t) + gins(arm.AMOVW, &con, &r1) + gmove(&r1, t) + gc.Regfree(&r1) + return + } + + f = &con + ft = gc.Simsimtype(con.Type) + + // constants can't move directly to memory + if gc.Ismem(t) && !gc.Is64(t.Type) { + goto hard + } + } + + // value -> value copy, only one memory operand. + // figure out the instruction to use. + // break out of switch for one-instruction gins. + // goto rdst for "destination must be register". + // goto hard for "convert to cvt type first". + // otherwise handle and return. + + switch uint32(ft)<<16 | uint32(tt) { + default: + // should not happen + gc.Fatal("gmove %v -> %v", f, t) + return + + /* + * integer copy and truncate + */ + case gc.TINT8<<16 | gc.TINT8: // same size + if !gc.Ismem(f) { + a = arm.AMOVB + break + } + fallthrough + + case gc.TUINT8<<16 | gc.TINT8, + gc.TINT16<<16 | gc.TINT8, // truncate + gc.TUINT16<<16 | gc.TINT8, + gc.TINT32<<16 | gc.TINT8, + gc.TUINT32<<16 | gc.TINT8: + a = arm.AMOVBS + + case gc.TUINT8<<16 | gc.TUINT8: + if !gc.Ismem(f) { + a = arm.AMOVB + break + } + fallthrough + + case gc.TINT8<<16 | gc.TUINT8, + gc.TINT16<<16 | gc.TUINT8, + gc.TUINT16<<16 | gc.TUINT8, + gc.TINT32<<16 | gc.TUINT8, + gc.TUINT32<<16 | gc.TUINT8: + a = arm.AMOVBU + + case gc.TINT64<<16 | gc.TINT8, // truncate low word + gc.TUINT64<<16 | gc.TINT8: + a = arm.AMOVBS + + goto trunc64 + + case gc.TINT64<<16 | gc.TUINT8, + gc.TUINT64<<16 | gc.TUINT8: + a = arm.AMOVBU + goto trunc64 + + case gc.TINT16<<16 | gc.TINT16: // same size + if !gc.Ismem(f) { + a = arm.AMOVH + break + } + fallthrough + + case gc.TUINT16<<16 | gc.TINT16, + gc.TINT32<<16 | gc.TINT16, // truncate + gc.TUINT32<<16 | gc.TINT16: + a = arm.AMOVHS + + case gc.TUINT16<<16 | gc.TUINT16: + if !gc.Ismem(f) { + a = arm.AMOVH + break + } + fallthrough + + case gc.TINT16<<16 | gc.TUINT16, + gc.TINT32<<16 | gc.TUINT16, + gc.TUINT32<<16 | gc.TUINT16: + a = arm.AMOVHU + + case gc.TINT64<<16 | gc.TINT16, // truncate low word + gc.TUINT64<<16 | gc.TINT16: + a = arm.AMOVHS + + goto trunc64 + + case gc.TINT64<<16 | gc.TUINT16, + gc.TUINT64<<16 | gc.TUINT16: + a = arm.AMOVHU + goto trunc64 + + case gc.TINT32<<16 | gc.TINT32, // same size + gc.TINT32<<16 | gc.TUINT32, + gc.TUINT32<<16 | gc.TINT32, + gc.TUINT32<<16 | gc.TUINT32: + a = arm.AMOVW + + case gc.TINT64<<16 | gc.TINT32, // truncate + gc.TUINT64<<16 | gc.TINT32, + gc.TINT64<<16 | gc.TUINT32, + gc.TUINT64<<16 | gc.TUINT32: + var flo gc.Node + var fhi gc.Node + split64(f, &flo, &fhi) + + var r1 gc.Node + gc.Regalloc(&r1, t.Type, nil) + gins(arm.AMOVW, &flo, &r1) + gins(arm.AMOVW, &r1, t) + gc.Regfree(&r1) + splitclean() + return + + case gc.TINT64<<16 | gc.TINT64, // same size + gc.TINT64<<16 | gc.TUINT64, + gc.TUINT64<<16 | gc.TINT64, + gc.TUINT64<<16 | gc.TUINT64: + var fhi gc.Node + var flo gc.Node + split64(f, &flo, &fhi) + + var tlo gc.Node + var thi gc.Node + split64(t, &tlo, &thi) + var r1 gc.Node + gc.Regalloc(&r1, flo.Type, nil) + var r2 gc.Node + gc.Regalloc(&r2, fhi.Type, nil) + gins(arm.AMOVW, &flo, &r1) + gins(arm.AMOVW, &fhi, &r2) + gins(arm.AMOVW, &r1, &tlo) + gins(arm.AMOVW, &r2, &thi) + gc.Regfree(&r1) + gc.Regfree(&r2) + splitclean() + splitclean() + return + + /* + * integer up-conversions + */ + case gc.TINT8<<16 | gc.TINT16, // sign extend int8 + gc.TINT8<<16 | gc.TUINT16, + gc.TINT8<<16 | gc.TINT32, + gc.TINT8<<16 | gc.TUINT32: + a = arm.AMOVBS + + goto rdst + + case gc.TINT8<<16 | gc.TINT64, // convert via int32 + gc.TINT8<<16 | gc.TUINT64: + cvt = gc.Types[gc.TINT32] + + goto hard + + case gc.TUINT8<<16 | gc.TINT16, // zero extend uint8 + gc.TUINT8<<16 | gc.TUINT16, + gc.TUINT8<<16 | gc.TINT32, + gc.TUINT8<<16 | gc.TUINT32: + a = arm.AMOVBU + + goto rdst + + case gc.TUINT8<<16 | gc.TINT64, // convert via uint32 + gc.TUINT8<<16 | gc.TUINT64: + cvt = gc.Types[gc.TUINT32] + + goto hard + + case gc.TINT16<<16 | gc.TINT32, // sign extend int16 + gc.TINT16<<16 | gc.TUINT32: + a = arm.AMOVHS + + goto rdst + + case gc.TINT16<<16 | gc.TINT64, // convert via int32 + gc.TINT16<<16 | gc.TUINT64: + cvt = gc.Types[gc.TINT32] + + goto hard + + case gc.TUINT16<<16 | gc.TINT32, // zero extend uint16 + gc.TUINT16<<16 | gc.TUINT32: + a = arm.AMOVHU + + goto rdst + + case gc.TUINT16<<16 | gc.TINT64, // convert via uint32 + gc.TUINT16<<16 | gc.TUINT64: + cvt = gc.Types[gc.TUINT32] + + goto hard + + case gc.TINT32<<16 | gc.TINT64, // sign extend int32 + gc.TINT32<<16 | gc.TUINT64: + var tlo gc.Node + var thi gc.Node + split64(t, &tlo, &thi) + + var r1 gc.Node + gc.Regalloc(&r1, tlo.Type, nil) + var r2 gc.Node + gc.Regalloc(&r2, thi.Type, nil) + gmove(f, &r1) + p1 := gins(arm.AMOVW, &r1, &r2) + p1.From.Type = obj.TYPE_SHIFT + p1.From.Offset = 2<<5 | 31<<7 | int64(r1.Reg)&15 // r1->31 + p1.From.Reg = 0 + + //print("gmove: %P\n", p1); + gins(arm.AMOVW, &r1, &tlo) + + gins(arm.AMOVW, &r2, &thi) + gc.Regfree(&r1) + gc.Regfree(&r2) + splitclean() + return + + case gc.TUINT32<<16 | gc.TINT64, // zero extend uint32 + gc.TUINT32<<16 | gc.TUINT64: + var thi gc.Node + var tlo gc.Node + split64(t, &tlo, &thi) + + gmove(f, &tlo) + var r1 gc.Node + gc.Regalloc(&r1, thi.Type, nil) + gins(arm.AMOVW, ncon(0), &r1) + gins(arm.AMOVW, &r1, &thi) + gc.Regfree(&r1) + splitclean() + return + + // case CASE(TFLOAT64, TUINT64): + /* + * float to integer + */ + case gc.TFLOAT32<<16 | gc.TINT8, + gc.TFLOAT32<<16 | gc.TUINT8, + gc.TFLOAT32<<16 | gc.TINT16, + gc.TFLOAT32<<16 | gc.TUINT16, + gc.TFLOAT32<<16 | gc.TINT32, + gc.TFLOAT32<<16 | gc.TUINT32, + + // case CASE(TFLOAT32, TUINT64): + + gc.TFLOAT64<<16 | gc.TINT8, + gc.TFLOAT64<<16 | gc.TUINT8, + gc.TFLOAT64<<16 | gc.TINT16, + gc.TFLOAT64<<16 | gc.TUINT16, + gc.TFLOAT64<<16 | gc.TINT32, + gc.TFLOAT64<<16 | gc.TUINT32: + fa := arm.AMOVF + + a := arm.AMOVFW + if ft == gc.TFLOAT64 { + fa = arm.AMOVD + a = arm.AMOVDW + } + + ta := arm.AMOVW + switch tt { + case gc.TINT8: + ta = arm.AMOVBS + + case gc.TUINT8: + ta = arm.AMOVBU + + case gc.TINT16: + ta = arm.AMOVHS + + case gc.TUINT16: + ta = arm.AMOVHU + } + + var r1 gc.Node + gc.Regalloc(&r1, gc.Types[ft], f) + var r2 gc.Node + gc.Regalloc(&r2, gc.Types[tt], t) + gins(fa, f, &r1) // load to fpu + p1 := gins(a, &r1, &r1) // convert to w + switch tt { + case gc.TUINT8, + gc.TUINT16, + gc.TUINT32: + p1.Scond |= arm.C_UBIT + } + + gins(arm.AMOVW, &r1, &r2) // copy to cpu + gins(ta, &r2, t) // store + gc.Regfree(&r1) + gc.Regfree(&r2) + return + + /* + * integer to float + */ + case gc.TINT8<<16 | gc.TFLOAT32, + gc.TUINT8<<16 | gc.TFLOAT32, + gc.TINT16<<16 | gc.TFLOAT32, + gc.TUINT16<<16 | gc.TFLOAT32, + gc.TINT32<<16 | gc.TFLOAT32, + gc.TUINT32<<16 | gc.TFLOAT32, + gc.TINT8<<16 | gc.TFLOAT64, + gc.TUINT8<<16 | gc.TFLOAT64, + gc.TINT16<<16 | gc.TFLOAT64, + gc.TUINT16<<16 | gc.TFLOAT64, + gc.TINT32<<16 | gc.TFLOAT64, + gc.TUINT32<<16 | gc.TFLOAT64: + fa := arm.AMOVW + + switch ft { + case gc.TINT8: + fa = arm.AMOVBS + + case gc.TUINT8: + fa = arm.AMOVBU + + case gc.TINT16: + fa = arm.AMOVHS + + case gc.TUINT16: + fa = arm.AMOVHU + } + + a := arm.AMOVWF + ta := arm.AMOVF + if tt == gc.TFLOAT64 { + a = arm.AMOVWD + ta = arm.AMOVD + } + + var r1 gc.Node + gc.Regalloc(&r1, gc.Types[ft], f) + var r2 gc.Node + gc.Regalloc(&r2, gc.Types[tt], t) + gins(fa, f, &r1) // load to cpu + gins(arm.AMOVW, &r1, &r2) // copy to fpu + p1 := gins(a, &r2, &r2) // convert + switch ft { + case gc.TUINT8, + gc.TUINT16, + gc.TUINT32: + p1.Scond |= arm.C_UBIT + } + + gins(ta, &r2, t) // store + gc.Regfree(&r1) + gc.Regfree(&r2) + return + + case gc.TUINT64<<16 | gc.TFLOAT32, + gc.TUINT64<<16 | gc.TFLOAT64: + gc.Fatal("gmove UINT64, TFLOAT not implemented") + return + + /* + * float to float + */ + case gc.TFLOAT32<<16 | gc.TFLOAT32: + a = arm.AMOVF + + case gc.TFLOAT64<<16 | gc.TFLOAT64: + a = arm.AMOVD + + case gc.TFLOAT32<<16 | gc.TFLOAT64: + var r1 gc.Node + gc.Regalloc(&r1, gc.Types[gc.TFLOAT64], t) + gins(arm.AMOVF, f, &r1) + gins(arm.AMOVFD, &r1, &r1) + gins(arm.AMOVD, &r1, t) + gc.Regfree(&r1) + return + + case gc.TFLOAT64<<16 | gc.TFLOAT32: + var r1 gc.Node + gc.Regalloc(&r1, gc.Types[gc.TFLOAT64], t) + gins(arm.AMOVD, f, &r1) + gins(arm.AMOVDF, &r1, &r1) + gins(arm.AMOVF, &r1, t) + gc.Regfree(&r1) + return + } + + gins(a, f, t) + return + + // TODO(kaib): we almost always require a register dest anyway, this can probably be + // removed. + // requires register destination +rdst: + { + gc.Regalloc(&r1, t.Type, t) + + gins(a, f, &r1) + gmove(&r1, t) + gc.Regfree(&r1) + return + } + + // requires register intermediate +hard: + gc.Regalloc(&r1, cvt, t) + + gmove(f, &r1) + gmove(&r1, t) + gc.Regfree(&r1) + return + + // truncate 64 bit integer +trunc64: + var fhi gc.Node + var flo gc.Node + split64(f, &flo, &fhi) + + gc.Regalloc(&r1, t.Type, nil) + gins(a, &flo, &r1) + gins(a, &r1, t) + gc.Regfree(&r1) + splitclean() + return +} + +func samaddr(f *gc.Node, t *gc.Node) bool { + if f.Op != t.Op { + return false + } + + switch f.Op { + case gc.OREGISTER: + if f.Reg != t.Reg { + break + } + return true + } + + return false +} + +/* + * generate one instruction: + * as f, t + */ +func gins(as int, f *gc.Node, t *gc.Node) *obj.Prog { + // Node nod; + // int32 v; + + if f != nil && f.Op == gc.OINDEX { + gc.Fatal("gins OINDEX not implemented") + } + + // gc.Regalloc(&nod, ®node, Z); + // v = constnode.vconst; + // gc.Cgen(f->right, &nod); + // constnode.vconst = v; + // idx.reg = nod.reg; + // gc.Regfree(&nod); + if t != nil && t.Op == gc.OINDEX { + gc.Fatal("gins OINDEX not implemented") + } + + // gc.Regalloc(&nod, ®node, Z); + // v = constnode.vconst; + // gc.Cgen(t->right, &nod); + // constnode.vconst = v; + // idx.reg = nod.reg; + // gc.Regfree(&nod); + + p := gc.Prog(as) + gc.Naddr(&p.From, f) + gc.Naddr(&p.To, t) + + switch as { + case arm.ABL: + if p.To.Type == obj.TYPE_REG { + p.To.Type = obj.TYPE_MEM + } + + case arm.ACMP, arm.ACMPF, arm.ACMPD: + if t != nil { + if f.Op != gc.OREGISTER { + /* generate a comparison + TODO(kaib): one of the args can actually be a small constant. relax the constraint and fix call sites. + */ + gc.Fatal("bad operands to gcmp") + } + p.From = p.To + p.To = obj.Addr{} + raddr(f, p) + } + + case arm.AMULU: + if f != nil && f.Op != gc.OREGISTER { + gc.Fatal("bad operands to mul") + } + + case arm.AMOVW: + if (p.From.Type == obj.TYPE_MEM || p.From.Type == obj.TYPE_ADDR || p.From.Type == obj.TYPE_CONST) && (p.To.Type == obj.TYPE_MEM || p.To.Type == obj.TYPE_ADDR) { + gc.Fatal("gins double memory") + } + + case arm.AADD: + if p.To.Type == obj.TYPE_MEM { + gc.Fatal("gins arith to mem") + } + + case arm.ARSB: + if p.From.Type == obj.TYPE_NONE { + gc.Fatal("rsb with no from") + } + } + + if gc.Debug['g'] != 0 { + fmt.Printf("%v\n", p) + } + return p +} + +/* + * insert n into reg slot of p + */ +func raddr(n *gc.Node, p *obj.Prog) { + var a obj.Addr + gc.Naddr(&a, n) + if a.Type != obj.TYPE_REG { + if n != nil { + gc.Fatal("bad in raddr: %v", gc.Oconv(int(n.Op), 0)) + } else { + gc.Fatal("bad in raddr: ") + } + p.Reg = 0 + } else { + p.Reg = a.Reg + } +} + +/* generate a constant shift + * arm encodes a shift by 32 as 0, thus asking for 0 shift is illegal. + */ +func gshift(as int, lhs *gc.Node, stype int32, sval int32, rhs *gc.Node) *obj.Prog { + if sval <= 0 || sval > 32 { + gc.Fatal("bad shift value: %d", sval) + } + + sval = sval & 0x1f + + p := gins(as, nil, rhs) + p.From.Type = obj.TYPE_SHIFT + p.From.Offset = int64(stype) | int64(sval)<<7 | int64(lhs.Reg)&15 + return p +} + +/* generate a register shift + */ +func gregshift(as int, lhs *gc.Node, stype int32, reg *gc.Node, rhs *gc.Node) *obj.Prog { + p := gins(as, nil, rhs) + p.From.Type = obj.TYPE_SHIFT + p.From.Offset = int64(stype) | (int64(reg.Reg)&15)<<8 | 1<<4 | int64(lhs.Reg)&15 + return p +} + +/* + * return Axxx for Oxxx on type t. + */ +func optoas(op int, t *gc.Type) int { + if t == nil { + gc.Fatal("optoas: t is nil") + } + + a := obj.AXXX + switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) { + default: + gc.Fatal("optoas: no entry %v-%v etype %v simtype %v", gc.Oconv(int(op), 0), t, gc.Types[t.Etype], gc.Types[gc.Simtype[t.Etype]]) + + /* case CASE(OADDR, TPTR32): + a = ALEAL; + break; + + case CASE(OADDR, TPTR64): + a = ALEAQ; + break; + */ + // TODO(kaib): make sure the conditional branches work on all edge cases + case gc.OEQ<<16 | gc.TBOOL, + gc.OEQ<<16 | gc.TINT8, + gc.OEQ<<16 | gc.TUINT8, + gc.OEQ<<16 | gc.TINT16, + gc.OEQ<<16 | gc.TUINT16, + gc.OEQ<<16 | gc.TINT32, + gc.OEQ<<16 | gc.TUINT32, + gc.OEQ<<16 | gc.TINT64, + gc.OEQ<<16 | gc.TUINT64, + gc.OEQ<<16 | gc.TPTR32, + gc.OEQ<<16 | gc.TPTR64, + gc.OEQ<<16 | gc.TFLOAT32, + gc.OEQ<<16 | gc.TFLOAT64: + a = arm.ABEQ + + case gc.ONE<<16 | gc.TBOOL, + gc.ONE<<16 | gc.TINT8, + gc.ONE<<16 | gc.TUINT8, + gc.ONE<<16 | gc.TINT16, + gc.ONE<<16 | gc.TUINT16, + gc.ONE<<16 | gc.TINT32, + gc.ONE<<16 | gc.TUINT32, + gc.ONE<<16 | gc.TINT64, + gc.ONE<<16 | gc.TUINT64, + gc.ONE<<16 | gc.TPTR32, + gc.ONE<<16 | gc.TPTR64, + gc.ONE<<16 | gc.TFLOAT32, + gc.ONE<<16 | gc.TFLOAT64: + a = arm.ABNE + + case gc.OLT<<16 | gc.TINT8, + gc.OLT<<16 | gc.TINT16, + gc.OLT<<16 | gc.TINT32, + gc.OLT<<16 | gc.TINT64, + gc.OLT<<16 | gc.TFLOAT32, + gc.OLT<<16 | gc.TFLOAT64: + a = arm.ABLT + + case gc.OLT<<16 | gc.TUINT8, + gc.OLT<<16 | gc.TUINT16, + gc.OLT<<16 | gc.TUINT32, + gc.OLT<<16 | gc.TUINT64: + a = arm.ABLO + + case gc.OLE<<16 | gc.TINT8, + gc.OLE<<16 | gc.TINT16, + gc.OLE<<16 | gc.TINT32, + gc.OLE<<16 | gc.TINT64, + gc.OLE<<16 | gc.TFLOAT32, + gc.OLE<<16 | gc.TFLOAT64: + a = arm.ABLE + + case gc.OLE<<16 | gc.TUINT8, + gc.OLE<<16 | gc.TUINT16, + gc.OLE<<16 | gc.TUINT32, + gc.OLE<<16 | gc.TUINT64: + a = arm.ABLS + + case gc.OGT<<16 | gc.TINT8, + gc.OGT<<16 | gc.TINT16, + gc.OGT<<16 | gc.TINT32, + gc.OGT<<16 | gc.TINT64, + gc.OGT<<16 | gc.TFLOAT32, + gc.OGT<<16 | gc.TFLOAT64: + a = arm.ABGT + + case gc.OGT<<16 | gc.TUINT8, + gc.OGT<<16 | gc.TUINT16, + gc.OGT<<16 | gc.TUINT32, + gc.OGT<<16 | gc.TUINT64: + a = arm.ABHI + + case gc.OGE<<16 | gc.TINT8, + gc.OGE<<16 | gc.TINT16, + gc.OGE<<16 | gc.TINT32, + gc.OGE<<16 | gc.TINT64, + gc.OGE<<16 | gc.TFLOAT32, + gc.OGE<<16 | gc.TFLOAT64: + a = arm.ABGE + + case gc.OGE<<16 | gc.TUINT8, + gc.OGE<<16 | gc.TUINT16, + gc.OGE<<16 | gc.TUINT32, + gc.OGE<<16 | gc.TUINT64: + a = arm.ABHS + + case gc.OCMP<<16 | gc.TBOOL, + gc.OCMP<<16 | gc.TINT8, + gc.OCMP<<16 | gc.TUINT8, + gc.OCMP<<16 | gc.TINT16, + gc.OCMP<<16 | gc.TUINT16, + gc.OCMP<<16 | gc.TINT32, + gc.OCMP<<16 | gc.TUINT32, + gc.OCMP<<16 | gc.TPTR32: + a = arm.ACMP + + case gc.OCMP<<16 | gc.TFLOAT32: + a = arm.ACMPF + + case gc.OCMP<<16 | gc.TFLOAT64: + a = arm.ACMPD + + case gc.OPS<<16 | gc.TFLOAT32, + gc.OPS<<16 | gc.TFLOAT64: + a = arm.ABVS + + case gc.OAS<<16 | gc.TBOOL: + a = arm.AMOVB + + case gc.OAS<<16 | gc.TINT8: + a = arm.AMOVBS + + case gc.OAS<<16 | gc.TUINT8: + a = arm.AMOVBU + + case gc.OAS<<16 | gc.TINT16: + a = arm.AMOVHS + + case gc.OAS<<16 | gc.TUINT16: + a = arm.AMOVHU + + case gc.OAS<<16 | gc.TINT32, + gc.OAS<<16 | gc.TUINT32, + gc.OAS<<16 | gc.TPTR32: + a = arm.AMOVW + + case gc.OAS<<16 | gc.TFLOAT32: + a = arm.AMOVF + + case gc.OAS<<16 | gc.TFLOAT64: + a = arm.AMOVD + + case gc.OADD<<16 | gc.TINT8, + gc.OADD<<16 | gc.TUINT8, + gc.OADD<<16 | gc.TINT16, + gc.OADD<<16 | gc.TUINT16, + gc.OADD<<16 | gc.TINT32, + gc.OADD<<16 | gc.TUINT32, + gc.OADD<<16 | gc.TPTR32: + a = arm.AADD + + case gc.OADD<<16 | gc.TFLOAT32: + a = arm.AADDF + + case gc.OADD<<16 | gc.TFLOAT64: + a = arm.AADDD + + case gc.OSUB<<16 | gc.TINT8, + gc.OSUB<<16 | gc.TUINT8, + gc.OSUB<<16 | gc.TINT16, + gc.OSUB<<16 | gc.TUINT16, + gc.OSUB<<16 | gc.TINT32, + gc.OSUB<<16 | gc.TUINT32, + gc.OSUB<<16 | gc.TPTR32: + a = arm.ASUB + + case gc.OSUB<<16 | gc.TFLOAT32: + a = arm.ASUBF + + case gc.OSUB<<16 | gc.TFLOAT64: + a = arm.ASUBD + + case gc.OMINUS<<16 | gc.TINT8, + gc.OMINUS<<16 | gc.TUINT8, + gc.OMINUS<<16 | gc.TINT16, + gc.OMINUS<<16 | gc.TUINT16, + gc.OMINUS<<16 | gc.TINT32, + gc.OMINUS<<16 | gc.TUINT32, + gc.OMINUS<<16 | gc.TPTR32: + a = arm.ARSB + + case gc.OAND<<16 | gc.TINT8, + gc.OAND<<16 | gc.TUINT8, + gc.OAND<<16 | gc.TINT16, + gc.OAND<<16 | gc.TUINT16, + gc.OAND<<16 | gc.TINT32, + gc.OAND<<16 | gc.TUINT32, + gc.OAND<<16 | gc.TPTR32: + a = arm.AAND + + case gc.OOR<<16 | gc.TINT8, + gc.OOR<<16 | gc.TUINT8, + gc.OOR<<16 | gc.TINT16, + gc.OOR<<16 | gc.TUINT16, + gc.OOR<<16 | gc.TINT32, + gc.OOR<<16 | gc.TUINT32, + gc.OOR<<16 | gc.TPTR32: + a = arm.AORR + + case gc.OXOR<<16 | gc.TINT8, + gc.OXOR<<16 | gc.TUINT8, + gc.OXOR<<16 | gc.TINT16, + gc.OXOR<<16 | gc.TUINT16, + gc.OXOR<<16 | gc.TINT32, + gc.OXOR<<16 | gc.TUINT32, + gc.OXOR<<16 | gc.TPTR32: + a = arm.AEOR + + case gc.OLSH<<16 | gc.TINT8, + gc.OLSH<<16 | gc.TUINT8, + gc.OLSH<<16 | gc.TINT16, + gc.OLSH<<16 | gc.TUINT16, + gc.OLSH<<16 | gc.TINT32, + gc.OLSH<<16 | gc.TUINT32, + gc.OLSH<<16 | gc.TPTR32: + a = arm.ASLL + + case gc.ORSH<<16 | gc.TUINT8, + gc.ORSH<<16 | gc.TUINT16, + gc.ORSH<<16 | gc.TUINT32, + gc.ORSH<<16 | gc.TPTR32: + a = arm.ASRL + + case gc.ORSH<<16 | gc.TINT8, + gc.ORSH<<16 | gc.TINT16, + gc.ORSH<<16 | gc.TINT32: + a = arm.ASRA + + case gc.OMUL<<16 | gc.TUINT8, + gc.OMUL<<16 | gc.TUINT16, + gc.OMUL<<16 | gc.TUINT32, + gc.OMUL<<16 | gc.TPTR32: + a = arm.AMULU + + case gc.OMUL<<16 | gc.TINT8, + gc.OMUL<<16 | gc.TINT16, + gc.OMUL<<16 | gc.TINT32: + a = arm.AMUL + + case gc.OMUL<<16 | gc.TFLOAT32: + a = arm.AMULF + + case gc.OMUL<<16 | gc.TFLOAT64: + a = arm.AMULD + + case gc.ODIV<<16 | gc.TUINT8, + gc.ODIV<<16 | gc.TUINT16, + gc.ODIV<<16 | gc.TUINT32, + gc.ODIV<<16 | gc.TPTR32: + a = arm.ADIVU + + case gc.ODIV<<16 | gc.TINT8, + gc.ODIV<<16 | gc.TINT16, + gc.ODIV<<16 | gc.TINT32: + a = arm.ADIV + + case gc.OMOD<<16 | gc.TUINT8, + gc.OMOD<<16 | gc.TUINT16, + gc.OMOD<<16 | gc.TUINT32, + gc.OMOD<<16 | gc.TPTR32: + a = arm.AMODU + + case gc.OMOD<<16 | gc.TINT8, + gc.OMOD<<16 | gc.TINT16, + gc.OMOD<<16 | gc.TINT32: + a = arm.AMOD + + // case CASE(OEXTEND, TINT16): + // a = ACWD; + // break; + + // case CASE(OEXTEND, TINT32): + // a = ACDQ; + // break; + + // case CASE(OEXTEND, TINT64): + // a = ACQO; + // break; + + case gc.ODIV<<16 | gc.TFLOAT32: + a = arm.ADIVF + + case gc.ODIV<<16 | gc.TFLOAT64: + a = arm.ADIVD + + case gc.OSQRT<<16 | gc.TFLOAT64: + a = arm.ASQRTD + } + + return a +} + +const ( + ODynam = 1 << 0 + OPtrto = 1 << 1 +) + +var clean [20]gc.Node + +var cleani int = 0 + +func sudoclean() { + if clean[cleani-1].Op != gc.OEMPTY { + gc.Regfree(&clean[cleani-1]) + } + if clean[cleani-2].Op != gc.OEMPTY { + gc.Regfree(&clean[cleani-2]) + } + cleani -= 2 +} + +func dotaddable(n *gc.Node, n1 *gc.Node) bool { + if n.Op != gc.ODOT { + return false + } + + var oary [10]int64 + var nn *gc.Node + o := gc.Dotoffset(n, oary[:], &nn) + if nn != nil && nn.Addable && o == 1 && oary[0] >= 0 { + *n1 = *nn + n1.Type = n.Type + n1.Xoffset += oary[0] + return true + } + + return false +} + +/* + * generate code to compute address of n, + * a reference to a (perhaps nested) field inside + * an array or struct. + * return 0 on failure, 1 on success. + * on success, leaves usable address in a. + * + * caller is responsible for calling sudoclean + * after successful sudoaddable, + * to release the register used for a. + */ +func sudoaddable(as int, n *gc.Node, a *obj.Addr) bool { + if n.Type == nil { + return false + } + + *a = obj.Addr{} + + switch n.Op { + case gc.OLITERAL: + if !gc.Isconst(n, gc.CTINT) { + break + } + v := n.Int() + if v >= 32000 || v <= -32000 { + break + } + switch as { + default: + return false + + case arm.AADD, + arm.ASUB, + arm.AAND, + arm.AORR, + arm.AEOR, + arm.AMOVB, + arm.AMOVBS, + arm.AMOVBU, + arm.AMOVH, + arm.AMOVHS, + arm.AMOVHU, + arm.AMOVW: + break + } + + cleani += 2 + reg := &clean[cleani-1] + reg1 := &clean[cleani-2] + reg.Op = gc.OEMPTY + reg1.Op = gc.OEMPTY + gc.Naddr(a, n) + return true + + case gc.ODOT, + gc.ODOTPTR: + cleani += 2 + reg := &clean[cleani-1] + reg1 := &clean[cleani-2] + reg.Op = gc.OEMPTY + reg1.Op = gc.OEMPTY + var nn *gc.Node + var oary [10]int64 + o := gc.Dotoffset(n, oary[:], &nn) + if nn == nil { + sudoclean() + return false + } + + if nn.Addable && o == 1 && oary[0] >= 0 { + // directly addressable set of DOTs + n1 := *nn + + n1.Type = n.Type + n1.Xoffset += oary[0] + gc.Naddr(a, &n1) + return true + } + + gc.Regalloc(reg, gc.Types[gc.Tptr], nil) + n1 := *reg + n1.Op = gc.OINDREG + if oary[0] >= 0 { + gc.Agen(nn, reg) + n1.Xoffset = oary[0] + } else { + gc.Cgen(nn, reg) + gc.Cgen_checknil(reg) + n1.Xoffset = -(oary[0] + 1) + } + + for i := 1; i < o; i++ { + if oary[i] >= 0 { + gc.Fatal("can't happen") + } + gins(arm.AMOVW, &n1, reg) + gc.Cgen_checknil(reg) + n1.Xoffset = -(oary[i] + 1) + } + + a.Type = obj.TYPE_NONE + a.Name = obj.NAME_NONE + n1.Type = n.Type + gc.Naddr(a, &n1) + return true + + case gc.OINDEX: + return false + } + + return false +} diff --git a/src/cmd/compile/internal/arm/peep.go b/src/cmd/compile/internal/arm/peep.go new file mode 100644 index 0000000000..66eba417c0 --- /dev/null +++ b/src/cmd/compile/internal/arm/peep.go @@ -0,0 +1,1748 @@ +// Inferno utils/5c/peep.c +// http://code.google.com/p/inferno-os/source/browse/utils/5c/peep.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package arm + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/arm" + "fmt" +) + +var gactive uint32 + +// UNUSED +func peep(firstp *obj.Prog) { + g := (*gc.Graph)(gc.Flowstart(firstp, nil)) + if g == nil { + return + } + gactive = 0 + + var r *gc.Flow + var p *obj.Prog + var t int +loop1: + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + gc.Dumpit("loop1", g.Start, 0) + } + + t = 0 + for r = g.Start; r != nil; r = r.Link { + p = r.Prog + switch p.As { + /* + * elide shift into TYPE_SHIFT operand of subsequent instruction + */ + // if(shiftprop(r)) { + // excise(r); + // t++; + // break; + // } + case arm.ASLL, + arm.ASRL, + arm.ASRA: + break + + case arm.AMOVB, + arm.AMOVH, + arm.AMOVW, + arm.AMOVF, + arm.AMOVD: + if regtyp(&p.From) { + if p.From.Type == p.To.Type && isfloatreg(&p.From) == isfloatreg(&p.To) { + if p.Scond == arm.C_SCOND_NONE { + if copyprop(g, r) { + excise(r) + t++ + break + } + + if subprop(r) && copyprop(g, r) { + excise(r) + t++ + break + } + } + } + } + + case arm.AMOVHS, + arm.AMOVHU, + arm.AMOVBS, + arm.AMOVBU: + if p.From.Type == obj.TYPE_REG { + if shortprop(r) { + t++ + } + } + } + } + + /* + if(p->scond == C_SCOND_NONE) + if(regtyp(&p->to)) + if(isdconst(&p->from)) { + constprop(&p->from, &p->to, r->s1); + } + break; + */ + if t != 0 { + goto loop1 + } + + for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { + p = r.Prog + switch p.As { + /* + * EOR -1,x,y => MVN x,y + */ + case arm.AEOR: + if isdconst(&p.From) && p.From.Offset == -1 { + p.As = arm.AMVN + p.From.Type = obj.TYPE_REG + if p.Reg != 0 { + p.From.Reg = p.Reg + } else { + p.From.Reg = p.To.Reg + } + p.Reg = 0 + } + } + } + + for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { + p = r.Prog + switch p.As { + case arm.AMOVW, + arm.AMOVB, + arm.AMOVBS, + arm.AMOVBU: + if p.From.Type == obj.TYPE_MEM && p.From.Offset == 0 { + xtramodes(g, r, &p.From) + } else if p.To.Type == obj.TYPE_MEM && p.To.Offset == 0 { + xtramodes(g, r, &p.To) + } else { + continue + } + } + } + + // case ACMP: + // /* + // * elide CMP $0,x if calculation of x can set condition codes + // */ + // if(isdconst(&p->from) || p->from.offset != 0) + // continue; + // r2 = r->s1; + // if(r2 == nil) + // continue; + // t = r2->prog->as; + // switch(t) { + // default: + // continue; + // case ABEQ: + // case ABNE: + // case ABMI: + // case ABPL: + // break; + // case ABGE: + // t = ABPL; + // break; + // case ABLT: + // t = ABMI; + // break; + // case ABHI: + // t = ABNE; + // break; + // case ABLS: + // t = ABEQ; + // break; + // } + // r1 = r; + // do + // r1 = uniqp(r1); + // while (r1 != nil && r1->prog->as == ANOP); + // if(r1 == nil) + // continue; + // p1 = r1->prog; + // if(p1->to.type != TYPE_REG) + // continue; + // if(p1->to.reg != p->reg) + // if(!(p1->as == AMOVW && p1->from.type == TYPE_REG && p1->from.reg == p->reg)) + // continue; + // + // switch(p1->as) { + // default: + // continue; + // case AMOVW: + // if(p1->from.type != TYPE_REG) + // continue; + // case AAND: + // case AEOR: + // case AORR: + // case ABIC: + // case AMVN: + // case ASUB: + // case ARSB: + // case AADD: + // case AADC: + // case ASBC: + // case ARSC: + // break; + // } + // p1->scond |= C_SBIT; + // r2->prog->as = t; + // excise(r); + // continue; + + // predicate(g); + + gc.Flowend(g) +} + +func regtyp(a *obj.Addr) bool { + return a.Type == obj.TYPE_REG && (arm.REG_R0 <= a.Reg && a.Reg <= arm.REG_R15 || arm.REG_F0 <= a.Reg && a.Reg <= arm.REG_F15) +} + +/* + * the idea is to substitute + * one register for another + * from one MOV to another + * MOV a, R0 + * ADD b, R0 / no use of R1 + * MOV R0, R1 + * would be converted to + * MOV a, R1 + * ADD b, R1 + * MOV R1, R0 + * hopefully, then the former or latter MOV + * will be eliminated by copy propagation. + */ +func subprop(r0 *gc.Flow) bool { + p := (*obj.Prog)(r0.Prog) + v1 := (*obj.Addr)(&p.From) + if !regtyp(v1) { + return false + } + v2 := (*obj.Addr)(&p.To) + if !regtyp(v2) { + return false + } + for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { + if gc.Uniqs(r) == nil { + break + } + p = r.Prog + if p.As == obj.AVARDEF || p.As == obj.AVARKILL { + continue + } + if p.Info.Flags&gc.Call != 0 { + return false + } + + // TODO(rsc): Whatever invalidated the info should have done this call. + proginfo(p) + + if (p.Info.Flags&gc.CanRegRead != 0) && p.To.Type == obj.TYPE_REG { + p.Info.Flags |= gc.RegRead + p.Info.Flags &^= (gc.CanRegRead | gc.RightRead) + p.Reg = p.To.Reg + } + + switch p.As { + case arm.AMULLU, + arm.AMULA, + arm.AMVN: + return false + } + + if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightWrite { + if p.To.Type == v1.Type { + if p.To.Reg == v1.Reg { + if p.Scond == arm.C_SCOND_NONE { + copysub(&p.To, v1, v2, 1) + if gc.Debug['P'] != 0 { + fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog) + if p.From.Type == v2.Type { + fmt.Printf(" excise") + } + fmt.Printf("\n") + } + + for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { + p = r.Prog + copysub(&p.From, v1, v2, 1) + copysub1(p, v1, v2, 1) + copysub(&p.To, v1, v2, 1) + if gc.Debug['P'] != 0 { + fmt.Printf("%v\n", r.Prog) + } + } + + t := int(int(v1.Reg)) + v1.Reg = v2.Reg + v2.Reg = int16(t) + if gc.Debug['P'] != 0 { + fmt.Printf("%v last\n", r.Prog) + } + return true + } + } + } + } + + if copyau(&p.From, v2) || copyau1(p, v2) || copyau(&p.To, v2) { + break + } + if copysub(&p.From, v1, v2, 0) != 0 || copysub1(p, v1, v2, 0) != 0 || copysub(&p.To, v1, v2, 0) != 0 { + break + } + } + + return false +} + +/* + * The idea is to remove redundant copies. + * v1->v2 F=0 + * (use v2 s/v2/v1/)* + * set v1 F=1 + * use v2 return fail + * ----------------- + * v1->v2 F=0 + * (use v2 s/v2/v1/)* + * set v1 F=1 + * set v2 return success + */ +func copyprop(g *gc.Graph, r0 *gc.Flow) bool { + p := (*obj.Prog)(r0.Prog) + v1 := (*obj.Addr)(&p.From) + v2 := (*obj.Addr)(&p.To) + if copyas(v1, v2) { + return true + } + gactive++ + return copy1(v1, v2, r0.S1, 0) +} + +func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f int) bool { + if uint32(r.Active) == gactive { + if gc.Debug['P'] != 0 { + fmt.Printf("act set; return 1\n") + } + return true + } + + r.Active = int32(gactive) + if gc.Debug['P'] != 0 { + fmt.Printf("copy %v->%v f=%d\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), f) + } + var t int + var p *obj.Prog + for ; r != nil; r = r.S1 { + p = r.Prog + if gc.Debug['P'] != 0 { + fmt.Printf("%v", p) + } + if f == 0 && gc.Uniqp(r) == nil { + f = 1 + if gc.Debug['P'] != 0 { + fmt.Printf("; merge; f=%d", f) + } + } + + t = copyu(p, v2, nil) + switch t { + case 2: /* rar, can't split */ + if gc.Debug['P'] != 0 { + fmt.Printf("; %vrar; return 0\n", gc.Ctxt.Dconv(v2)) + } + return false + + case 3: /* set */ + if gc.Debug['P'] != 0 { + fmt.Printf("; %vset; return 1\n", gc.Ctxt.Dconv(v2)) + } + return true + + case 1, /* used, substitute */ + 4: /* use and set */ + if f != 0 { + if gc.Debug['P'] == 0 { + return false + } + if t == 4 { + fmt.Printf("; %vused+set and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f) + } else { + fmt.Printf("; %vused and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f) + } + return false + } + + if copyu(p, v2, v1) != 0 { + if gc.Debug['P'] != 0 { + fmt.Printf("; sub fail; return 0\n") + } + return false + } + + if gc.Debug['P'] != 0 { + fmt.Printf("; sub%v/%v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1)) + } + if t == 4 { + if gc.Debug['P'] != 0 { + fmt.Printf("; %vused+set; return 1\n", gc.Ctxt.Dconv(v2)) + } + return true + } + } + + if f == 0 { + t = copyu(p, v1, nil) + if f == 0 && (t == 2 || t == 3 || t == 4) { + f = 1 + if gc.Debug['P'] != 0 { + fmt.Printf("; %vset and !f; f=%d", gc.Ctxt.Dconv(v1), f) + } + } + } + + if gc.Debug['P'] != 0 { + fmt.Printf("\n") + } + if r.S2 != nil { + if !copy1(v1, v2, r.S2, f) { + return false + } + } + } + + return true +} + +// UNUSED +/* + * The idea is to remove redundant constants. + * $c1->v1 + * ($c1->v2 s/$c1/v1)* + * set v1 return + * The v1->v2 should be eliminated by copy propagation. + */ +func constprop(c1 *obj.Addr, v1 *obj.Addr, r *gc.Flow) { + if gc.Debug['P'] != 0 { + fmt.Printf("constprop %v->%v\n", gc.Ctxt.Dconv(c1), gc.Ctxt.Dconv(v1)) + } + var p *obj.Prog + for ; r != nil; r = r.S1 { + p = r.Prog + if gc.Debug['P'] != 0 { + fmt.Printf("%v", p) + } + if gc.Uniqp(r) == nil { + if gc.Debug['P'] != 0 { + fmt.Printf("; merge; return\n") + } + return + } + + if p.As == arm.AMOVW && copyas(&p.From, c1) { + if gc.Debug['P'] != 0 { + fmt.Printf("; sub%v/%v", gc.Ctxt.Dconv(&p.From), gc.Ctxt.Dconv(v1)) + } + p.From = *v1 + } else if copyu(p, v1, nil) > 1 { + if gc.Debug['P'] != 0 { + fmt.Printf("; %vset; return\n", gc.Ctxt.Dconv(v1)) + } + return + } + + if gc.Debug['P'] != 0 { + fmt.Printf("\n") + } + if r.S2 != nil { + constprop(c1, v1, r.S2) + } + } +} + +/* + * shortprop eliminates redundant zero/sign extensions. + * + * MOVBS x, R + * + * MOVBS R, R' + * + * changed to + * + * MOVBS x, R + * ... + * MOVB R, R' (compiled to mov) + * + * MOVBS above can be a MOVBS, MOVBU, MOVHS or MOVHU. + */ +func shortprop(r *gc.Flow) bool { + p := (*obj.Prog)(r.Prog) + r1 := (*gc.Flow)(findpre(r, &p.From)) + if r1 == nil { + return false + } + + p1 := (*obj.Prog)(r1.Prog) + if p1.As == p.As { + // Two consecutive extensions. + goto gotit + } + + if p1.As == arm.AMOVW && isdconst(&p1.From) && p1.From.Offset >= 0 && p1.From.Offset < 128 { + // Loaded an immediate. + goto gotit + } + + return false + +gotit: + if gc.Debug['P'] != 0 { + fmt.Printf("shortprop\n%v\n%v", p1, p) + } + switch p.As { + case arm.AMOVBS, + arm.AMOVBU: + p.As = arm.AMOVB + + case arm.AMOVHS, + arm.AMOVHU: + p.As = arm.AMOVH + } + + if gc.Debug['P'] != 0 { + fmt.Printf(" => %v\n", obj.Aconv(int(p.As))) + } + return true +} + +// UNUSED +/* + * ASLL x,y,w + * .. (not use w, not set x y w) + * AXXX w,a,b (a != w) + * .. (not use w) + * (set w) + * ----------- changed to + * .. + * AXXX (x< 1) || (a.Type == obj.TYPE_REG && copyu(p1, &a, nil) > 1) { + if gc.Debug['P'] != 0 { + fmt.Printf("\targs modified; FAILURE\n") + } + return false + } + + continue + case 3: /* set, not used */ + { + if gc.Debug['P'] != 0 { + fmt.Printf("\tBOTCH: noref; FAILURE\n") + } + return false + } + } + + break + } + + /* check whether substitution can be done */ + switch p1.As { + default: + if gc.Debug['P'] != 0 { + fmt.Printf("\tnon-dpi; FAILURE\n") + } + return false + + case arm.AAND, + arm.AEOR, + arm.AADD, + arm.AADC, + arm.AORR, + arm.ASUB, + arm.ASBC, + arm.ARSB, + arm.ARSC: + if int(p1.Reg) == n || (p1.Reg == 0 && p1.To.Type == obj.TYPE_REG && int(p1.To.Reg) == n) { + if p1.From.Type != obj.TYPE_REG { + if gc.Debug['P'] != 0 { + fmt.Printf("\tcan't swap; FAILURE\n") + } + return false + } + + p1.Reg = p1.From.Reg + p1.From.Reg = int16(n) + switch p1.As { + case arm.ASUB: + p1.As = arm.ARSB + + case arm.ARSB: + p1.As = arm.ASUB + + case arm.ASBC: + p1.As = arm.ARSC + + case arm.ARSC: + p1.As = arm.ASBC + } + + if gc.Debug['P'] != 0 { + fmt.Printf("\t=>%v", p1) + } + } + fallthrough + + case arm.ABIC, + arm.ATST, + arm.ACMP, + arm.ACMN: + if int(p1.Reg) == n { + if gc.Debug['P'] != 0 { + fmt.Printf("\tcan't swap; FAILURE\n") + } + return false + } + + if p1.Reg == 0 && int(p1.To.Reg) == n { + if gc.Debug['P'] != 0 { + fmt.Printf("\tshift result used twice; FAILURE\n") + } + return false + } + + // case AMVN: + if p1.From.Type == obj.TYPE_SHIFT { + if gc.Debug['P'] != 0 { + fmt.Printf("\tshift result used in shift; FAILURE\n") + } + return false + } + + if p1.From.Type != obj.TYPE_REG || int(p1.From.Reg) != n { + if gc.Debug['P'] != 0 { + fmt.Printf("\tBOTCH: where is it used?; FAILURE\n") + } + return false + } + } + + /* check whether shift result is used subsequently */ + p2 := (*obj.Prog)(p1) + + if int(p1.To.Reg) != n { + var p1 *obj.Prog + for { + r1 = gc.Uniqs(r1) + if r1 == nil { + if gc.Debug['P'] != 0 { + fmt.Printf("\tinconclusive; FAILURE\n") + } + return false + } + + p1 = r1.Prog + if gc.Debug['P'] != 0 { + fmt.Printf("\n%v", p1) + } + switch copyu(p1, &p.To, nil) { + case 0: /* not used or set */ + continue + + case 3: /* set, not used */ + break + + default: /* used */ + if gc.Debug['P'] != 0 { + fmt.Printf("\treused; FAILURE\n") + } + return false + } + + break + } + } + + /* make the substitution */ + p2.From.Reg = 0 + + o := int(int(p.Reg)) + if o == 0 { + o = int(p.To.Reg) + } + o &= 15 + + switch p.From.Type { + case obj.TYPE_CONST: + o |= int((p.From.Offset & 0x1f) << 7) + + case obj.TYPE_REG: + o |= 1<<4 | (int(p.From.Reg)&15)<<8 + } + + switch p.As { + case arm.ASLL: + o |= 0 << 5 + + case arm.ASRL: + o |= 1 << 5 + + case arm.ASRA: + o |= 2 << 5 + } + + p2.From = obj.Addr{} + p2.From.Type = obj.TYPE_SHIFT + p2.From.Offset = int64(o) + if gc.Debug['P'] != 0 { + fmt.Printf("\t=>%v\tSUCCEED\n", p2) + } + return true +} + +/* + * findpre returns the last instruction mentioning v + * before r. It must be a set, and there must be + * a unique path from that instruction to r. + */ +func findpre(r *gc.Flow, v *obj.Addr) *gc.Flow { + var r1 *gc.Flow + + for r1 = gc.Uniqp(r); r1 != nil; r, r1 = r1, gc.Uniqp(r1) { + if gc.Uniqs(r1) != r { + return nil + } + switch copyu(r1.Prog, v, nil) { + case 1, /* used */ + 2: /* read-alter-rewrite */ + return nil + + case 3, /* set */ + 4: /* set and used */ + return r1 + } + } + + return nil +} + +/* + * findinc finds ADD instructions with a constant + * argument which falls within the immed_12 range. + */ +func findinc(r *gc.Flow, r2 *gc.Flow, v *obj.Addr) *gc.Flow { + var r1 *gc.Flow + var p *obj.Prog + + for r1 = gc.Uniqs(r); r1 != nil && r1 != r2; r, r1 = r1, gc.Uniqs(r1) { + if gc.Uniqp(r1) != r { + return nil + } + switch copyu(r1.Prog, v, nil) { + case 0: /* not touched */ + continue + + case 4: /* set and used */ + p = r1.Prog + + if p.As == arm.AADD { + if isdconst(&p.From) { + if p.From.Offset > -4096 && p.From.Offset < 4096 { + return r1 + } + } + } + fallthrough + + default: + return nil + } + } + + return nil +} + +func nochange(r *gc.Flow, r2 *gc.Flow, p *obj.Prog) bool { + if r == r2 { + return true + } + n := int(0) + var a [3]obj.Addr + if p.Reg != 0 && p.Reg != p.To.Reg { + a[n].Type = obj.TYPE_REG + a[n].Reg = p.Reg + n++ + } + + switch p.From.Type { + case obj.TYPE_SHIFT: + a[n].Type = obj.TYPE_REG + a[n].Reg = int16(arm.REG_R0 + (p.From.Offset & 0xf)) + n++ + fallthrough + + case obj.TYPE_REG: + a[n].Type = obj.TYPE_REG + a[n].Reg = p.From.Reg + n++ + } + + if n == 0 { + return true + } + var i int + for ; r != nil && r != r2; r = gc.Uniqs(r) { + p = r.Prog + for i = 0; i < n; i++ { + if copyu(p, &a[i], nil) > 1 { + return false + } + } + } + + return true +} + +func findu1(r *gc.Flow, v *obj.Addr) bool { + for ; r != nil; r = r.S1 { + if r.Active != 0 { + return false + } + r.Active = 1 + switch copyu(r.Prog, v, nil) { + case 1, /* used */ + 2, /* read-alter-rewrite */ + 4: /* set and used */ + return true + + case 3: /* set */ + return false + } + + if r.S2 != nil { + if findu1(r.S2, v) { + return true + } + } + } + + return false +} + +func finduse(g *gc.Graph, r *gc.Flow, v *obj.Addr) bool { + for r1 := (*gc.Flow)(g.Start); r1 != nil; r1 = r1.Link { + r1.Active = 0 + } + return findu1(r, v) +} + +/* + * xtramodes enables the ARM post increment and + * shift offset addressing modes to transform + * MOVW 0(R3),R1 + * ADD $4,R3,R3 + * into + * MOVW.P 4(R3),R1 + * and + * ADD R0,R1 + * MOVBU 0(R1),R0 + * into + * MOVBU R0<<0(R1),R0 + */ +func xtramodes(g *gc.Graph, r *gc.Flow, a *obj.Addr) bool { + p := (*obj.Prog)(r.Prog) + v := obj.Addr(*a) + v.Type = obj.TYPE_REG + r1 := (*gc.Flow)(findpre(r, &v)) + if r1 != nil { + p1 := r1.Prog + if p1.To.Type == obj.TYPE_REG && p1.To.Reg == v.Reg { + switch p1.As { + case arm.AADD: + if p1.Scond&arm.C_SBIT != 0 { + // avoid altering ADD.S/ADC sequences. + break + } + + if p1.From.Type == obj.TYPE_REG || (p1.From.Type == obj.TYPE_SHIFT && p1.From.Offset&(1<<4) == 0 && ((p.As != arm.AMOVB && p.As != arm.AMOVBS) || (a == &p.From && p1.From.Offset&^0xf == 0))) || ((p1.From.Type == obj.TYPE_ADDR || p1.From.Type == obj.TYPE_CONST) && p1.From.Offset > -4096 && p1.From.Offset < 4096) { + if nochange(gc.Uniqs(r1), r, p1) { + if a != &p.From || v.Reg != p.To.Reg { + if finduse(g, r.S1, &v) { + if p1.Reg == 0 || p1.Reg == v.Reg { + /* pre-indexing */ + p.Scond |= arm.C_WBIT + } else { + return false + } + } + } + + switch p1.From.Type { + /* register offset */ + case obj.TYPE_REG: + if gc.Nacl { + return false + } + *a = obj.Addr{} + a.Type = obj.TYPE_SHIFT + a.Offset = int64(p1.From.Reg) & 15 + + /* scaled register offset */ + case obj.TYPE_SHIFT: + if gc.Nacl { + return false + } + *a = obj.Addr{} + a.Type = obj.TYPE_SHIFT + fallthrough + + /* immediate offset */ + case obj.TYPE_CONST, + obj.TYPE_ADDR: + a.Offset = p1.From.Offset + } + + if p1.Reg != 0 { + a.Reg = p1.Reg + } + excise(r1) + return true + } + } + + case arm.AMOVW: + if p1.From.Type == obj.TYPE_REG { + r2 := (*gc.Flow)(findinc(r1, r, &p1.From)) + if r2 != nil { + var r3 *gc.Flow + for r3 = gc.Uniqs(r2); r3.Prog.As == obj.ANOP; r3 = gc.Uniqs(r3) { + } + if r3 == r { + /* post-indexing */ + p1 := r2.Prog + + a.Reg = p1.To.Reg + a.Offset = p1.From.Offset + p.Scond |= arm.C_PBIT + if !finduse(g, r, &r1.Prog.To) { + excise(r1) + } + excise(r2) + return true + } + } + } + } + } + } + + if a != &p.From || a.Reg != p.To.Reg { + r1 := (*gc.Flow)(findinc(r, nil, &v)) + if r1 != nil { + /* post-indexing */ + p1 := r1.Prog + + a.Offset = p1.From.Offset + p.Scond |= arm.C_PBIT + excise(r1) + return true + } + } + + return false +} + +/* + * return + * 1 if v only used (and substitute), + * 2 if read-alter-rewrite + * 3 if set + * 4 if set and used + * 0 otherwise (not touched) + */ +func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int { + switch p.As { + default: + fmt.Printf("copyu: can't find %v\n", obj.Aconv(int(p.As))) + return 2 + + case arm.AMOVM: + if v.Type != obj.TYPE_REG { + return 0 + } + if p.From.Type == obj.TYPE_CONST { /* read reglist, read/rar */ + if s != nil { + if p.From.Offset&(1<type to v->name and enable. + //if(v->type == NAME_AUTO || v->type == NAME_PARAM) { + // if(v->offset == a->offset) + // return 1; + //} + return false +} + +/* + * either direct or indirect + */ +func copyau(a *obj.Addr, v *obj.Addr) bool { + if copyas(a, v) { + return true + } + if v.Type == obj.TYPE_REG { + if a.Type == obj.TYPE_ADDR && a.Reg != 0 { + if a.Reg == v.Reg { + return true + } + } else if a.Type == obj.TYPE_MEM { + if a.Reg == v.Reg { + return true + } + } else if a.Type == obj.TYPE_REGREG || a.Type == obj.TYPE_REGREG2 { + if a.Reg == v.Reg { + return true + } + if a.Offset == int64(v.Reg) { + return true + } + } else if a.Type == obj.TYPE_SHIFT { + if a.Offset&0xf == int64(v.Reg-arm.REG_R0) { + return true + } + if (a.Offset&(1<<4) != 0) && (a.Offset>>8)&0xf == int64(v.Reg-arm.REG_R0) { + return true + } + } + } + + return false +} + +/* + * compare v to the center + * register in p (p->reg) + */ +func copyau1(p *obj.Prog, v *obj.Addr) bool { + if v.Type == obj.TYPE_REG && v.Reg == 0 { + return false + } + return p.Reg == v.Reg +} + +/* + * substitute s for v in a + * return failure to substitute + */ +func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f int) int { + if f != 0 { + if copyau(a, v) { + if a.Type == obj.TYPE_SHIFT { + if a.Offset&0xf == int64(v.Reg-arm.REG_R0) { + a.Offset = a.Offset&^0xf | int64(s.Reg)&0xf + } + if (a.Offset&(1<<4) != 0) && (a.Offset>>8)&0xf == int64(v.Reg-arm.REG_R0) { + a.Offset = a.Offset&^(0xf<<8) | (int64(s.Reg)&0xf)<<8 + } + } else if a.Type == obj.TYPE_REGREG || a.Type == obj.TYPE_REGREG2 { + if a.Offset == int64(v.Reg) { + a.Offset = int64(s.Reg) + } + if a.Reg == v.Reg { + a.Reg = s.Reg + } + } else { + a.Reg = s.Reg + } + } + } + + return 0 +} + +func copysub1(p1 *obj.Prog, v *obj.Addr, s *obj.Addr, f int) int { + if f != 0 { + if copyau1(p1, v) { + p1.Reg = s.Reg + } + } + return 0 +} + +var predinfo = []struct { + opcode int + notopcode int + scond int + notscond int +}{ + {arm.ABEQ, arm.ABNE, 0x0, 0x1}, + {arm.ABNE, arm.ABEQ, 0x1, 0x0}, + {arm.ABCS, arm.ABCC, 0x2, 0x3}, + {arm.ABHS, arm.ABLO, 0x2, 0x3}, + {arm.ABCC, arm.ABCS, 0x3, 0x2}, + {arm.ABLO, arm.ABHS, 0x3, 0x2}, + {arm.ABMI, arm.ABPL, 0x4, 0x5}, + {arm.ABPL, arm.ABMI, 0x5, 0x4}, + {arm.ABVS, arm.ABVC, 0x6, 0x7}, + {arm.ABVC, arm.ABVS, 0x7, 0x6}, + {arm.ABHI, arm.ABLS, 0x8, 0x9}, + {arm.ABLS, arm.ABHI, 0x9, 0x8}, + {arm.ABGE, arm.ABLT, 0xA, 0xB}, + {arm.ABLT, arm.ABGE, 0xB, 0xA}, + {arm.ABGT, arm.ABLE, 0xC, 0xD}, + {arm.ABLE, arm.ABGT, 0xD, 0xC}, +} + +type Joininfo struct { + start *gc.Flow + last *gc.Flow + end *gc.Flow + len int +} + +const ( + Join = iota + Split + End + Branch + Setcond + Toolong +) + +const ( + Falsecond = iota + Truecond + Delbranch + Keepbranch +) + +func isbranch(p *obj.Prog) bool { + return (arm.ABEQ <= p.As) && (p.As <= arm.ABLE) +} + +func predicable(p *obj.Prog) bool { + switch p.As { + case obj.ANOP, + obj.AXXX, + obj.ADATA, + obj.AGLOBL, + obj.ATEXT, + arm.AWORD, + arm.ABCASE, + arm.ACASE: + return false + } + + if isbranch(p) { + return false + } + return true +} + +/* + * Depends on an analysis of the encodings performed by 5l. + * These seem to be all of the opcodes that lead to the "S" bit + * being set in the instruction encodings. + * + * C_SBIT may also have been set explicitly in p->scond. + */ +func modifiescpsr(p *obj.Prog) bool { + switch p.As { + case arm.AMULLU, + arm.AMULA, + arm.AMULU, + arm.ADIVU, + arm.ATEQ, + arm.ACMN, + arm.ATST, + arm.ACMP, + arm.AMUL, + arm.ADIV, + arm.AMOD, + arm.AMODU, + arm.ABL: + return true + } + + if p.Scond&arm.C_SBIT != 0 { + return true + } + return false +} + +/* + * Find the maximal chain of instructions starting with r which could + * be executed conditionally + */ +func joinsplit(r *gc.Flow, j *Joininfo) int { + j.start = r + j.last = r + j.len = 0 + for { + if r.P2 != nil && (r.P1 != nil || r.P2.P2link != nil) { + j.end = r + return Join + } + + if r.S1 != nil && r.S2 != nil { + j.end = r + return Split + } + + j.last = r + if r.Prog.As != obj.ANOP { + j.len++ + } + if r.S1 == nil && r.S2 == nil { + j.end = r.Link + return End + } + + if r.S2 != nil { + j.end = r.S2 + return Branch + } + + if modifiescpsr(r.Prog) { + j.end = r.S1 + return Setcond + } + + r = r.S1 + if j.len >= 4 { + break + } + } + + j.end = r + return Toolong +} + +func successor(r *gc.Flow) *gc.Flow { + if r.S1 != nil { + return r.S1 + } else { + return r.S2 + } +} + +func applypred(rstart *gc.Flow, j *Joininfo, cond int, branch int) { + if j.len == 0 { + return + } + var pred int + if cond == Truecond { + pred = predinfo[rstart.Prog.As-arm.ABEQ].scond + } else { + pred = predinfo[rstart.Prog.As-arm.ABEQ].notscond + } + + for r := (*gc.Flow)(j.start); ; r = successor(r) { + if r.Prog.As == arm.AB { + if r != j.last || branch == Delbranch { + excise(r) + } else { + if cond == Truecond { + r.Prog.As = int16(predinfo[rstart.Prog.As-arm.ABEQ].opcode) + } else { + r.Prog.As = int16(predinfo[rstart.Prog.As-arm.ABEQ].notopcode) + } + } + } else if predicable(r.Prog) { + r.Prog.Scond = uint8(int(r.Prog.Scond&^arm.C_SCOND) | pred) + } + if r.S1 != r.Link { + r.S1 = r.Link + r.Link.P1 = r + } + + if r == j.last { + break + } + } +} + +func predicate(g *gc.Graph) { + var t1 int + var t2 int + var j1 Joininfo + var j2 Joininfo + + for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { + if isbranch(r.Prog) { + t1 = joinsplit(r.S1, &j1) + t2 = joinsplit(r.S2, &j2) + if j1.last.Link != j2.start { + continue + } + if j1.end == j2.end { + if (t1 == Branch && (t2 == Join || t2 == Setcond)) || (t2 == Join && (t1 == Join || t1 == Setcond)) { + applypred(r, &j1, Falsecond, Delbranch) + applypred(r, &j2, Truecond, Delbranch) + excise(r) + continue + } + } + + if t1 == End || t1 == Branch { + applypred(r, &j1, Falsecond, Keepbranch) + excise(r) + continue + } + } + } +} + +func isdconst(a *obj.Addr) bool { + return a.Type == obj.TYPE_CONST +} + +func isfloatreg(a *obj.Addr) bool { + return arm.REG_F0 <= a.Reg && a.Reg <= arm.REG_F15 +} + +func stackaddr(a *obj.Addr) bool { + return regtyp(a) && a.Reg == arm.REGSP +} + +func smallindir(a *obj.Addr, reg *obj.Addr) bool { + return reg.Type == obj.TYPE_REG && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && 0 <= a.Offset && a.Offset < 4096 +} + +func excise(r *gc.Flow) { + p := (*obj.Prog)(r.Prog) + obj.Nopout(p) +} diff --git a/src/cmd/compile/internal/arm/prog.go b/src/cmd/compile/internal/arm/prog.go new file mode 100644 index 0000000000..cdf9d29192 --- /dev/null +++ b/src/cmd/compile/internal/arm/prog.go @@ -0,0 +1,165 @@ +// Copyright 2013 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 arm + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/arm" +) + +const ( + RightRdwr = gc.RightRead | gc.RightWrite +) + +// This table gives the basic information about instruction +// generated by the compiler and processed in the optimizer. +// See opt.h for bit definitions. +// +// Instructions not generated need not be listed. +// As an exception to that rule, we typically write down all the +// size variants of an operation even if we just use a subset. +// +// The table is formatted for 8-space tabs. +var progtable = [arm.ALAST]obj.ProgInfo{ + obj.ATYPE: {gc.Pseudo | gc.Skip, 0, 0, 0}, + obj.ATEXT: {gc.Pseudo, 0, 0, 0}, + obj.AFUNCDATA: {gc.Pseudo, 0, 0, 0}, + obj.APCDATA: {gc.Pseudo, 0, 0, 0}, + obj.AUNDEF: {gc.Break, 0, 0, 0}, + obj.AUSEFIELD: {gc.OK, 0, 0, 0}, + obj.ACHECKNIL: {gc.LeftRead, 0, 0, 0}, + obj.AVARDEF: {gc.Pseudo | gc.RightWrite, 0, 0, 0}, + obj.AVARKILL: {gc.Pseudo | gc.RightWrite, 0, 0, 0}, + + // NOP is an internal no-op that also stands + // for USED and SET annotations, not the Intel opcode. + obj.ANOP: {gc.LeftRead | gc.RightWrite, 0, 0, 0}, + + // Integer. + arm.AADC: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm.AADD: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm.AAND: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm.ABIC: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm.ACMN: {gc.SizeL | gc.LeftRead | gc.RightRead, 0, 0, 0}, + arm.ACMP: {gc.SizeL | gc.LeftRead | gc.RightRead, 0, 0, 0}, + arm.ADIVU: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm.ADIV: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm.AEOR: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm.AMODU: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm.AMOD: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm.AMULALU: {gc.SizeL | gc.LeftRead | gc.RegRead | RightRdwr, 0, 0, 0}, + arm.AMULAL: {gc.SizeL | gc.LeftRead | gc.RegRead | RightRdwr, 0, 0, 0}, + arm.AMULA: {gc.SizeL | gc.LeftRead | gc.RegRead | RightRdwr, 0, 0, 0}, + arm.AMULU: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm.AMUL: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm.AMULL: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm.AMULLU: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm.AMVN: {gc.SizeL | gc.LeftRead | gc.RightWrite, 0, 0, 0}, + arm.AORR: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm.ARSB: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm.ARSC: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm.ASBC: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm.ASLL: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm.ASRA: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm.ASRL: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm.ASUB: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm.ATEQ: {gc.SizeL | gc.LeftRead | gc.RightRead, 0, 0, 0}, + arm.ATST: {gc.SizeL | gc.LeftRead | gc.RightRead, 0, 0, 0}, + + // Floating point. + arm.AADDD: {gc.SizeD | gc.LeftRead | RightRdwr, 0, 0, 0}, + arm.AADDF: {gc.SizeF | gc.LeftRead | RightRdwr, 0, 0, 0}, + arm.ACMPD: {gc.SizeD | gc.LeftRead | gc.RightRead, 0, 0, 0}, + arm.ACMPF: {gc.SizeF | gc.LeftRead | gc.RightRead, 0, 0, 0}, + arm.ADIVD: {gc.SizeD | gc.LeftRead | RightRdwr, 0, 0, 0}, + arm.ADIVF: {gc.SizeF | gc.LeftRead | RightRdwr, 0, 0, 0}, + arm.AMULD: {gc.SizeD | gc.LeftRead | RightRdwr, 0, 0, 0}, + arm.AMULF: {gc.SizeF | gc.LeftRead | RightRdwr, 0, 0, 0}, + arm.ASUBD: {gc.SizeD | gc.LeftRead | RightRdwr, 0, 0, 0}, + arm.ASUBF: {gc.SizeF | gc.LeftRead | RightRdwr, 0, 0, 0}, + arm.ASQRTD: {gc.SizeD | gc.LeftRead | RightRdwr, 0, 0, 0}, + + // Conversions. + arm.AMOVWD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + arm.AMOVWF: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + arm.AMOVDF: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + arm.AMOVDW: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + arm.AMOVFD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + arm.AMOVFW: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + + // Moves. + arm.AMOVB: {gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, + arm.AMOVD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, + arm.AMOVF: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, + arm.AMOVH: {gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, + arm.AMOVW: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, + + // In addtion, duffzero reads R0,R1 and writes R1. This fact is + // encoded in peep.c + obj.ADUFFZERO: {gc.Call, 0, 0, 0}, + + // In addtion, duffcopy reads R1,R2 and writes R0,R1,R2. This fact is + // encoded in peep.c + obj.ADUFFCOPY: {gc.Call, 0, 0, 0}, + + // These should be split into the two different conversions instead + // of overloading the one. + arm.AMOVBS: {gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + arm.AMOVBU: {gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + arm.AMOVHS: {gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + arm.AMOVHU: {gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + + // Jumps. + arm.AB: {gc.Jump | gc.Break, 0, 0, 0}, + arm.ABL: {gc.Call, 0, 0, 0}, + arm.ABEQ: {gc.Cjmp, 0, 0, 0}, + arm.ABNE: {gc.Cjmp, 0, 0, 0}, + arm.ABCS: {gc.Cjmp, 0, 0, 0}, + arm.ABHS: {gc.Cjmp, 0, 0, 0}, + arm.ABCC: {gc.Cjmp, 0, 0, 0}, + arm.ABLO: {gc.Cjmp, 0, 0, 0}, + arm.ABMI: {gc.Cjmp, 0, 0, 0}, + arm.ABPL: {gc.Cjmp, 0, 0, 0}, + arm.ABVS: {gc.Cjmp, 0, 0, 0}, + arm.ABVC: {gc.Cjmp, 0, 0, 0}, + arm.ABHI: {gc.Cjmp, 0, 0, 0}, + arm.ABLS: {gc.Cjmp, 0, 0, 0}, + arm.ABGE: {gc.Cjmp, 0, 0, 0}, + arm.ABLT: {gc.Cjmp, 0, 0, 0}, + arm.ABGT: {gc.Cjmp, 0, 0, 0}, + arm.ABLE: {gc.Cjmp, 0, 0, 0}, + obj.ARET: {gc.Break, 0, 0, 0}, +} + +func proginfo(p *obj.Prog) { + info := &p.Info + *info = progtable[p.As] + if info.Flags == 0 { + gc.Fatal("unknown instruction %v", p) + } + + if p.From.Type == obj.TYPE_ADDR && p.From.Sym != nil && (info.Flags&gc.LeftRead != 0) { + info.Flags &^= gc.LeftRead + info.Flags |= gc.LeftAddr + } + + if (info.Flags&gc.RegRead != 0) && p.Reg == 0 { + info.Flags &^= gc.RegRead + info.Flags |= gc.CanRegRead | gc.RightRead + } + + if (p.Scond&arm.C_SCOND != arm.C_SCOND_NONE) && (info.Flags&gc.RightWrite != 0) { + info.Flags |= gc.RightRead + } + + switch p.As { + case arm.ADIV, + arm.ADIVU, + arm.AMOD, + arm.AMODU: + info.Regset |= RtoB(arm.REG_R12) + } +} diff --git a/src/cmd/compile/internal/arm/reg.go b/src/cmd/compile/internal/arm/reg.go new file mode 100644 index 0000000000..b72ccc9815 --- /dev/null +++ b/src/cmd/compile/internal/arm/reg.go @@ -0,0 +1,136 @@ +// Inferno utils/5c/reg.c +// http://code.google.com/p/inferno-os/source/browse/utils/5c/reg.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package arm + +import "cmd/internal/obj/arm" +import "cmd/compile/internal/gc" + +const ( + NREGVAR = 32 +) + +var regname = []string{ + ".R0", + ".R1", + ".R2", + ".R3", + ".R4", + ".R5", + ".R6", + ".R7", + ".R8", + ".R9", + ".R10", + ".R11", + ".R12", + ".R13", + ".R14", + ".R15", + ".F0", + ".F1", + ".F2", + ".F3", + ".F4", + ".F5", + ".F6", + ".F7", + ".F8", + ".F9", + ".F10", + ".F11", + ".F12", + ".F13", + ".F14", + ".F15", +} + +func regnames(n *int) []string { + *n = NREGVAR + return regname +} + +func excludedregs() uint64 { + return RtoB(arm.REGSP) | RtoB(arm.REGLINK) | RtoB(arm.REGPC) +} + +func doregbits(r int) uint64 { + return 0 +} + +/* + * bit reg + * 0 R0 + * 1 R1 + * ... ... + * 10 R10 + * 12 R12 + * + * bit reg + * 18 F2 + * 19 F3 + * ... ... + * 31 F15 + */ +func RtoB(r int) uint64 { + if arm.REG_R0 <= r && r <= arm.REG_R15 { + if r >= arm.REGTMP-2 && r != arm.REG_R12 { // excluded R9 and R10 for m and g, but not R12 + return 0 + } + return 1 << uint(r-arm.REG_R0) + } + + if arm.REG_F0 <= r && r <= arm.REG_F15 { + if r < arm.REG_F2 || r > arm.REG_F0+arm.NFREG-1 { + return 0 + } + return 1 << uint((r-arm.REG_F0)+16) + } + + return 0 +} + +func BtoR(b uint64) int { + // TODO Allow R0 and R1, but be careful with a 0 return + // TODO Allow R9. Only R10 is reserved now (just g, not m). + b &= 0x11fc // excluded R9 and R10 for m and g, but not R12 + if b == 0 { + return 0 + } + return gc.Bitno(b) + arm.REG_R0 +} + +func BtoF(b uint64) int { + b &= 0xfffc0000 + if b == 0 { + return 0 + } + return gc.Bitno(b) - 16 + arm.REG_F0 +} diff --git a/src/cmd/compile/internal/arm64/cgen.go b/src/cmd/compile/internal/arm64/cgen.go new file mode 100644 index 0000000000..30326d73e2 --- /dev/null +++ b/src/cmd/compile/internal/arm64/cgen.go @@ -0,0 +1,157 @@ +// Copyright 2009 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 arm64 + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/arm64" +) + +func blockcopy(n, res *gc.Node, osrc, odst, w int64) { + // determine alignment. + // want to avoid unaligned access, so have to use + // smaller operations for less aligned types. + // for example moving [4]byte must use 4 MOVB not 1 MOVW. + align := int(n.Type.Align) + + var op int + switch align { + default: + gc.Fatal("sgen: invalid alignment %d for %v", align, n.Type) + + case 1: + op = arm64.AMOVB + + case 2: + op = arm64.AMOVH + + case 4: + op = arm64.AMOVW + + case 8: + op = arm64.AMOVD + } + + if w%int64(align) != 0 { + gc.Fatal("sgen: unaligned size %d (align=%d) for %v", w, align, n.Type) + } + c := int32(w / int64(align)) + + if osrc%int64(align) != 0 || odst%int64(align) != 0 { + gc.Fatal("sgen: unaligned offset src %d or dst %d (align %d)", osrc, odst, align) + } + + // if we are copying forward on the stack and + // the src and dst overlap, then reverse direction + dir := align + + if osrc < odst && int64(odst) < int64(osrc)+w { + dir = -dir + } + + var dst gc.Node + var src gc.Node + if n.Ullman >= res.Ullman { + gc.Agenr(n, &dst, res) // temporarily use dst + gc.Regalloc(&src, gc.Types[gc.Tptr], nil) + gins(arm64.AMOVD, &dst, &src) + if res.Op == gc.ONAME { + gc.Gvardef(res) + } + gc.Agen(res, &dst) + } else { + if res.Op == gc.ONAME { + gc.Gvardef(res) + } + gc.Agenr(res, &dst, res) + gc.Agenr(n, &src, nil) + } + + var tmp gc.Node + gc.Regalloc(&tmp, gc.Types[gc.Tptr], nil) + + // set up end marker + var nend gc.Node + + // move src and dest to the end of block if necessary + if dir < 0 { + if c >= 4 { + gc.Regalloc(&nend, gc.Types[gc.Tptr], nil) + gins(arm64.AMOVD, &src, &nend) + } + + p := gins(arm64.AADD, nil, &src) + p.From.Type = obj.TYPE_CONST + p.From.Offset = w + + p = gins(arm64.AADD, nil, &dst) + p.From.Type = obj.TYPE_CONST + p.From.Offset = w + } else { + p := gins(arm64.AADD, nil, &src) + p.From.Type = obj.TYPE_CONST + p.From.Offset = int64(-dir) + + p = gins(arm64.AADD, nil, &dst) + p.From.Type = obj.TYPE_CONST + p.From.Offset = int64(-dir) + + if c >= 4 { + gc.Regalloc(&nend, gc.Types[gc.Tptr], nil) + p := gins(arm64.AMOVD, &src, &nend) + p.From.Type = obj.TYPE_ADDR + p.From.Offset = w + } + } + + // move + // TODO: enable duffcopy for larger copies. + if c >= 4 { + p := gins(op, &src, &tmp) + p.From.Type = obj.TYPE_MEM + p.From.Offset = int64(dir) + p.Scond = arm64.C_XPRE + ploop := p + + p = gins(op, &tmp, &dst) + p.To.Type = obj.TYPE_MEM + p.To.Offset = int64(dir) + p.Scond = arm64.C_XPRE + + p = gcmp(arm64.ACMP, &src, &nend) + + gc.Patch(gc.Gbranch(arm64.ABNE, nil, 0), ploop) + gc.Regfree(&nend) + } else { + // TODO(austin): Instead of generating ADD $-8,R8; ADD + // $-8,R7; n*(MOVDU 8(R8),R9; MOVDU R9,8(R7);) just + // generate the offsets directly and eliminate the + // ADDs. That will produce shorter, more + // pipeline-able code. + var p *obj.Prog + for { + tmp14 := c + c-- + if tmp14 <= 0 { + break + } + + p = gins(op, &src, &tmp) + p.From.Type = obj.TYPE_MEM + p.From.Offset = int64(dir) + p.Scond = arm64.C_XPRE + + p = gins(op, &tmp, &dst) + p.To.Type = obj.TYPE_MEM + p.To.Offset = int64(dir) + p.Scond = arm64.C_XPRE + } + } + + gc.Regfree(&dst) + gc.Regfree(&src) + gc.Regfree(&tmp) +} diff --git a/src/cmd/compile/internal/arm64/galign.go b/src/cmd/compile/internal/arm64/galign.go new file mode 100644 index 0000000000..38def8f5a4 --- /dev/null +++ b/src/cmd/compile/internal/arm64/galign.go @@ -0,0 +1,93 @@ +// Copyright 2009 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 arm64 + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/arm64" +) + +var thechar int = '7' + +var thestring string = "arm64" + +var thelinkarch *obj.LinkArch = &arm64.Linkarm64 + +func linkarchinit() { +} + +var MAXWIDTH int64 = 1 << 50 + +/* + * go declares several platform-specific type aliases: + * int, uint, and uintptr + */ +var typedefs = []gc.Typedef{ + gc.Typedef{"int", gc.TINT, gc.TINT64}, + gc.Typedef{"uint", gc.TUINT, gc.TUINT64}, + gc.Typedef{"uintptr", gc.TUINTPTR, gc.TUINT64}, +} + +func betypeinit() { + gc.Widthptr = 8 + gc.Widthint = 8 + gc.Widthreg = 8 +} + +func Main() { + gc.Thearch.Thechar = thechar + gc.Thearch.Thestring = thestring + gc.Thearch.Thelinkarch = thelinkarch + gc.Thearch.Typedefs = typedefs + gc.Thearch.REGSP = arm64.REGSP + gc.Thearch.REGCTXT = arm64.REGCTXT + gc.Thearch.REGCALLX = arm64.REGRT1 + gc.Thearch.REGCALLX2 = arm64.REGRT2 + gc.Thearch.REGRETURN = arm64.REG_R0 + gc.Thearch.REGMIN = arm64.REG_R0 + gc.Thearch.REGMAX = arm64.REG_R31 + gc.Thearch.REGZERO = arm64.REGZERO + gc.Thearch.FREGMIN = arm64.REG_F0 + gc.Thearch.FREGMAX = arm64.REG_F31 + gc.Thearch.MAXWIDTH = MAXWIDTH + gc.Thearch.ReservedRegs = resvd + + gc.Thearch.Betypeinit = betypeinit + gc.Thearch.Cgen_hmul = cgen_hmul + gc.Thearch.Cgen_shift = cgen_shift + gc.Thearch.Clearfat = clearfat + gc.Thearch.Defframe = defframe + gc.Thearch.Dodiv = dodiv + gc.Thearch.Excise = excise + 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 + gc.Thearch.Linkarchinit = linkarchinit + gc.Thearch.Peep = peep + gc.Thearch.Proginfo = proginfo + gc.Thearch.Regtyp = regtyp + gc.Thearch.Sameaddr = sameaddr + gc.Thearch.Smallindir = smallindir + gc.Thearch.Stackaddr = stackaddr + gc.Thearch.Blockcopy = blockcopy + gc.Thearch.Sudoaddable = sudoaddable + gc.Thearch.Sudoclean = sudoclean + gc.Thearch.Excludedregs = excludedregs + gc.Thearch.RtoB = RtoB + gc.Thearch.FtoB = RtoB + gc.Thearch.BtoR = BtoR + gc.Thearch.BtoF = BtoF + gc.Thearch.Optoas = optoas + gc.Thearch.Doregbits = doregbits + gc.Thearch.Regnames = regnames + + gc.Main() + gc.Exit(0) +} diff --git a/src/cmd/compile/internal/arm64/ggen.go b/src/cmd/compile/internal/arm64/ggen.go new file mode 100644 index 0000000000..851ca4e30f --- /dev/null +++ b/src/cmd/compile/internal/arm64/ggen.go @@ -0,0 +1,532 @@ +// Copyright 2009 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 arm64 + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/arm64" + "fmt" +) + +func defframe(ptxt *obj.Prog) { + var n *gc.Node + + // fill in argument size, stack size + ptxt.To.Type = obj.TYPE_TEXTSIZE + + ptxt.To.Val = int32(gc.Rnd(gc.Curfn.Type.Argwid, int64(gc.Widthptr))) + frame := uint32(gc.Rnd(gc.Stksize+gc.Maxarg, int64(gc.Widthreg))) + ptxt.To.Offset = int64(frame) + + // insert code to zero ambiguously live variables + // so that the garbage collector only sees initialized values + // when it looks for pointers. + p := ptxt + + hi := int64(0) + lo := hi + + // 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.Name.Needzero { + continue + } + if n.Class != gc.PAUTO { + gc.Fatal("needzero class %d", n.Class) + } + if n.Type.Width%int64(gc.Widthptr) != 0 || n.Xoffset%int64(gc.Widthptr) != 0 || n.Type.Width == 0 { + gc.Fatal("var %v has size %d offset %d", gc.Nconv(n, obj.FmtLong), int(n.Type.Width), int(n.Xoffset)) + } + + if lo != hi && n.Xoffset+n.Type.Width >= lo-int64(2*gc.Widthreg) { + // merge with range we already have + lo = n.Xoffset + + continue + } + + // zero old range + p = zerorange(p, int64(frame), lo, hi) + + // set new range + hi = n.Xoffset + n.Type.Width + + lo = n.Xoffset + } + + // zero final range + zerorange(p, int64(frame), lo, hi) +} + +var darwin = obj.Getgoos() == "darwin" + +func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog { + cnt := hi - lo + if cnt == 0 { + return p + } + if cnt < int64(4*gc.Widthptr) { + for i := int64(0); i < cnt; i += int64(gc.Widthptr) { + p = appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGSP, 8+frame+lo+i) + } + } else if cnt <= int64(128*gc.Widthptr) && !darwin { // darwin ld64 cannot handle BR26 reloc with non-zero addend + p = appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGSP, 0, obj.TYPE_REG, arm64.REGRT1, 0) + p = appendpp(p, arm64.AADD, obj.TYPE_CONST, 0, 8+frame+lo-8, obj.TYPE_REG, arm64.REGRT1, 0) + p.Reg = arm64.REGRT1 + p = appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0) + f := gc.Sysfunc("duffzero") + gc.Naddr(&p.To, f) + gc.Afunclit(&p.To, f) + p.To.Offset = 4 * (128 - cnt/int64(gc.Widthptr)) + } else { + p = appendpp(p, arm64.AMOVD, obj.TYPE_CONST, 0, 8+frame+lo-8, obj.TYPE_REG, arm64.REGTMP, 0) + p = appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGSP, 0, obj.TYPE_REG, arm64.REGRT1, 0) + p = appendpp(p, arm64.AADD, obj.TYPE_REG, arm64.REGTMP, 0, obj.TYPE_REG, arm64.REGRT1, 0) + p.Reg = arm64.REGRT1 + p = appendpp(p, arm64.AMOVD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, arm64.REGTMP, 0) + p = appendpp(p, arm64.AADD, obj.TYPE_REG, arm64.REGTMP, 0, obj.TYPE_REG, arm64.REGRT2, 0) + p.Reg = arm64.REGRT1 + p = appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGRT1, int64(gc.Widthptr)) + p.Scond = arm64.C_XPRE + p1 := p + p = appendpp(p, arm64.ACMP, obj.TYPE_REG, arm64.REGRT1, 0, obj.TYPE_NONE, 0, 0) + p.Reg = arm64.REGRT2 + p = appendpp(p, arm64.ABNE, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0) + gc.Patch(p, p1) + } + + return p +} + +func appendpp(p *obj.Prog, as int, ftype int, freg int, foffset int64, ttype int, treg int, toffset int64) *obj.Prog { + q := gc.Ctxt.NewProg() + gc.Clearp(q) + q.As = int16(as) + q.Lineno = p.Lineno + q.From.Type = int16(ftype) + q.From.Reg = int16(freg) + q.From.Offset = foffset + q.To.Type = int16(ttype) + q.To.Reg = int16(treg) + q.To.Offset = toffset + q.Link = p.Link + p.Link = q + return q +} + +func ginsnop() { + var con gc.Node + gc.Nodconst(&con, gc.Types[gc.TINT], 0) + gins(arm64.AHINT, &con, nil) +} + +var panicdiv *gc.Node + +/* + * generate division. + * generates one of: + * res = nl / nr + * res = nl % nr + * according to op. + */ +func dodiv(op int, nl *gc.Node, nr *gc.Node, res *gc.Node) { + // Have to be careful about handling + // most negative int divided by -1 correctly. + // The hardware will generate undefined result. + // Also need to explicitly trap on division on zero, + // the hardware will silently generate undefined result. + // DIVW will leave unpredicable result in higher 32-bit, + // so always use DIVD/DIVDU. + t := nl.Type + + t0 := t + check := 0 + if gc.Issigned[t.Etype] { + check = 1 + if gc.Isconst(nl, gc.CTINT) && nl.Int() != -(1<= nr.Ullman { + gc.Cgen(nl, &tl) + gc.Cgen(nr, &tr) + } else { + gc.Cgen(nr, &tr) + gc.Cgen(nl, &tl) + } + + if t != t0 { + // Convert + tl2 := tl + + tr2 := tr + tl.Type = t + tr.Type = t + gmove(&tl2, &tl) + gmove(&tr2, &tr) + } + + // Handle divide-by-zero panic. + p1 := gins(optoas(gc.OCMP, t), &tr, nil) + p1.Reg = arm64.REGZERO + p1 = gc.Gbranch(optoas(gc.ONE, t), nil, +1) + if panicdiv == nil { + panicdiv = gc.Sysfunc("panicdivide") + } + gc.Ginscall(panicdiv, -1) + gc.Patch(p1, gc.Pc) + + var p2 *obj.Prog + if check != 0 { + var nm1 gc.Node + gc.Nodconst(&nm1, t, -1) + gcmp(optoas(gc.OCMP, t), &tr, &nm1) + p1 := gc.Gbranch(optoas(gc.ONE, t), nil, +1) + if op == gc.ODIV { + // a / (-1) is -a. + gins(optoas(gc.OMINUS, t), &tl, &tl) + + gmove(&tl, res) + } else { + // a % (-1) is 0. + var nz gc.Node + gc.Nodconst(&nz, t, 0) + + gmove(&nz, res) + } + + p2 = gc.Gbranch(obj.AJMP, nil, 0) + gc.Patch(p1, gc.Pc) + } + + p1 = gins(a, &tr, &tl) + if op == gc.ODIV { + gc.Regfree(&tr) + gmove(&tl, res) + } else { + // A%B = A-(A/B*B) + var tm gc.Node + gc.Regalloc(&tm, t, nil) + + // patch div to use the 3 register form + // TODO(minux): add gins3? + p1.Reg = p1.To.Reg + + p1.To.Reg = tm.Reg + gins(optoas(gc.OMUL, t), &tr, &tm) + gc.Regfree(&tr) + gins(optoas(gc.OSUB, t), &tm, &tl) + gc.Regfree(&tm) + gmove(&tl, res) + } + + gc.Regfree(&tl) + if check != 0 { + gc.Patch(p2, gc.Pc) + } +} + +/* + * generate high multiply: + * res = (nl*nr) >> width + */ +func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) { + // largest ullman on left. + if nl.Ullman < nr.Ullman { + tmp := (*gc.Node)(nl) + nl = nr + nr = tmp + } + + t := (*gc.Type)(nl.Type) + w := int(int(t.Width * 8)) + var n1 gc.Node + gc.Cgenr(nl, &n1, res) + var n2 gc.Node + gc.Cgenr(nr, &n2, nil) + switch gc.Simtype[t.Etype] { + case gc.TINT8, + gc.TINT16, + gc.TINT32: + gins(optoas(gc.OMUL, t), &n2, &n1) + p := (*obj.Prog)(gins(arm64.AASR, nil, &n1)) + p.From.Type = obj.TYPE_CONST + p.From.Offset = int64(w) + + case gc.TUINT8, + gc.TUINT16, + gc.TUINT32: + gins(optoas(gc.OMUL, t), &n2, &n1) + p := (*obj.Prog)(gins(arm64.ALSR, nil, &n1)) + p.From.Type = obj.TYPE_CONST + p.From.Offset = int64(w) + + case gc.TINT64, + gc.TUINT64: + if gc.Issigned[t.Etype] { + gins(arm64.ASMULH, &n2, &n1) + } else { + gins(arm64.AUMULH, &n2, &n1) + } + + default: + gc.Fatal("cgen_hmul %v", t) + } + + gc.Cgen(&n1, res) + gc.Regfree(&n1) + gc.Regfree(&n2) +} + +/* + * generate shift according to op, one of: + * res = nl << nr + * res = nl >> nr + */ +func cgen_shift(op int, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) { + a := int(optoas(op, nl.Type)) + + if nr.Op == gc.OLITERAL { + var n1 gc.Node + gc.Regalloc(&n1, nl.Type, res) + gc.Cgen(nl, &n1) + sc := uint64(nr.Int()) + if sc >= uint64(nl.Type.Width*8) { + // large shift gets 2 shifts by width-1 + var n3 gc.Node + gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1) + + gins(a, &n3, &n1) + gins(a, &n3, &n1) + } else { + gins(a, nr, &n1) + } + gmove(&n1, res) + gc.Regfree(&n1) + return + } + + if nl.Ullman >= gc.UINF { + var n4 gc.Node + gc.Tempname(&n4, nl.Type) + gc.Cgen(nl, &n4) + nl = &n4 + } + + if nr.Ullman >= gc.UINF { + var n5 gc.Node + gc.Tempname(&n5, nr.Type) + gc.Cgen(nr, &n5) + nr = &n5 + } + + // Allow either uint32 or uint64 as shift type, + // to avoid unnecessary conversion from uint32 to uint64 + // just to do the comparison. + tcount := gc.Types[gc.Simtype[nr.Type.Etype]] + + if tcount.Etype < gc.TUINT32 { + tcount = gc.Types[gc.TUINT32] + } + + var n1 gc.Node + gc.Regalloc(&n1, nr.Type, nil) // to hold the shift type in CX + var n3 gc.Node + gc.Regalloc(&n3, tcount, &n1) // to clear high bits of CX + + var n2 gc.Node + gc.Regalloc(&n2, nl.Type, res) + + if nl.Ullman >= nr.Ullman { + gc.Cgen(nl, &n2) + gc.Cgen(nr, &n1) + gmove(&n1, &n3) + } else { + gc.Cgen(nr, &n1) + gmove(&n1, &n3) + gc.Cgen(nl, &n2) + } + + gc.Regfree(&n3) + + // test and fix up large shifts + if !bounded { + gc.Nodconst(&n3, tcount, nl.Type.Width*8) + gcmp(optoas(gc.OCMP, tcount), &n1, &n3) + p1 := (*obj.Prog)(gc.Gbranch(optoas(gc.OLT, tcount), nil, +1)) + if op == gc.ORSH && gc.Issigned[nl.Type.Etype] { + gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1) + gins(a, &n3, &n2) + } else { + gc.Nodconst(&n3, nl.Type, 0) + gmove(&n3, &n2) + } + + gc.Patch(p1, gc.Pc) + } + + gins(a, &n1, &n2) + + gmove(&n2, res) + + gc.Regfree(&n1) + gc.Regfree(&n2) +} + +func clearfat(nl *gc.Node) { + /* clear a fat object */ + if gc.Debug['g'] != 0 { + fmt.Printf("clearfat %v (%v, size: %d)\n", nl, nl.Type, nl.Type.Width) + } + + w := uint64(uint64(nl.Type.Width)) + + // Avoid taking the address for simple enough types. + if gc.Componentgen(nil, nl) { + return + } + + c := uint64(w % 8) // bytes + q := uint64(w / 8) // dwords + + if reg[arm64.REGRT1-arm64.REG_R0] > 0 { + gc.Fatal("R%d in use during clearfat", arm64.REGRT1-arm64.REG_R0) + } + + var r0 gc.Node + gc.Nodreg(&r0, gc.Types[gc.TUINT64], arm64.REGZERO) + var dst gc.Node + gc.Nodreg(&dst, gc.Types[gc.Tptr], arm64.REGRT1) + reg[arm64.REGRT1-arm64.REG_R0]++ + gc.Agen(nl, &dst) + + var boff uint64 + if q > 128 { + p := gins(arm64.ASUB, nil, &dst) + p.From.Type = obj.TYPE_CONST + p.From.Offset = 8 + + var end gc.Node + gc.Regalloc(&end, gc.Types[gc.Tptr], nil) + p = gins(arm64.AMOVD, &dst, &end) + p.From.Type = obj.TYPE_ADDR + p.From.Offset = int64(q * 8) + + p = gins(arm64.AMOVD, &r0, &dst) + p.To.Type = obj.TYPE_MEM + p.To.Offset = 8 + p.Scond = arm64.C_XPRE + pl := (*obj.Prog)(p) + + p = gcmp(arm64.ACMP, &dst, &end) + gc.Patch(gc.Gbranch(arm64.ABNE, nil, 0), pl) + + gc.Regfree(&end) + + // The loop leaves R16 on the last zeroed dword + boff = 8 + } else if q >= 4 && !darwin { // darwin ld64 cannot handle BR26 reloc with non-zero addend + p := gins(arm64.ASUB, nil, &dst) + p.From.Type = obj.TYPE_CONST + p.From.Offset = 8 + f := (*gc.Node)(gc.Sysfunc("duffzero")) + p = gins(obj.ADUFFZERO, nil, f) + gc.Afunclit(&p.To, f) + + // 4 and 128 = magic constants: see ../../runtime/asm_arm64x.s + p.To.Offset = int64(4 * (128 - q)) + + // duffzero leaves R16 on the last zeroed dword + boff = 8 + } else { + var p *obj.Prog + for t := uint64(0); t < q; t++ { + p = gins(arm64.AMOVD, &r0, &dst) + p.To.Type = obj.TYPE_MEM + p.To.Offset = int64(8 * t) + } + + boff = 8 * q + } + + var p *obj.Prog + for t := uint64(0); t < c; t++ { + p = gins(arm64.AMOVB, &r0, &dst) + p.To.Type = obj.TYPE_MEM + p.To.Offset = int64(t + boff) + } + + reg[arm64.REGRT1-arm64.REG_R0]-- +} + +// Called after regopt and peep have run. +// Expand CHECKNIL pseudo-op into actual nil pointer check. +func expandchecks(firstp *obj.Prog) { + var p1 *obj.Prog + + for p := (*obj.Prog)(firstp); p != nil; p = p.Link { + if gc.Debug_checknil != 0 && gc.Ctxt.Debugvlog != 0 { + fmt.Printf("expandchecks: %v\n", p) + } + if p.As != obj.ACHECKNIL { + continue + } + if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers + gc.Warnl(int(p.Lineno), "generated nil check") + } + if p.From.Type != obj.TYPE_REG { + gc.Fatal("invalid nil check %v\n", p) + } + + // check is + // CBNZ arg, 2(PC) + // MOVD ZR, 0(arg) + p1 = gc.Ctxt.NewProg() + gc.Clearp(p1) + p1.Link = p.Link + p.Link = p1 + p1.Lineno = p.Lineno + p1.Pc = 9999 + + p.As = arm64.ACBNZ + p.To.Type = obj.TYPE_BRANCH + p.To.Val = p1.Link + + // crash by write to memory address 0. + p1.As = arm64.AMOVD + p1.From.Type = obj.TYPE_REG + p1.From.Reg = arm64.REGZERO + p1.To.Type = obj.TYPE_MEM + p1.To.Reg = p.From.Reg + p1.To.Offset = 0 + } +} + +// res = runtime.getg() +func getg(res *gc.Node) { + var n1 gc.Node + gc.Nodreg(&n1, res.Type, arm64.REGG) + gmove(&n1, res) +} diff --git a/src/cmd/compile/internal/arm64/gsubr.go b/src/cmd/compile/internal/arm64/gsubr.go new file mode 100644 index 0000000000..0a14654d83 --- /dev/null +++ b/src/cmd/compile/internal/arm64/gsubr.go @@ -0,0 +1,983 @@ +// Derived from Inferno utils/6c/txt.c +// http://code.google.com/p/inferno-os/source/browse/utils/6c/txt.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package arm64 + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/arm64" + "fmt" +) + +var resvd = []int{ + arm64.REGTMP, + arm64.REGG, + arm64.REGRT1, + arm64.REGRT2, + arm64.REG_R31, // REGZERO and REGSP + arm64.FREGZERO, + arm64.FREGHALF, + arm64.FREGONE, + arm64.FREGTWO, +} + +/* + * generate + * as $c, n + */ +func ginscon(as int, c int64, n2 *gc.Node) { + var n1 gc.Node + + gc.Nodconst(&n1, gc.Types[gc.TINT64], c) + + if as != arm64.AMOVD && (c < -arm64.BIG || c > arm64.BIG) || as == arm64.AMUL || n2 != nil && n2.Op != gc.OREGISTER { + // cannot have more than 16-bit of immediate in ADD, etc. + // instead, MOV into register first. + var ntmp gc.Node + gc.Regalloc(&ntmp, gc.Types[gc.TINT64], nil) + + gins(arm64.AMOVD, &n1, &ntmp) + gins(as, &ntmp, n2) + gc.Regfree(&ntmp) + return + } + + rawgins(as, &n1, n2) +} + +/* + * generate + * as n, $c (CMP) + */ +func ginscon2(as int, n2 *gc.Node, c int64) { + var n1 gc.Node + + gc.Nodconst(&n1, gc.Types[gc.TINT64], c) + + switch as { + default: + gc.Fatal("ginscon2") + + case arm64.ACMP: + if -arm64.BIG <= c && c <= arm64.BIG { + gcmp(as, n2, &n1) + return + } + } + + // MOV n1 into register first + var ntmp gc.Node + gc.Regalloc(&ntmp, gc.Types[gc.TINT64], nil) + + rawgins(arm64.AMOVD, &n1, &ntmp) + gcmp(as, n2, &ntmp) + 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, n2.Int()) + } 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 + * hard part is conversions. + */ +func gmove(f *gc.Node, t *gc.Node) { + if gc.Debug['M'] != 0 { + fmt.Printf("gmove %v -> %v\n", gc.Nconv(f, obj.FmtLong), gc.Nconv(t, obj.FmtLong)) + } + + ft := int(gc.Simsimtype(f.Type)) + tt := int(gc.Simsimtype(t.Type)) + cvt := (*gc.Type)(t.Type) + + if gc.Iscomplex[ft] || gc.Iscomplex[tt] { + gc.Complexmove(f, t) + return + } + + // cannot have two memory operands + var r1 gc.Node + var a int + if gc.Ismem(f) && gc.Ismem(t) { + goto hard + } + + // convert constant to desired type + if f.Op == gc.OLITERAL { + var con gc.Node + switch tt { + default: + f.Convconst(&con, t.Type) + + case gc.TINT32, + gc.TINT16, + gc.TINT8: + var con gc.Node + f.Convconst(&con, gc.Types[gc.TINT64]) + var r1 gc.Node + gc.Regalloc(&r1, con.Type, t) + gins(arm64.AMOVD, &con, &r1) + gmove(&r1, t) + gc.Regfree(&r1) + return + + case gc.TUINT32, + gc.TUINT16, + gc.TUINT8: + var con gc.Node + f.Convconst(&con, gc.Types[gc.TUINT64]) + var r1 gc.Node + gc.Regalloc(&r1, con.Type, t) + gins(arm64.AMOVD, &con, &r1) + gmove(&r1, t) + gc.Regfree(&r1) + return + } + + f = &con + ft = tt // so big switch will choose a simple mov + + // constants can't move directly to memory. + if gc.Ismem(t) { + goto hard + } + } + + // value -> value copy, first operand in memory. + // any floating point operand requires register + // src, so goto hard to copy to register first. + if gc.Ismem(f) && ft != tt && (gc.Isfloat[ft] || gc.Isfloat[tt]) { + cvt = gc.Types[ft] + goto hard + } + + // value -> value copy, only one memory operand. + // figure out the instruction to use. + // break out of switch for one-instruction gins. + // goto rdst for "destination must be register". + // goto hard for "convert to cvt type first". + // otherwise handle and return. + + switch uint32(ft)<<16 | uint32(tt) { + default: + gc.Fatal("gmove %v -> %v", gc.Tconv(f.Type, obj.FmtLong), gc.Tconv(t.Type, obj.FmtLong)) + + /* + * integer copy and truncate + */ + case gc.TINT8<<16 | gc.TINT8, // same size + gc.TUINT8<<16 | gc.TINT8, + gc.TINT16<<16 | gc.TINT8, + // truncate + gc.TUINT16<<16 | gc.TINT8, + gc.TINT32<<16 | gc.TINT8, + gc.TUINT32<<16 | gc.TINT8, + gc.TINT64<<16 | gc.TINT8, + gc.TUINT64<<16 | gc.TINT8: + a = arm64.AMOVB + + case gc.TINT8<<16 | gc.TUINT8, // same size + gc.TUINT8<<16 | gc.TUINT8, + gc.TINT16<<16 | gc.TUINT8, + // truncate + gc.TUINT16<<16 | gc.TUINT8, + gc.TINT32<<16 | gc.TUINT8, + gc.TUINT32<<16 | gc.TUINT8, + gc.TINT64<<16 | gc.TUINT8, + gc.TUINT64<<16 | gc.TUINT8: + a = arm64.AMOVBU + + case gc.TINT16<<16 | gc.TINT16, // same size + gc.TUINT16<<16 | gc.TINT16, + gc.TINT32<<16 | gc.TINT16, + // truncate + gc.TUINT32<<16 | gc.TINT16, + gc.TINT64<<16 | gc.TINT16, + gc.TUINT64<<16 | gc.TINT16: + a = arm64.AMOVH + + case gc.TINT16<<16 | gc.TUINT16, // same size + gc.TUINT16<<16 | gc.TUINT16, + gc.TINT32<<16 | gc.TUINT16, + // truncate + gc.TUINT32<<16 | gc.TUINT16, + gc.TINT64<<16 | gc.TUINT16, + gc.TUINT64<<16 | gc.TUINT16: + a = arm64.AMOVHU + + case gc.TINT32<<16 | gc.TINT32, // same size + gc.TUINT32<<16 | gc.TINT32, + gc.TINT64<<16 | gc.TINT32, + // truncate + gc.TUINT64<<16 | gc.TINT32: + a = arm64.AMOVW + + case gc.TINT32<<16 | gc.TUINT32, // same size + gc.TUINT32<<16 | gc.TUINT32, + gc.TINT64<<16 | gc.TUINT32, + gc.TUINT64<<16 | gc.TUINT32: + a = arm64.AMOVWU + + case gc.TINT64<<16 | gc.TINT64, // same size + gc.TINT64<<16 | gc.TUINT64, + gc.TUINT64<<16 | gc.TINT64, + gc.TUINT64<<16 | gc.TUINT64: + a = arm64.AMOVD + + /* + * integer up-conversions + */ + case gc.TINT8<<16 | gc.TINT16, // sign extend int8 + gc.TINT8<<16 | gc.TUINT16, + gc.TINT8<<16 | gc.TINT32, + gc.TINT8<<16 | gc.TUINT32, + gc.TINT8<<16 | gc.TINT64, + gc.TINT8<<16 | gc.TUINT64: + a = arm64.AMOVB + + goto rdst + + case gc.TUINT8<<16 | gc.TINT16, // zero extend uint8 + gc.TUINT8<<16 | gc.TUINT16, + gc.TUINT8<<16 | gc.TINT32, + gc.TUINT8<<16 | gc.TUINT32, + gc.TUINT8<<16 | gc.TINT64, + gc.TUINT8<<16 | gc.TUINT64: + a = arm64.AMOVBU + + goto rdst + + case gc.TINT16<<16 | gc.TINT32, // sign extend int16 + gc.TINT16<<16 | gc.TUINT32, + gc.TINT16<<16 | gc.TINT64, + gc.TINT16<<16 | gc.TUINT64: + a = arm64.AMOVH + + goto rdst + + case gc.TUINT16<<16 | gc.TINT32, // zero extend uint16 + gc.TUINT16<<16 | gc.TUINT32, + gc.TUINT16<<16 | gc.TINT64, + gc.TUINT16<<16 | gc.TUINT64: + a = arm64.AMOVHU + + goto rdst + + case gc.TINT32<<16 | gc.TINT64, // sign extend int32 + gc.TINT32<<16 | gc.TUINT64: + a = arm64.AMOVW + + goto rdst + + case gc.TUINT32<<16 | gc.TINT64, // zero extend uint32 + gc.TUINT32<<16 | gc.TUINT64: + a = arm64.AMOVWU + + goto rdst + + /* + * float to integer + */ + case gc.TFLOAT32<<16 | gc.TINT32: + a = arm64.AFCVTZSSW + goto rdst + + case gc.TFLOAT64<<16 | gc.TINT32: + a = arm64.AFCVTZSDW + goto rdst + + case gc.TFLOAT32<<16 | gc.TINT64: + a = arm64.AFCVTZSS + goto rdst + + case gc.TFLOAT64<<16 | gc.TINT64: + a = arm64.AFCVTZSD + goto rdst + + case gc.TFLOAT32<<16 | gc.TUINT32: + a = arm64.AFCVTZUSW + goto rdst + + case gc.TFLOAT64<<16 | gc.TUINT32: + a = arm64.AFCVTZUDW + goto rdst + + case gc.TFLOAT32<<16 | gc.TUINT64: + a = arm64.AFCVTZUS + goto rdst + + case gc.TFLOAT64<<16 | gc.TUINT64: + a = arm64.AFCVTZUD + goto rdst + + case gc.TFLOAT32<<16 | gc.TINT16, + gc.TFLOAT32<<16 | gc.TINT8, + gc.TFLOAT64<<16 | gc.TINT16, + gc.TFLOAT64<<16 | gc.TINT8: + cvt = gc.Types[gc.TINT32] + + goto hard + + case gc.TFLOAT32<<16 | gc.TUINT16, + gc.TFLOAT32<<16 | gc.TUINT8, + gc.TFLOAT64<<16 | gc.TUINT16, + gc.TFLOAT64<<16 | gc.TUINT8: + cvt = gc.Types[gc.TUINT32] + + goto hard + + /* + * integer to float + */ + case gc.TINT8<<16 | gc.TFLOAT32, + gc.TINT16<<16 | gc.TFLOAT32, + gc.TINT32<<16 | gc.TFLOAT32: + a = arm64.ASCVTFWS + + goto rdst + + case gc.TINT8<<16 | gc.TFLOAT64, + gc.TINT16<<16 | gc.TFLOAT64, + gc.TINT32<<16 | gc.TFLOAT64: + a = arm64.ASCVTFWD + + goto rdst + + case gc.TINT64<<16 | gc.TFLOAT32: + a = arm64.ASCVTFS + goto rdst + + case gc.TINT64<<16 | gc.TFLOAT64: + a = arm64.ASCVTFD + goto rdst + + case gc.TUINT8<<16 | gc.TFLOAT32, + gc.TUINT16<<16 | gc.TFLOAT32, + gc.TUINT32<<16 | gc.TFLOAT32: + a = arm64.AUCVTFWS + + goto rdst + + case gc.TUINT8<<16 | gc.TFLOAT64, + gc.TUINT16<<16 | gc.TFLOAT64, + gc.TUINT32<<16 | gc.TFLOAT64: + a = arm64.AUCVTFWD + + goto rdst + + case gc.TUINT64<<16 | gc.TFLOAT32: + a = arm64.AUCVTFS + goto rdst + + case gc.TUINT64<<16 | gc.TFLOAT64: + a = arm64.AUCVTFD + goto rdst + + /* + * float to float + */ + case gc.TFLOAT32<<16 | gc.TFLOAT32: + a = arm64.AFMOVS + + case gc.TFLOAT64<<16 | gc.TFLOAT64: + a = arm64.AFMOVD + + case gc.TFLOAT32<<16 | gc.TFLOAT64: + a = arm64.AFCVTSD + goto rdst + + case gc.TFLOAT64<<16 | gc.TFLOAT32: + a = arm64.AFCVTDS + goto rdst + } + + gins(a, f, t) + return + + // requires register destination +rdst: + gc.Regalloc(&r1, t.Type, t) + + gins(a, f, &r1) + gmove(&r1, t) + gc.Regfree(&r1) + return + + // requires register intermediate +hard: + gc.Regalloc(&r1, cvt, t) + + gmove(f, &r1) + gmove(&r1, t) + gc.Regfree(&r1) + return +} + +func intLiteral(n *gc.Node) (x int64, ok bool) { + switch { + case n == nil: + return + case gc.Isconst(n, gc.CTINT): + return n.Int(), true + case gc.Isconst(n, gc.CTBOOL): + return int64(obj.Bool2int(n.Bool())), true + } + return +} + +// gins is called by the front end. +// It synthesizes some multiple-instruction sequences +// so the front end can stay simpler. +func gins(as int, f, t *gc.Node) *obj.Prog { + if as >= obj.A_ARCHSPECIFIC { + if x, ok := intLiteral(f); ok { + ginscon(as, x, t) + return nil // caller must not use + } + } + if as == arm64.ACMP { + if x, ok := intLiteral(t); ok { + ginscon2(as, f, x) + return nil // caller must not use + } + } + return rawgins(as, f, t) +} + +/* + * generate one instruction: + * as f, t + */ +func rawgins(as int, f *gc.Node, t *gc.Node) *obj.Prog { + // TODO(austin): Add self-move test like in 6g (but be careful + // of truncation moves) + + p := gc.Prog(as) + gc.Naddr(&p.From, f) + gc.Naddr(&p.To, t) + + switch as { + case arm64.ACMP, arm64.AFCMPS, arm64.AFCMPD: + if t != nil { + if f.Op != gc.OREGISTER { + gc.Fatal("bad operands to gcmp") + } + p.From = p.To + p.To = obj.Addr{} + raddr(f, p) + } + } + + // Bad things the front end has done to us. Crash to find call stack. + switch as { + case arm64.AAND, arm64.AMUL: + if p.From.Type == obj.TYPE_CONST { + gc.Debug['h'] = 1 + gc.Fatal("bad inst: %v", p) + } + case arm64.ACMP: + if p.From.Type == obj.TYPE_MEM || p.To.Type == obj.TYPE_MEM { + gc.Debug['h'] = 1 + gc.Fatal("bad inst: %v", p) + } + } + + if gc.Debug['g'] != 0 { + fmt.Printf("%v\n", p) + } + + w := int32(0) + switch as { + case arm64.AMOVB, + arm64.AMOVBU: + w = 1 + + case arm64.AMOVH, + arm64.AMOVHU: + w = 2 + + case arm64.AMOVW, + arm64.AMOVWU: + w = 4 + + case arm64.AMOVD: + if p.From.Type == obj.TYPE_CONST || p.From.Type == obj.TYPE_ADDR { + break + } + w = 8 + } + + if w != 0 && ((f != nil && p.From.Width < int64(w)) || (t != nil && p.To.Type != obj.TYPE_REG && p.To.Width > int64(w))) { + gc.Dump("f", f) + gc.Dump("t", t) + gc.Fatal("bad width: %v (%d, %d)\n", p, p.From.Width, p.To.Width) + } + + return p +} + +/* + * insert n into reg slot of p + */ +func raddr(n *gc.Node, p *obj.Prog) { + var a obj.Addr + + gc.Naddr(&a, n) + if a.Type != obj.TYPE_REG { + if n != nil { + gc.Fatal("bad in raddr: %v", gc.Oconv(int(n.Op), 0)) + } else { + gc.Fatal("bad in raddr: ") + } + p.Reg = 0 + } else { + p.Reg = a.Reg + } +} + +func gcmp(as int, lhs *gc.Node, rhs *gc.Node) *obj.Prog { + if lhs.Op != gc.OREGISTER { + gc.Fatal("bad operands to gcmp: %v %v", gc.Oconv(int(lhs.Op), 0), gc.Oconv(int(rhs.Op), 0)) + } + + p := rawgins(as, rhs, nil) + raddr(lhs, p) + return p +} + +/* + * return Axxx for Oxxx on type t. + */ +func optoas(op int, t *gc.Type) int { + if t == nil { + gc.Fatal("optoas: t is nil") + } + + a := int(obj.AXXX) + switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) { + default: + gc.Fatal("optoas: no entry for op=%v type=%v", gc.Oconv(int(op), 0), t) + + case gc.OEQ<<16 | gc.TBOOL, + gc.OEQ<<16 | gc.TINT8, + gc.OEQ<<16 | gc.TUINT8, + gc.OEQ<<16 | gc.TINT16, + gc.OEQ<<16 | gc.TUINT16, + gc.OEQ<<16 | gc.TINT32, + gc.OEQ<<16 | gc.TUINT32, + gc.OEQ<<16 | gc.TINT64, + gc.OEQ<<16 | gc.TUINT64, + gc.OEQ<<16 | gc.TPTR32, + gc.OEQ<<16 | gc.TPTR64, + gc.OEQ<<16 | gc.TFLOAT32, + gc.OEQ<<16 | gc.TFLOAT64: + a = arm64.ABEQ + + case gc.ONE<<16 | gc.TBOOL, + gc.ONE<<16 | gc.TINT8, + gc.ONE<<16 | gc.TUINT8, + gc.ONE<<16 | gc.TINT16, + gc.ONE<<16 | gc.TUINT16, + gc.ONE<<16 | gc.TINT32, + gc.ONE<<16 | gc.TUINT32, + gc.ONE<<16 | gc.TINT64, + gc.ONE<<16 | gc.TUINT64, + gc.ONE<<16 | gc.TPTR32, + gc.ONE<<16 | gc.TPTR64, + gc.ONE<<16 | gc.TFLOAT32, + gc.ONE<<16 | gc.TFLOAT64: + a = arm64.ABNE + + case gc.OLT<<16 | gc.TINT8, + gc.OLT<<16 | gc.TINT16, + gc.OLT<<16 | gc.TINT32, + gc.OLT<<16 | gc.TINT64: + a = arm64.ABLT + + case gc.OLT<<16 | gc.TUINT8, + gc.OLT<<16 | gc.TUINT16, + gc.OLT<<16 | gc.TUINT32, + gc.OLT<<16 | gc.TUINT64, + gc.OLT<<16 | gc.TFLOAT32, + gc.OLT<<16 | gc.TFLOAT64: + a = arm64.ABLO + + case gc.OLE<<16 | gc.TINT8, + gc.OLE<<16 | gc.TINT16, + gc.OLE<<16 | gc.TINT32, + gc.OLE<<16 | gc.TINT64: + a = arm64.ABLE + + case gc.OLE<<16 | gc.TUINT8, + gc.OLE<<16 | gc.TUINT16, + gc.OLE<<16 | gc.TUINT32, + gc.OLE<<16 | gc.TUINT64, + gc.OLE<<16 | gc.TFLOAT32, + gc.OLE<<16 | gc.TFLOAT64: + a = arm64.ABLS + + case gc.OGT<<16 | gc.TINT8, + gc.OGT<<16 | gc.TINT16, + gc.OGT<<16 | gc.TINT32, + gc.OGT<<16 | gc.TINT64, + gc.OGT<<16 | gc.TFLOAT32, + gc.OGT<<16 | gc.TFLOAT64: + a = arm64.ABGT + + case gc.OGT<<16 | gc.TUINT8, + gc.OGT<<16 | gc.TUINT16, + gc.OGT<<16 | gc.TUINT32, + gc.OGT<<16 | gc.TUINT64: + a = arm64.ABHI + + case gc.OGE<<16 | gc.TINT8, + gc.OGE<<16 | gc.TINT16, + gc.OGE<<16 | gc.TINT32, + gc.OGE<<16 | gc.TINT64, + gc.OGE<<16 | gc.TFLOAT32, + gc.OGE<<16 | gc.TFLOAT64: + a = arm64.ABGE + + case gc.OGE<<16 | gc.TUINT8, + gc.OGE<<16 | gc.TUINT16, + gc.OGE<<16 | gc.TUINT32, + gc.OGE<<16 | gc.TUINT64: + a = arm64.ABHS + + case gc.OCMP<<16 | gc.TBOOL, + gc.OCMP<<16 | gc.TINT8, + gc.OCMP<<16 | gc.TINT16, + gc.OCMP<<16 | gc.TINT32, + gc.OCMP<<16 | gc.TPTR32, + gc.OCMP<<16 | gc.TINT64, + gc.OCMP<<16 | gc.TUINT8, + gc.OCMP<<16 | gc.TUINT16, + gc.OCMP<<16 | gc.TUINT32, + gc.OCMP<<16 | gc.TUINT64, + gc.OCMP<<16 | gc.TPTR64: + a = arm64.ACMP + + case gc.OCMP<<16 | gc.TFLOAT32: + a = arm64.AFCMPS + + case gc.OCMP<<16 | gc.TFLOAT64: + a = arm64.AFCMPD + + case gc.OAS<<16 | gc.TBOOL, + gc.OAS<<16 | gc.TINT8: + a = arm64.AMOVB + + case gc.OAS<<16 | gc.TUINT8: + a = arm64.AMOVBU + + case gc.OAS<<16 | gc.TINT16: + a = arm64.AMOVH + + case gc.OAS<<16 | gc.TUINT16: + a = arm64.AMOVHU + + case gc.OAS<<16 | gc.TINT32: + a = arm64.AMOVW + + case gc.OAS<<16 | gc.TUINT32, + gc.OAS<<16 | gc.TPTR32: + a = arm64.AMOVWU + + case gc.OAS<<16 | gc.TINT64, + gc.OAS<<16 | gc.TUINT64, + gc.OAS<<16 | gc.TPTR64: + a = arm64.AMOVD + + case gc.OAS<<16 | gc.TFLOAT32: + a = arm64.AFMOVS + + case gc.OAS<<16 | gc.TFLOAT64: + a = arm64.AFMOVD + + case gc.OADD<<16 | gc.TINT8, + gc.OADD<<16 | gc.TUINT8, + gc.OADD<<16 | gc.TINT16, + gc.OADD<<16 | gc.TUINT16, + gc.OADD<<16 | gc.TINT32, + gc.OADD<<16 | gc.TUINT32, + gc.OADD<<16 | gc.TPTR32, + gc.OADD<<16 | gc.TINT64, + gc.OADD<<16 | gc.TUINT64, + gc.OADD<<16 | gc.TPTR64: + a = arm64.AADD + + case gc.OADD<<16 | gc.TFLOAT32: + a = arm64.AFADDS + + case gc.OADD<<16 | gc.TFLOAT64: + a = arm64.AFADDD + + case gc.OSUB<<16 | gc.TINT8, + gc.OSUB<<16 | gc.TUINT8, + gc.OSUB<<16 | gc.TINT16, + gc.OSUB<<16 | gc.TUINT16, + gc.OSUB<<16 | gc.TINT32, + gc.OSUB<<16 | gc.TUINT32, + gc.OSUB<<16 | gc.TPTR32, + gc.OSUB<<16 | gc.TINT64, + gc.OSUB<<16 | gc.TUINT64, + gc.OSUB<<16 | gc.TPTR64: + a = arm64.ASUB + + case gc.OSUB<<16 | gc.TFLOAT32: + a = arm64.AFSUBS + + case gc.OSUB<<16 | gc.TFLOAT64: + a = arm64.AFSUBD + + case gc.OMINUS<<16 | gc.TINT8, + gc.OMINUS<<16 | gc.TUINT8, + gc.OMINUS<<16 | gc.TINT16, + gc.OMINUS<<16 | gc.TUINT16, + gc.OMINUS<<16 | gc.TINT32, + gc.OMINUS<<16 | gc.TUINT32, + gc.OMINUS<<16 | gc.TPTR32, + gc.OMINUS<<16 | gc.TINT64, + gc.OMINUS<<16 | gc.TUINT64, + gc.OMINUS<<16 | gc.TPTR64: + a = arm64.ANEG + + case gc.OMINUS<<16 | gc.TFLOAT32: + a = arm64.AFNEGS + + case gc.OMINUS<<16 | gc.TFLOAT64: + a = arm64.AFNEGD + + case gc.OAND<<16 | gc.TINT8, + gc.OAND<<16 | gc.TUINT8, + gc.OAND<<16 | gc.TINT16, + gc.OAND<<16 | gc.TUINT16, + gc.OAND<<16 | gc.TINT32, + gc.OAND<<16 | gc.TUINT32, + gc.OAND<<16 | gc.TPTR32, + gc.OAND<<16 | gc.TINT64, + gc.OAND<<16 | gc.TUINT64, + gc.OAND<<16 | gc.TPTR64: + a = arm64.AAND + + case gc.OOR<<16 | gc.TINT8, + gc.OOR<<16 | gc.TUINT8, + gc.OOR<<16 | gc.TINT16, + gc.OOR<<16 | gc.TUINT16, + gc.OOR<<16 | gc.TINT32, + gc.OOR<<16 | gc.TUINT32, + gc.OOR<<16 | gc.TPTR32, + gc.OOR<<16 | gc.TINT64, + gc.OOR<<16 | gc.TUINT64, + gc.OOR<<16 | gc.TPTR64: + a = arm64.AORR + + case gc.OXOR<<16 | gc.TINT8, + gc.OXOR<<16 | gc.TUINT8, + gc.OXOR<<16 | gc.TINT16, + gc.OXOR<<16 | gc.TUINT16, + gc.OXOR<<16 | gc.TINT32, + gc.OXOR<<16 | gc.TUINT32, + gc.OXOR<<16 | gc.TPTR32, + gc.OXOR<<16 | gc.TINT64, + gc.OXOR<<16 | gc.TUINT64, + gc.OXOR<<16 | gc.TPTR64: + a = arm64.AEOR + + // TODO(minux): handle rotates + //case CASE(OLROT, TINT8): + //case CASE(OLROT, TUINT8): + //case CASE(OLROT, TINT16): + //case CASE(OLROT, TUINT16): + //case CASE(OLROT, TINT32): + //case CASE(OLROT, TUINT32): + //case CASE(OLROT, TPTR32): + //case CASE(OLROT, TINT64): + //case CASE(OLROT, TUINT64): + //case CASE(OLROT, TPTR64): + // a = 0//???; RLDC? + // break; + + case gc.OLSH<<16 | gc.TINT8, + gc.OLSH<<16 | gc.TUINT8, + gc.OLSH<<16 | gc.TINT16, + gc.OLSH<<16 | gc.TUINT16, + gc.OLSH<<16 | gc.TINT32, + gc.OLSH<<16 | gc.TUINT32, + gc.OLSH<<16 | gc.TPTR32, + gc.OLSH<<16 | gc.TINT64, + gc.OLSH<<16 | gc.TUINT64, + gc.OLSH<<16 | gc.TPTR64: + a = arm64.ALSL + + case gc.ORSH<<16 | gc.TUINT8, + gc.ORSH<<16 | gc.TUINT16, + gc.ORSH<<16 | gc.TUINT32, + gc.ORSH<<16 | gc.TPTR32, + gc.ORSH<<16 | gc.TUINT64, + gc.ORSH<<16 | gc.TPTR64: + a = arm64.ALSR + + case gc.ORSH<<16 | gc.TINT8, + gc.ORSH<<16 | gc.TINT16, + gc.ORSH<<16 | gc.TINT32, + gc.ORSH<<16 | gc.TINT64: + a = arm64.AASR + + // TODO(minux): handle rotates + //case CASE(ORROTC, TINT8): + //case CASE(ORROTC, TUINT8): + //case CASE(ORROTC, TINT16): + //case CASE(ORROTC, TUINT16): + //case CASE(ORROTC, TINT32): + //case CASE(ORROTC, TUINT32): + //case CASE(ORROTC, TINT64): + //case CASE(ORROTC, TUINT64): + // a = 0//??? RLDC?? + // break; + + case gc.OHMUL<<16 | gc.TINT64: + a = arm64.ASMULH + + case gc.OHMUL<<16 | gc.TUINT64, + gc.OHMUL<<16 | gc.TPTR64: + a = arm64.AUMULH + + case gc.OMUL<<16 | gc.TINT8, + gc.OMUL<<16 | gc.TINT16, + gc.OMUL<<16 | gc.TINT32: + a = arm64.ASMULL + + case gc.OMUL<<16 | gc.TINT64: + a = arm64.AMUL + + case gc.OMUL<<16 | gc.TUINT8, + gc.OMUL<<16 | gc.TUINT16, + gc.OMUL<<16 | gc.TUINT32, + gc.OMUL<<16 | gc.TPTR32: + // don't use word multiply, the high 32-bit are undefined. + a = arm64.AUMULL + + case gc.OMUL<<16 | gc.TUINT64, + gc.OMUL<<16 | gc.TPTR64: + a = arm64.AMUL // for 64-bit multiplies, signedness doesn't matter. + + case gc.OMUL<<16 | gc.TFLOAT32: + a = arm64.AFMULS + + case gc.OMUL<<16 | gc.TFLOAT64: + a = arm64.AFMULD + + case gc.ODIV<<16 | gc.TINT8, + gc.ODIV<<16 | gc.TINT16, + gc.ODIV<<16 | gc.TINT32, + gc.ODIV<<16 | gc.TINT64: + a = arm64.ASDIV + + case gc.ODIV<<16 | gc.TUINT8, + gc.ODIV<<16 | gc.TUINT16, + gc.ODIV<<16 | gc.TUINT32, + gc.ODIV<<16 | gc.TPTR32, + gc.ODIV<<16 | gc.TUINT64, + gc.ODIV<<16 | gc.TPTR64: + a = arm64.AUDIV + + case gc.ODIV<<16 | gc.TFLOAT32: + a = arm64.AFDIVS + + case gc.ODIV<<16 | gc.TFLOAT64: + a = arm64.AFDIVD + + case gc.OSQRT<<16 | gc.TFLOAT64: + a = arm64.AFSQRTD + } + + return a +} + +const ( + ODynam = 1 << 0 + OAddable = 1 << 1 +) + +func xgen(n *gc.Node, a *gc.Node, o int) bool { + // TODO(minux) + + return -1 != 0 /*TypeKind(100016)*/ +} + +func sudoclean() { + return +} + +/* + * generate code to compute address of n, + * a reference to a (perhaps nested) field inside + * an array or struct. + * return 0 on failure, 1 on success. + * on success, leaves usable address in a. + * + * caller is responsible for calling sudoclean + * after successful sudoaddable, + * to release the register used for a. + */ +func sudoaddable(as int, n *gc.Node, a *obj.Addr) bool { + // TODO(minux) + + *a = obj.Addr{} + return false +} diff --git a/src/cmd/compile/internal/arm64/peep.go b/src/cmd/compile/internal/arm64/peep.go new file mode 100644 index 0000000000..1c3b2891aa --- /dev/null +++ b/src/cmd/compile/internal/arm64/peep.go @@ -0,0 +1,809 @@ +// Derived from Inferno utils/6c/peep.c +// http://code.google.com/p/inferno-os/source/browse/utils/6c/peep.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package arm64 + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/arm64" + "fmt" +) + +var gactive uint32 + +func peep(firstp *obj.Prog) { + g := (*gc.Graph)(gc.Flowstart(firstp, nil)) + if g == nil { + return + } + gactive = 0 + + var p *obj.Prog + var r *gc.Flow + var t int +loop1: + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + gc.Dumpit("loop1", g.Start, 0) + } + + t = 0 + for r = g.Start; r != nil; r = r.Link { + p = r.Prog + + // TODO(minux) Handle smaller moves. arm and amd64 + // distinguish between moves that *must* sign/zero + // extend and moves that don't care so they + // can eliminate moves that don't care without + // breaking moves that do care. This might let us + // simplify or remove the next peep loop, too. + if p.As == arm64.AMOVD || p.As == arm64.AFMOVD { + if regtyp(&p.To) { + // Try to eliminate reg->reg moves + if regtyp(&p.From) { + if p.From.Type == p.To.Type { + if copyprop(r) { + excise(r) + t++ + } else if subprop(r) && copyprop(r) { + excise(r) + t++ + } + } + } + } + } + } + + if t != 0 { + goto loop1 + } + + /* + * look for MOVB x,R; MOVB R,R (for small MOVs not handled above) + */ + var p1 *obj.Prog + var r1 *gc.Flow + for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { + p = r.Prog + switch p.As { + default: + continue + + case arm64.AMOVH, + arm64.AMOVHU, + arm64.AMOVB, + arm64.AMOVBU, + arm64.AMOVW, + arm64.AMOVWU: + if p.To.Type != obj.TYPE_REG { + continue + } + } + + r1 = r.Link + if r1 == nil { + continue + } + p1 = r1.Prog + if p1.As != p.As { + continue + } + if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg { + continue + } + if p1.To.Type != obj.TYPE_REG || p1.To.Reg != p.To.Reg { + continue + } + excise(r1) + } + + if gc.Debug['D'] > 1 { + goto ret /* allow following code improvement to be suppressed */ + } + + // MOVD $c, R'; ADD R', R (R' unused) -> ADD $c, R + for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { + p = r.Prog + switch p.As { + default: + continue + + case arm64.AMOVD: + if p.To.Type != obj.TYPE_REG { + continue + } + if p.From.Type != obj.TYPE_CONST { + continue + } + if p.From.Offset < 0 || 4096 <= p.From.Offset { + continue + } + } + r1 = r.Link + if r1 == nil { + continue + } + p1 = r1.Prog + if p1.As != arm64.AADD && p1.As != arm64.ASUB { // TODO(aram): also logical after we have bimm. + continue + } + if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg { + continue + } + if p1.To.Type != obj.TYPE_REG { + continue + } + if gc.Debug['P'] != 0 { + fmt.Printf("encoding $%d directly into %v in:\n%v\n%v\n", p.From.Offset, obj.Aconv(int(p1.As)), p, p1) + } + p1.From.Type = obj.TYPE_CONST + p1.From = p.From + excise(r) + } + + /* TODO(minux): + * look for OP x,y,R; CMP R, $0 -> OP.S x,y,R + * when OP can set condition codes correctly + */ + +ret: + gc.Flowend(g) +} + +func excise(r *gc.Flow) { + p := (*obj.Prog)(r.Prog) + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + fmt.Printf("%v ===delete===\n", p) + } + obj.Nopout(p) + gc.Ostats.Ndelmov++ +} + +func regtyp(a *obj.Addr) bool { + // TODO(rsc): Floating point register exclusions? + return a.Type == obj.TYPE_REG && arm64.REG_R0 <= a.Reg && a.Reg <= arm64.REG_F31 && a.Reg != arm64.REGZERO +} + +/* + * the idea is to substitute + * one register for another + * from one MOV to another + * MOV a, R1 + * ADD b, R1 / no use of R2 + * MOV R1, R2 + * would be converted to + * MOV a, R2 + * ADD b, R2 + * MOV R2, R1 + * hopefully, then the former or latter MOV + * will be eliminated by copy propagation. + * + * r0 (the argument, not the register) is the MOV at the end of the + * above sequences. This returns 1 if it modified any instructions. + */ +func subprop(r0 *gc.Flow) bool { + p := (*obj.Prog)(r0.Prog) + v1 := (*obj.Addr)(&p.From) + if !regtyp(v1) { + return false + } + v2 := (*obj.Addr)(&p.To) + if !regtyp(v2) { + return false + } + for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { + if gc.Uniqs(r) == nil { + break + } + p = r.Prog + if p.As == obj.AVARDEF || p.As == obj.AVARKILL { + continue + } + if p.Info.Flags&gc.Call != 0 { + return false + } + + if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightWrite { + if p.To.Type == v1.Type { + if p.To.Reg == v1.Reg { + copysub(&p.To, v1, v2, 1) + if gc.Debug['P'] != 0 { + fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog) + if p.From.Type == v2.Type { + fmt.Printf(" excise") + } + fmt.Printf("\n") + } + + for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { + p = r.Prog + copysub(&p.From, v1, v2, 1) + copysub1(p, v1, v2, 1) + copysub(&p.To, v1, v2, 1) + if gc.Debug['P'] != 0 { + fmt.Printf("%v\n", r.Prog) + } + } + + t := int(int(v1.Reg)) + v1.Reg = v2.Reg + v2.Reg = int16(t) + if gc.Debug['P'] != 0 { + fmt.Printf("%v last\n", r.Prog) + } + return true + } + } + } + + if copyau(&p.From, v2) || copyau1(p, v2) || copyau(&p.To, v2) { + break + } + if copysub(&p.From, v1, v2, 0) != 0 || copysub1(p, v1, v2, 0) != 0 || copysub(&p.To, v1, v2, 0) != 0 { + break + } + } + + return false +} + +/* + * The idea is to remove redundant copies. + * v1->v2 F=0 + * (use v2 s/v2/v1/)* + * set v1 F=1 + * use v2 return fail (v1->v2 move must remain) + * ----------------- + * v1->v2 F=0 + * (use v2 s/v2/v1/)* + * set v1 F=1 + * set v2 return success (caller can remove v1->v2 move) + */ +func copyprop(r0 *gc.Flow) bool { + p := (*obj.Prog)(r0.Prog) + v1 := (*obj.Addr)(&p.From) + v2 := (*obj.Addr)(&p.To) + if copyas(v1, v2) { + if gc.Debug['P'] != 0 { + fmt.Printf("eliminating self-move: %v\n", r0.Prog) + } + return true + } + + gactive++ + if gc.Debug['P'] != 0 { + fmt.Printf("trying to eliminate %v->%v move from:\n%v\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r0.Prog) + } + return copy1(v1, v2, r0.S1, 0) +} + +// copy1 replaces uses of v2 with v1 starting at r and returns 1 if +// all uses were rewritten. +func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f int) bool { + if uint32(r.Active) == gactive { + if gc.Debug['P'] != 0 { + fmt.Printf("act set; return 1\n") + } + return true + } + + r.Active = int32(gactive) + if gc.Debug['P'] != 0 { + fmt.Printf("copy1 replace %v with %v f=%d\n", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), f) + } + var t int + var p *obj.Prog + for ; r != nil; r = r.S1 { + p = r.Prog + if gc.Debug['P'] != 0 { + fmt.Printf("%v", p) + } + if f == 0 && gc.Uniqp(r) == nil { + // Multiple predecessors; conservatively + // assume v1 was set on other path + f = 1 + + if gc.Debug['P'] != 0 { + fmt.Printf("; merge; f=%d", f) + } + } + + t = copyu(p, v2, nil) + switch t { + case 2: /* rar, can't split */ + if gc.Debug['P'] != 0 { + fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2)) + } + return false + + case 3: /* set */ + if gc.Debug['P'] != 0 { + fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2)) + } + return true + + case 1, /* used, substitute */ + 4: /* use and set */ + if f != 0 { + if gc.Debug['P'] == 0 { + return false + } + if t == 4 { + fmt.Printf("; %v used+set and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f) + } else { + fmt.Printf("; %v used and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f) + } + return false + } + + if copyu(p, v2, v1) != 0 { + if gc.Debug['P'] != 0 { + fmt.Printf("; sub fail; return 0\n") + } + return false + } + + if gc.Debug['P'] != 0 { + fmt.Printf("; sub %v->%v\n => %v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), p) + } + if t == 4 { + if gc.Debug['P'] != 0 { + fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2)) + } + return true + } + } + + if f == 0 { + t = copyu(p, v1, nil) + if f == 0 && (t == 2 || t == 3 || t == 4) { + f = 1 + if gc.Debug['P'] != 0 { + fmt.Printf("; %v set and !f; f=%d", gc.Ctxt.Dconv(v1), f) + } + } + } + + if gc.Debug['P'] != 0 { + fmt.Printf("\n") + } + if r.S2 != nil { + if !copy1(v1, v2, r.S2, f) { + return false + } + } + } + + return true +} + +// If s==nil, copyu returns the set/use of v in p; otherwise, it +// modifies p to replace reads of v with reads of s and returns 0 for +// success or non-zero for failure. +// +// If s==nil, copy returns one of the following values: +// 1 if v only used +// 2 if v is set and used in one address (read-alter-rewrite; +// can't substitute) +// 3 if v is only set +// 4 if v is set in one address and used in another (so addresses +// can be rewritten independently) +// 0 otherwise (not touched) +func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int { + if p.From3.Type != obj.TYPE_NONE { + // 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 { + // 7g never generates a to2 + fmt.Printf("copyu: to2 (%v) not implemented\n", gc.Ctxt.Dconv(&p.To2)) + } + + switch p.As { + default: + fmt.Printf("copyu: can't find %v\n", obj.Aconv(int(p.As))) + return 2 + + case obj.ANOP, /* read p->from, write p->to */ + arm64.ANEG, + arm64.AFNEGD, + arm64.AFNEGS, + arm64.AFSQRTD, + arm64.AFCVTZSD, + arm64.AFCVTZSS, + arm64.AFCVTZSDW, + arm64.AFCVTZSSW, + arm64.AFCVTZUD, + arm64.AFCVTZUS, + arm64.AFCVTZUDW, + arm64.AFCVTZUSW, + arm64.AFCVTSD, + arm64.AFCVTDS, + arm64.ASCVTFD, + arm64.ASCVTFS, + arm64.ASCVTFWD, + arm64.ASCVTFWS, + arm64.AUCVTFD, + arm64.AUCVTFS, + arm64.AUCVTFWD, + arm64.AUCVTFWS, + arm64.AMOVB, + arm64.AMOVBU, + arm64.AMOVH, + arm64.AMOVHU, + arm64.AMOVW, + arm64.AMOVWU, + arm64.AMOVD, + arm64.AFMOVS, + arm64.AFMOVD: + if p.Scond == 0 { + if s != nil { + if copysub(&p.From, v, s, 1) != 0 { + return 1 + } + + // Update only indirect uses of v in p->to + if !copyas(&p.To, v) { + if copysub(&p.To, v, s, 1) != 0 { + return 1 + } + } + return 0 + } + + if copyas(&p.To, v) { + // Fix up implicit from + if p.From.Type == obj.TYPE_NONE { + p.From = p.To + } + if copyau(&p.From, v) { + return 4 + } + return 3 + } + + if copyau(&p.From, v) { + return 1 + } + if copyau(&p.To, v) { + // p->to only indirectly uses v + return 1 + } + + return 0 + } + + /* rar p->from, write p->to or read p->from, rar p->to */ + if p.From.Type == obj.TYPE_MEM { + if copyas(&p.From, v) { + // No s!=nil check; need to fail + // anyway in that case + return 2 + } + + if s != nil { + if copysub(&p.To, v, s, 1) != 0 { + return 1 + } + return 0 + } + + if copyas(&p.To, v) { + return 3 + } + } else if p.To.Type == obj.TYPE_MEM { + if copyas(&p.To, v) { + return 2 + } + if s != nil { + if copysub(&p.From, v, s, 1) != 0 { + return 1 + } + return 0 + } + + if copyau(&p.From, v) { + return 1 + } + } else { + fmt.Printf("copyu: bad %v\n", p) + } + + return 0 + + case arm64.AADD, /* read p->from, read p->reg, write p->to */ + arm64.ASUB, + arm64.AAND, + arm64.AORR, + arm64.AEOR, + arm64.AMUL, + arm64.ASMULL, + arm64.AUMULL, + arm64.ASMULH, + arm64.AUMULH, + arm64.ASDIV, + arm64.AUDIV, + arm64.ALSL, + arm64.ALSR, + arm64.AASR, + arm64.AFADDD, + arm64.AFADDS, + arm64.AFSUBD, + arm64.AFSUBS, + arm64.AFMULD, + arm64.AFMULS, + arm64.AFDIVD, + arm64.AFDIVS: + if s != nil { + if copysub(&p.From, v, s, 1) != 0 { + return 1 + } + if copysub1(p, v, s, 1) != 0 { + return 1 + } + + // Update only indirect uses of v in p->to + if !copyas(&p.To, v) { + if copysub(&p.To, v, s, 1) != 0 { + return 1 + } + } + return 0 + } + + if copyas(&p.To, v) { + if p.Reg == 0 { + // Fix up implicit reg (e.g., ADD + // R3,R4 -> ADD R3,R4,R4) so we can + // update reg and to separately. + p.Reg = p.To.Reg + } + + if copyau(&p.From, v) { + return 4 + } + if copyau1(p, v) { + return 4 + } + return 3 + } + + if copyau(&p.From, v) { + return 1 + } + if copyau1(p, v) { + return 1 + } + if copyau(&p.To, v) { + return 1 + } + return 0 + + case arm64.ABEQ, + arm64.ABNE, + arm64.ABGE, + arm64.ABLT, + arm64.ABGT, + arm64.ABLE, + arm64.ABLO, + arm64.ABLS, + arm64.ABHI, + arm64.ABHS: + return 0 + + case obj.ACHECKNIL, /* read p->from */ + arm64.ACMP, /* read p->from, read p->reg */ + arm64.AFCMPD, + arm64.AFCMPS: + if s != nil { + if copysub(&p.From, v, s, 1) != 0 { + return 1 + } + return copysub1(p, v, s, 1) + } + + if copyau(&p.From, v) { + return 1 + } + if copyau1(p, v) { + return 1 + } + return 0 + + case arm64.AB: /* read p->to */ + if s != nil { + if copysub(&p.To, v, s, 1) != 0 { + return 1 + } + return 0 + } + + if copyau(&p.To, v) { + return 1 + } + return 0 + + case obj.ARET: /* funny */ + if s != nil { + return 0 + } + + // All registers die at this point, so claim + // everything is set (and not used). + return 3 + + case arm64.ABL: /* funny */ + if p.From.Type == obj.TYPE_REG && v.Type == obj.TYPE_REG && p.From.Reg == v.Reg { + return 2 + } + + if s != nil { + if copysub(&p.To, v, s, 1) != 0 { + return 1 + } + return 0 + } + + if copyau(&p.To, v) { + return 4 + } + return 3 + + // R31 is zero, used by DUFFZERO, cannot be substituted. + // R16 is ptr to memory, used and set, cannot be substituted. + case obj.ADUFFZERO: + if v.Type == obj.TYPE_REG { + if v.Reg == 31 { + return 1 + } + if v.Reg == 16 { + return 2 + } + } + + return 0 + + // R16, R17 are ptr to src, dst, used and set, cannot be substituted. + // R27 is scratch, set by DUFFCOPY, cannot be substituted. + case obj.ADUFFCOPY: + if v.Type == obj.TYPE_REG { + if v.Reg == 16 || v.Reg == 17 { + return 2 + } + if v.Reg == 27 { + return 3 + } + } + + return 0 + + case arm64.AHINT, + obj.ATEXT, + obj.APCDATA, + obj.AFUNCDATA, + obj.AVARDEF, + obj.AVARKILL: + return 0 + } +} + +// copyas returns 1 if a and v address the same register. +// +// If a is the from operand, this means this operation reads the +// register in v. If a is the to operand, this means this operation +// writes the register in v. +func copyas(a *obj.Addr, v *obj.Addr) bool { + if regtyp(v) { + if a.Type == v.Type { + if a.Reg == v.Reg { + return true + } + } + } + return false +} + +// copyau returns 1 if a either directly or indirectly addresses the +// same register as v. +// +// If a is the from operand, this means this operation reads the +// register in v. If a is the to operand, this means the operation +// either reads or writes the register in v (if !copyas(a, v), then +// the operation reads the register in v). +func copyau(a *obj.Addr, v *obj.Addr) bool { + if copyas(a, v) { + return true + } + if v.Type == obj.TYPE_REG { + if a.Type == obj.TYPE_MEM || (a.Type == obj.TYPE_ADDR && a.Reg != 0) { + if v.Reg == a.Reg { + return true + } + } + } + return false +} + +// copyau1 returns 1 if p->reg references the same register as v and v +// is a direct reference. +func copyau1(p *obj.Prog, v *obj.Addr) bool { + if regtyp(v) && v.Reg != 0 { + if p.Reg == v.Reg { + return true + } + } + return false +} + +// copysub replaces v with s in a if f!=0 or indicates it if could if f==0. +// Returns 1 on failure to substitute (it always succeeds on arm64). +func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f int) int { + if f != 0 { + if copyau(a, v) { + a.Reg = s.Reg + } + } + return 0 +} + +// copysub1 replaces v with s in p1->reg if f!=0 or indicates if it could if f==0. +// Returns 1 on failure to substitute (it always succeeds on arm64). +func copysub1(p1 *obj.Prog, v *obj.Addr, s *obj.Addr, f int) int { + if f != 0 { + if copyau1(p1, v) { + p1.Reg = s.Reg + } + } + return 0 +} + +func sameaddr(a *obj.Addr, v *obj.Addr) bool { + if a.Type != v.Type { + return false + } + if regtyp(v) && a.Reg == v.Reg { + return true + } + if v.Type == obj.NAME_AUTO || v.Type == obj.NAME_PARAM { + if v.Offset == a.Offset { + return true + } + } + return false +} + +func smallindir(a *obj.Addr, reg *obj.Addr) bool { + return reg.Type == obj.TYPE_REG && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && 0 <= a.Offset && a.Offset < 4096 +} + +func stackaddr(a *obj.Addr) bool { + return a.Type == obj.TYPE_REG && a.Reg == arm64.REGSP +} diff --git a/src/cmd/compile/internal/arm64/prog.go b/src/cmd/compile/internal/arm64/prog.go new file mode 100644 index 0000000000..1106e788a5 --- /dev/null +++ b/src/cmd/compile/internal/arm64/prog.go @@ -0,0 +1,174 @@ +// Copyright 2014 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 arm64 + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/arm64" +) + +const ( + LeftRdwr uint32 = gc.LeftRead | gc.LeftWrite + RightRdwr uint32 = gc.RightRead | gc.RightWrite +) + +// This table gives the basic information about instruction +// generated by the compiler and processed in the optimizer. +// See opt.h for bit definitions. +// +// Instructions not generated need not be listed. +// As an exception to that rule, we typically write down all the +// size variants of an operation even if we just use a subset. +// +// The table is formatted for 8-space tabs. +var progtable = [arm64.ALAST]obj.ProgInfo{ + obj.ATYPE: {gc.Pseudo | gc.Skip, 0, 0, 0}, + obj.ATEXT: {gc.Pseudo, 0, 0, 0}, + obj.AFUNCDATA: {gc.Pseudo, 0, 0, 0}, + obj.APCDATA: {gc.Pseudo, 0, 0, 0}, + obj.AUNDEF: {gc.Break, 0, 0, 0}, + obj.AUSEFIELD: {gc.OK, 0, 0, 0}, + obj.ACHECKNIL: {gc.LeftRead, 0, 0, 0}, + obj.AVARDEF: {gc.Pseudo | gc.RightWrite, 0, 0, 0}, + obj.AVARKILL: {gc.Pseudo | gc.RightWrite, 0, 0, 0}, + + // NOP is an internal no-op that also stands + // for USED and SET annotations, not the Power opcode. + obj.ANOP: {gc.LeftRead | gc.RightWrite, 0, 0, 0}, + arm64.AHINT: {gc.OK, 0, 0, 0}, + + // Integer + arm64.AADD: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm64.ASUB: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm64.ANEG: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm64.AAND: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm64.AORR: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm64.AEOR: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm64.AMUL: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm64.ASMULL: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm64.AUMULL: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm64.ASMULH: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm64.AUMULH: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm64.ASDIV: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm64.AUDIV: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm64.ALSL: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm64.ALSR: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm64.AASR: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm64.ACMP: {gc.SizeQ | gc.LeftRead | gc.RegRead, 0, 0, 0}, + + // Floating point. + arm64.AFADDD: {gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm64.AFADDS: {gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm64.AFSUBD: {gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm64.AFSUBS: {gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm64.AFNEGD: {gc.SizeD | gc.LeftRead | gc.RightWrite, 0, 0, 0}, + arm64.AFNEGS: {gc.SizeF | gc.LeftRead | gc.RightWrite, 0, 0, 0}, + arm64.AFSQRTD: {gc.SizeD | gc.LeftRead | gc.RightWrite, 0, 0, 0}, + arm64.AFMULD: {gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm64.AFMULS: {gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm64.AFDIVD: {gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm64.AFDIVS: {gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + arm64.AFCMPD: {gc.SizeD | gc.LeftRead | gc.RegRead, 0, 0, 0}, + arm64.AFCMPS: {gc.SizeF | gc.LeftRead | gc.RegRead, 0, 0, 0}, + + // float -> integer + arm64.AFCVTZSD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + arm64.AFCVTZSS: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + arm64.AFCVTZSDW: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + arm64.AFCVTZSSW: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + arm64.AFCVTZUD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + arm64.AFCVTZUS: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + arm64.AFCVTZUDW: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + arm64.AFCVTZUSW: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + + // float -> float + arm64.AFCVTSD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + arm64.AFCVTDS: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + + // integer -> float + arm64.ASCVTFD: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + arm64.ASCVTFS: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + arm64.ASCVTFWD: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + arm64.ASCVTFWS: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + arm64.AUCVTFD: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + arm64.AUCVTFS: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + arm64.AUCVTFWD: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + arm64.AUCVTFWS: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + + // Moves + arm64.AMOVB: {gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv, 0, 0, 0}, + arm64.AMOVBU: {gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv, 0, 0, 0}, + arm64.AMOVH: {gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv, 0, 0, 0}, + arm64.AMOVHU: {gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv, 0, 0, 0}, + arm64.AMOVW: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv, 0, 0, 0}, + arm64.AMOVWU: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv, 0, 0, 0}, + arm64.AMOVD: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, + arm64.AFMOVS: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv, 0, 0, 0}, + arm64.AFMOVD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, + + // Jumps + arm64.AB: {gc.Jump | gc.Break, 0, 0, 0}, + arm64.ABL: {gc.Call, 0, 0, 0}, + arm64.ABEQ: {gc.Cjmp, 0, 0, 0}, + arm64.ABNE: {gc.Cjmp, 0, 0, 0}, + arm64.ABGE: {gc.Cjmp, 0, 0, 0}, + arm64.ABLT: {gc.Cjmp, 0, 0, 0}, + arm64.ABGT: {gc.Cjmp, 0, 0, 0}, + arm64.ABLE: {gc.Cjmp, 0, 0, 0}, + arm64.ABLO: {gc.Cjmp, 0, 0, 0}, + arm64.ABLS: {gc.Cjmp, 0, 0, 0}, + arm64.ABHI: {gc.Cjmp, 0, 0, 0}, + arm64.ABHS: {gc.Cjmp, 0, 0, 0}, + arm64.ACBZ: {gc.Cjmp, 0, 0, 0}, + arm64.ACBNZ: {gc.Cjmp, 0, 0, 0}, + obj.ARET: {gc.Break, 0, 0, 0}, + obj.ADUFFZERO: {gc.Call, 0, 0, 0}, + obj.ADUFFCOPY: {gc.Call, 0, 0, 0}, +} + +func proginfo(p *obj.Prog) { + info := &p.Info + *info = progtable[p.As] + if info.Flags == 0 { + gc.Fatal("proginfo: unknown instruction %v", p) + } + + if (info.Flags&gc.RegRead != 0) && p.Reg == 0 { + info.Flags &^= gc.RegRead + info.Flags |= gc.RightRead /*CanRegRead |*/ + } + + if (p.From.Type == obj.TYPE_MEM || p.From.Type == obj.TYPE_ADDR) && p.From.Reg != 0 { + info.Regindex |= RtoB(int(p.From.Reg)) + if p.Scond != 0 { + info.Regset |= RtoB(int(p.From.Reg)) + } + } + + if (p.To.Type == obj.TYPE_MEM || p.To.Type == obj.TYPE_ADDR) && p.To.Reg != 0 { + info.Regindex |= RtoB(int(p.To.Reg)) + if p.Scond != 0 { + info.Regset |= RtoB(int(p.To.Reg)) + } + } + + if p.From.Type == obj.TYPE_ADDR && p.From.Sym != nil && (info.Flags&gc.LeftRead != 0) { + info.Flags &^= gc.LeftRead + info.Flags |= gc.LeftAddr + } + + if p.As == obj.ADUFFZERO { + info.Reguse |= RtoB(arm64.REGRT1) + info.Regset |= RtoB(arm64.REGRT1) + } + + if p.As == obj.ADUFFCOPY { + // TODO(austin) Revisit when duffcopy is implemented + info.Reguse |= RtoB(arm64.REGRT1) | RtoB(arm64.REGRT2) | RtoB(arm64.REG_R5) + + info.Regset |= RtoB(arm64.REGRT1) | RtoB(arm64.REGRT2) + } +} diff --git a/src/cmd/compile/internal/arm64/reg.go b/src/cmd/compile/internal/arm64/reg.go new file mode 100644 index 0000000000..7bc756b7bf --- /dev/null +++ b/src/cmd/compile/internal/arm64/reg.go @@ -0,0 +1,171 @@ +// Derived from Inferno utils/6c/reg.c +// http://code.google.com/p/inferno-os/source/browse/utils/6c/reg.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package arm64 + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj/arm64" +) + +const ( + NREGVAR = 64 /* 32 general + 32 floating */ +) + +var reg [arm64.NREG + arm64.NFREG]uint8 + +var regname = []string{ + ".R0", + ".R1", + ".R2", + ".R3", + ".R4", + ".R5", + ".R6", + ".R7", + ".R8", + ".R9", + ".R10", + ".R11", + ".R12", + ".R13", + ".R14", + ".R15", + ".R16", + ".R17", + ".R18", + ".R19", + ".R20", + ".R21", + ".R22", + ".R23", + ".R24", + ".R25", + ".R26", + ".R27", + ".R28", + ".R29", + ".R30", + ".R31", + ".F0", + ".F1", + ".F2", + ".F3", + ".F4", + ".F5", + ".F6", + ".F7", + ".F8", + ".F9", + ".F10", + ".F11", + ".F12", + ".F13", + ".F14", + ".F15", + ".F16", + ".F17", + ".F18", + ".F19", + ".F20", + ".F21", + ".F22", + ".F23", + ".F24", + ".F25", + ".F26", + ".F27", + ".F28", + ".F29", + ".F30", + ".F31", +} + +func regnames(n *int) []string { + *n = NREGVAR + return regname +} + +func excludedregs() uint64 { + // Exclude registers with fixed functions + regbits := uint64(RtoB(arm64.REGRT1) | RtoB(arm64.REGRT2) | RtoB(arm64.REGPR)) + + // Exclude R26 - R31. + for r := arm64.REGMAX + 1; r <= arm64.REGZERO; r++ { + regbits |= RtoB(r) + } + + // Also exclude floating point registers with fixed constants + regbits |= RtoB(arm64.REG_F27) | RtoB(arm64.REG_F28) | RtoB(arm64.REG_F29) | RtoB(arm64.REG_F30) | RtoB(arm64.REG_F31) + + return regbits +} + +func doregbits(r int) uint64 { + return 0 +} + +/* + * track register variables including external registers: + * bit reg + * 0 R0 + * 1 R1 + * ... ... + * 31 R31 + * 32+0 F0 + * 32+1 F1 + * ... ... + * 32+31 F31 + */ +func RtoB(r int) uint64 { + if r >= arm64.REG_R0 && r <= arm64.REG_R31 { + return 1 << uint(r-arm64.REG_R0) + } + if r >= arm64.REG_F0 && r <= arm64.REG_F31 { + return 1 << uint(32+r-arm64.REG_F0) + } + return 0 +} + +func BtoR(b uint64) int { + b &= 0xffffffff + if b == 0 { + return 0 + } + return gc.Bitno(b) + arm64.REG_R0 +} + +func BtoF(b uint64) int { + b >>= 32 + if b == 0 { + return 0 + } + return gc.Bitno(b) + arm64.REG_F0 +} diff --git a/src/cmd/compile/internal/big/accuracy_string.go b/src/cmd/compile/internal/big/accuracy_string.go new file mode 100644 index 0000000000..24ef7f1077 --- /dev/null +++ b/src/cmd/compile/internal/big/accuracy_string.go @@ -0,0 +1,17 @@ +// generated by stringer -type=Accuracy; DO NOT EDIT + +package big + +import "fmt" + +const _Accuracy_name = "BelowExactAbove" + +var _Accuracy_index = [...]uint8{0, 5, 10, 15} + +func (i Accuracy) String() string { + i -= -1 + if i < 0 || i+1 >= Accuracy(len(_Accuracy_index)) { + return fmt.Sprintf("Accuracy(%d)", i+-1) + } + return _Accuracy_name[_Accuracy_index[i]:_Accuracy_index[i+1]] +} diff --git a/src/cmd/compile/internal/big/arith.go b/src/cmd/compile/internal/big/arith.go new file mode 100644 index 0000000000..328c85c4f7 --- /dev/null +++ b/src/cmd/compile/internal/big/arith.go @@ -0,0 +1,291 @@ +// Copyright 2009 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. + +// This file provides Go implementations of elementary multi-precision +// arithmetic operations on word vectors. Needed for platforms without +// assembly implementations of these routines. + +package big + +// A Word represents a single digit of a multi-precision unsigned integer. +type Word uintptr + +const ( + // Compute the size _S of a Word in bytes. + _m = ^Word(0) + _logS = _m>>8&1 + _m>>16&1 + _m>>32&1 + _S = 1 << _logS + + _W = _S << 3 // word size in bits + _B = 1 << _W // digit base + _M = _B - 1 // digit mask + + _W2 = _W / 2 // half word size in bits + _B2 = 1 << _W2 // half digit base + _M2 = _B2 - 1 // half digit mask +) + +// ---------------------------------------------------------------------------- +// Elementary operations on words +// +// These operations are used by the vector operations below. + +// z1<<_W + z0 = x+y+c, with c == 0 or 1 +func addWW_g(x, y, c Word) (z1, z0 Word) { + yc := y + c + z0 = x + yc + if z0 < x || yc < y { + z1 = 1 + } + return +} + +// z1<<_W + z0 = x-y-c, with c == 0 or 1 +func subWW_g(x, y, c Word) (z1, z0 Word) { + yc := y + c + z0 = x - yc + if z0 > x || yc < y { + z1 = 1 + } + return +} + +// z1<<_W + z0 = x*y +// Adapted from Warren, Hacker's Delight, p. 132. +func mulWW_g(x, y Word) (z1, z0 Word) { + x0 := x & _M2 + x1 := x >> _W2 + y0 := y & _M2 + y1 := y >> _W2 + w0 := x0 * y0 + t := x1*y0 + w0>>_W2 + w1 := t & _M2 + w2 := t >> _W2 + w1 += x0 * y1 + z1 = x1*y1 + w2 + w1>>_W2 + z0 = x * y + return +} + +// z1<<_W + z0 = x*y + c +func mulAddWWW_g(x, y, c Word) (z1, z0 Word) { + z1, zz0 := mulWW_g(x, y) + if z0 = zz0 + c; z0 < zz0 { + z1++ + } + return +} + +// Length of x in bits. +func bitLen_g(x Word) (n int) { + for ; x >= 0x8000; x >>= 16 { + n += 16 + } + if x >= 0x80 { + x >>= 8 + n += 8 + } + if x >= 0x8 { + x >>= 4 + n += 4 + } + if x >= 0x2 { + x >>= 2 + n += 2 + } + if x >= 0x1 { + n++ + } + return +} + +// log2 computes the integer binary logarithm of x. +// The result is the integer n for which 2^n <= x < 2^(n+1). +// If x == 0, the result is -1. +func log2(x Word) int { + return bitLen(x) - 1 +} + +// Number of leading zeros in x. +func leadingZeros(x Word) uint { + return uint(_W - bitLen(x)) +} + +// q = (u1<<_W + u0 - r)/y +// Adapted from Warren, Hacker's Delight, p. 152. +func divWW_g(u1, u0, v Word) (q, r Word) { + if u1 >= v { + return 1<<_W - 1, 1<<_W - 1 + } + + s := leadingZeros(v) + v <<= s + + vn1 := v >> _W2 + vn0 := v & _M2 + un32 := u1<>(_W-s) + un10 := u0 << s + un1 := un10 >> _W2 + un0 := un10 & _M2 + q1 := un32 / vn1 + rhat := un32 - q1*vn1 + + for q1 >= _B2 || q1*vn0 > _B2*rhat+un1 { + q1-- + rhat += vn1 + if rhat >= _B2 { + break + } + } + + un21 := un32*_B2 + un1 - q1*v + q0 := un21 / vn1 + rhat = un21 - q0*vn1 + + for q0 >= _B2 || q0*vn0 > _B2*rhat+un0 { + q0-- + rhat += vn1 + if rhat >= _B2 { + break + } + } + + return q1*_B2 + q0, (un21*_B2 + un0 - q0*v) >> s +} + +// Keep for performance debugging. +// Using addWW_g is likely slower. +const use_addWW_g = false + +// The resulting carry c is either 0 or 1. +func addVV_g(z, x, y []Word) (c Word) { + if use_addWW_g { + for i := range z { + c, z[i] = addWW_g(x[i], y[i], c) + } + return + } + + for i, xi := range x[:len(z)] { + yi := y[i] + zi := xi + yi + c + z[i] = zi + // see "Hacker's Delight", section 2-12 (overflow detection) + c = (xi&yi | (xi|yi)&^zi) >> (_W - 1) + } + return +} + +// The resulting carry c is either 0 or 1. +func subVV_g(z, x, y []Word) (c Word) { + if use_addWW_g { + for i := range z { + c, z[i] = subWW_g(x[i], y[i], c) + } + return + } + + for i, xi := range x[:len(z)] { + yi := y[i] + zi := xi - yi - c + z[i] = zi + // see "Hacker's Delight", section 2-12 (overflow detection) + c = (yi&^xi | (yi|^xi)&zi) >> (_W - 1) + } + 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 { + c = y + for i := range z { + c, z[i] = addWW_g(x[i], c, 0) + } + return + } + + c = y + for i, xi := range x[:len(z)] { + zi := xi + c + z[i] = zi + c = xi &^ zi >> (_W - 1) + } + return +} + +func subVW_g(z, x []Word, y Word) (c Word) { + if use_addWW_g { + c = y + for i := range z { + c, z[i] = subWW_g(x[i], c, 0) + } + return + } + + c = y + for i, xi := range x[:len(z)] { + zi := xi - c + z[i] = zi + c = (zi &^ xi) >> (_W - 1) + } + return +} + +func shlVU_g(z, x []Word, s uint) (c Word) { + if n := len(z); n > 0 { + ŝ := _W - s + w1 := x[n-1] + c = w1 >> ŝ + for i := n - 1; i > 0; i-- { + w := w1 + w1 = x[i-1] + z[i] = w<>ŝ + } + z[0] = w1 << s + } + return +} + +func shrVU_g(z, x []Word, s uint) (c Word) { + if n := len(z); n > 0 { + ŝ := _W - s + w1 := x[0] + c = w1 << ŝ + for i := 0; i < n-1; i++ { + w := w1 + w1 = x[i+1] + z[i] = w>>s | w1<<ŝ + } + z[n-1] = w1 >> s + } + return +} + +func mulAddVWW_g(z, x []Word, y, r Word) (c Word) { + c = r + for i := range z { + c, z[i] = mulAddWWW_g(x[i], y, c) + } + return +} + +// TODO(gri) Remove use of addWW_g here and then we can remove addWW_g and subWW_g. +func addMulVVW_g(z, x []Word, y Word) (c Word) { + for i := range z { + z1, z0 := mulAddWWW_g(x[i], y, z[i]) + c, z[i] = addWW_g(z0, c, 0) + c += z1 + } + return +} + +func divWVW_g(z []Word, xn Word, x []Word, y Word) (r Word) { + r = xn + for i := len(z) - 1; i >= 0; i-- { + z[i], r = divWW_g(r, x[i], y) + } + return +} diff --git a/src/cmd/compile/internal/big/arith_decl.go b/src/cmd/compile/internal/big/arith_decl.go new file mode 100644 index 0000000000..fe13577203 --- /dev/null +++ b/src/cmd/compile/internal/big/arith_decl.go @@ -0,0 +1,53 @@ +// 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 big + +func mulWW(x, y Word) (z1, z0 Word) { + return mulWW_g(x, y) +} + +func divWW(x1, x0, y Word) (q, r Word) { + return divWW_g(x1, x0, y) +} + +func addVV(z, x, y []Word) (c Word) { + return addVV_g(z, x, y) +} + +func subVV(z, x, y []Word) (c Word) { + return subVV_g(z, x, y) +} + +func addVW(z, x []Word, y Word) (c Word) { + return addVW_g(z, x, y) +} + +func subVW(z, x []Word, y Word) (c Word) { + return subVW_g(z, x, y) +} + +func shlVU(z, x []Word, s uint) (c Word) { + return shlVU_g(z, x, s) +} + +func shrVU(z, x []Word, s uint) (c Word) { + return shrVU_g(z, x, s) +} + +func mulAddVWW(z, x []Word, y, r Word) (c Word) { + return mulAddVWW_g(z, x, y, r) +} + +func addMulVVW(z, x []Word, y Word) (c Word) { + return addMulVVW_g(z, x, y) +} + +func divWVW(z []Word, xn Word, x []Word, y Word) (r Word) { + return divWVW_g(z, xn, x, y) +} + +func bitLen(x Word) (n int) { + return bitLen_g(x) +} diff --git a/src/cmd/compile/internal/big/arith_test.go b/src/cmd/compile/internal/big/arith_test.go new file mode 100644 index 0000000000..cd92dd7173 --- /dev/null +++ b/src/cmd/compile/internal/big/arith_test.go @@ -0,0 +1,456 @@ +// Copyright 2009 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 big + +import ( + "math/rand" + "testing" +) + +type funWW func(x, y, c Word) (z1, z0 Word) +type argWW struct { + x, y, c, z1, z0 Word +} + +var sumWW = []argWW{ + {0, 0, 0, 0, 0}, + {0, 1, 0, 0, 1}, + {0, 0, 1, 0, 1}, + {0, 1, 1, 0, 2}, + {12345, 67890, 0, 0, 80235}, + {12345, 67890, 1, 0, 80236}, + {_M, 1, 0, 1, 0}, + {_M, 0, 1, 1, 0}, + {_M, 1, 1, 1, 1}, + {_M, _M, 0, 1, _M - 1}, + {_M, _M, 1, 1, _M}, +} + +func testFunWW(t *testing.T, msg string, f funWW, a argWW) { + z1, z0 := f(a.x, a.y, a.c) + if z1 != a.z1 || z0 != a.z0 { + t.Errorf("%s%+v\n\tgot z1:z0 = %#x:%#x; want %#x:%#x", msg, a, z1, z0, a.z1, a.z0) + } +} + +func TestFunWW(t *testing.T) { + for _, a := range sumWW { + arg := a + testFunWW(t, "addWW_g", addWW_g, arg) + + arg = argWW{a.y, a.x, a.c, a.z1, a.z0} + testFunWW(t, "addWW_g symmetric", addWW_g, arg) + + arg = argWW{a.z0, a.x, a.c, a.z1, a.y} + testFunWW(t, "subWW_g", subWW_g, arg) + + arg = argWW{a.z0, a.y, a.c, a.z1, a.x} + testFunWW(t, "subWW_g symmetric", subWW_g, arg) + } +} + +type funVV func(z, x, y []Word) (c Word) +type argVV struct { + z, x, y nat + c Word +} + +var sumVV = []argVV{ + {}, + {nat{0}, nat{0}, nat{0}, 0}, + {nat{1}, nat{1}, nat{0}, 0}, + {nat{0}, nat{_M}, nat{1}, 1}, + {nat{80235}, nat{12345}, nat{67890}, 0}, + {nat{_M - 1}, nat{_M}, nat{_M}, 1}, + {nat{0, 0, 0, 0}, nat{_M, _M, _M, _M}, nat{1, 0, 0, 0}, 1}, + {nat{0, 0, 0, _M}, nat{_M, _M, _M, _M - 1}, nat{1, 0, 0, 0}, 0}, + {nat{0, 0, 0, 0}, nat{_M, 0, _M, 0}, nat{1, _M, 0, _M}, 1}, +} + +func testFunVV(t *testing.T, msg string, f funVV, a argVV) { + z := make(nat, len(a.z)) + c := f(z, a.x, a.y) + for i, zi := range z { + if zi != a.z[i] { + t.Errorf("%s%+v\n\tgot z[%d] = %#x; want %#x", msg, a, i, zi, a.z[i]) + break + } + } + if c != a.c { + t.Errorf("%s%+v\n\tgot c = %#x; want %#x", msg, a, c, a.c) + } +} + +func TestFunVV(t *testing.T) { + for _, a := range sumVV { + arg := a + testFunVV(t, "addVV_g", addVV_g, arg) + testFunVV(t, "addVV", addVV, arg) + + arg = argVV{a.z, a.y, a.x, a.c} + testFunVV(t, "addVV_g symmetric", addVV_g, arg) + testFunVV(t, "addVV symmetric", addVV, arg) + + arg = argVV{a.x, a.z, a.y, a.c} + testFunVV(t, "subVV_g", subVV_g, arg) + testFunVV(t, "subVV", subVV, arg) + + arg = argVV{a.y, a.z, a.x, a.c} + testFunVV(t, "subVV_g symmetric", subVV_g, arg) + testFunVV(t, "subVV symmetric", subVV, arg) + } +} + +// Always the same seed for reproducible results. +var rnd = rand.New(rand.NewSource(0)) + +func rndW() Word { + return Word(rnd.Int63()<<1 | rnd.Int63n(2)) +} + +func rndV(n int) []Word { + v := make([]Word, n) + for i := range v { + v[i] = rndW() + } + return v +} + +func benchmarkFunVV(b *testing.B, f funVV, n int) { + x := rndV(n) + y := rndV(n) + z := make([]Word, n) + b.SetBytes(int64(n * _W)) + b.ResetTimer() + for i := 0; i < b.N; i++ { + f(z, x, y) + } +} + +func BenchmarkAddVV_1(b *testing.B) { benchmarkFunVV(b, addVV, 1) } +func BenchmarkAddVV_2(b *testing.B) { benchmarkFunVV(b, addVV, 2) } +func BenchmarkAddVV_3(b *testing.B) { benchmarkFunVV(b, addVV, 3) } +func BenchmarkAddVV_4(b *testing.B) { benchmarkFunVV(b, addVV, 4) } +func BenchmarkAddVV_5(b *testing.B) { benchmarkFunVV(b, addVV, 5) } +func BenchmarkAddVV_1e1(b *testing.B) { benchmarkFunVV(b, addVV, 1e1) } +func BenchmarkAddVV_1e2(b *testing.B) { benchmarkFunVV(b, addVV, 1e2) } +func BenchmarkAddVV_1e3(b *testing.B) { benchmarkFunVV(b, addVV, 1e3) } +func BenchmarkAddVV_1e4(b *testing.B) { benchmarkFunVV(b, addVV, 1e4) } +func BenchmarkAddVV_1e5(b *testing.B) { benchmarkFunVV(b, addVV, 1e5) } + +type funVW func(z, x []Word, y Word) (c Word) +type argVW struct { + z, x nat + y Word + c Word +} + +var sumVW = []argVW{ + {}, + {nil, nil, 2, 2}, + {nat{0}, nat{0}, 0, 0}, + {nat{1}, nat{0}, 1, 0}, + {nat{1}, nat{1}, 0, 0}, + {nat{0}, nat{_M}, 1, 1}, + {nat{0, 0, 0, 0}, nat{_M, _M, _M, _M}, 1, 1}, +} + +var prodVW = []argVW{ + {}, + {nat{0}, nat{0}, 0, 0}, + {nat{0}, nat{_M}, 0, 0}, + {nat{0}, nat{0}, _M, 0}, + {nat{1}, nat{1}, 1, 0}, + {nat{22793}, nat{991}, 23, 0}, + {nat{0, 0, 0, 22793}, nat{0, 0, 0, 991}, 23, 0}, + {nat{0, 0, 0, 0}, nat{7893475, 7395495, 798547395, 68943}, 0, 0}, + {nat{0, 0, 0, 0}, nat{0, 0, 0, 0}, 894375984, 0}, + {nat{_M << 1 & _M}, nat{_M}, 1 << 1, _M >> (_W - 1)}, + {nat{_M << 7 & _M}, nat{_M}, 1 << 7, _M >> (_W - 7)}, + {nat{_M << 7 & _M, _M, _M, _M}, nat{_M, _M, _M, _M}, 1 << 7, _M >> (_W - 7)}, +} + +var lshVW = []argVW{ + {}, + {nat{0}, nat{0}, 0, 0}, + {nat{0}, nat{0}, 1, 0}, + {nat{0}, nat{0}, 20, 0}, + + {nat{_M}, nat{_M}, 0, 0}, + {nat{_M << 1 & _M}, nat{_M}, 1, 1}, + {nat{_M << 20 & _M}, nat{_M}, 20, _M >> (_W - 20)}, + + {nat{_M, _M, _M}, nat{_M, _M, _M}, 0, 0}, + {nat{_M << 1 & _M, _M, _M}, nat{_M, _M, _M}, 1, 1}, + {nat{_M << 20 & _M, _M, _M}, nat{_M, _M, _M}, 20, _M >> (_W - 20)}, +} + +var rshVW = []argVW{ + {}, + {nat{0}, nat{0}, 0, 0}, + {nat{0}, nat{0}, 1, 0}, + {nat{0}, nat{0}, 20, 0}, + + {nat{_M}, nat{_M}, 0, 0}, + {nat{_M >> 1}, nat{_M}, 1, _M << (_W - 1) & _M}, + {nat{_M >> 20}, nat{_M}, 20, _M << (_W - 20) & _M}, + + {nat{_M, _M, _M}, nat{_M, _M, _M}, 0, 0}, + {nat{_M, _M, _M >> 1}, nat{_M, _M, _M}, 1, _M << (_W - 1) & _M}, + {nat{_M, _M, _M >> 20}, nat{_M, _M, _M}, 20, _M << (_W - 20) & _M}, +} + +func testFunVW(t *testing.T, msg string, f funVW, a argVW) { + z := make(nat, len(a.z)) + c := f(z, a.x, a.y) + for i, zi := range z { + if zi != a.z[i] { + t.Errorf("%s%+v\n\tgot z[%d] = %#x; want %#x", msg, a, i, zi, a.z[i]) + break + } + } + if c != a.c { + t.Errorf("%s%+v\n\tgot c = %#x; want %#x", msg, a, c, a.c) + } +} + +func makeFunVW(f func(z, x []Word, s uint) (c Word)) funVW { + return func(z, x []Word, s Word) (c Word) { + return f(z, x, uint(s)) + } +} + +func TestFunVW(t *testing.T) { + for _, a := range sumVW { + arg := a + testFunVW(t, "addVW_g", addVW_g, arg) + testFunVW(t, "addVW", addVW, arg) + + arg = argVW{a.x, a.z, a.y, a.c} + testFunVW(t, "subVW_g", subVW_g, arg) + testFunVW(t, "subVW", subVW, arg) + } + + shlVW_g := makeFunVW(shlVU_g) + shlVW := makeFunVW(shlVU) + for _, a := range lshVW { + arg := a + testFunVW(t, "shlVU_g", shlVW_g, arg) + testFunVW(t, "shlVU", shlVW, arg) + } + + shrVW_g := makeFunVW(shrVU_g) + shrVW := makeFunVW(shrVU) + for _, a := range rshVW { + arg := a + testFunVW(t, "shrVU_g", shrVW_g, arg) + testFunVW(t, "shrVU", shrVW, arg) + } +} + +func benchmarkFunVW(b *testing.B, f funVW, n int) { + x := rndV(n) + y := rndW() + z := make([]Word, n) + b.SetBytes(int64(n * _S)) + b.ResetTimer() + for i := 0; i < b.N; i++ { + f(z, x, y) + } +} + +func BenchmarkAddVW_1(b *testing.B) { benchmarkFunVW(b, addVW, 1) } +func BenchmarkAddVW_2(b *testing.B) { benchmarkFunVW(b, addVW, 2) } +func BenchmarkAddVW_3(b *testing.B) { benchmarkFunVW(b, addVW, 3) } +func BenchmarkAddVW_4(b *testing.B) { benchmarkFunVW(b, addVW, 4) } +func BenchmarkAddVW_5(b *testing.B) { benchmarkFunVW(b, addVW, 5) } +func BenchmarkAddVW_1e1(b *testing.B) { benchmarkFunVW(b, addVW, 1e1) } +func BenchmarkAddVW_1e2(b *testing.B) { benchmarkFunVW(b, addVW, 1e2) } +func BenchmarkAddVW_1e3(b *testing.B) { benchmarkFunVW(b, addVW, 1e3) } +func BenchmarkAddVW_1e4(b *testing.B) { benchmarkFunVW(b, addVW, 1e4) } +func BenchmarkAddVW_1e5(b *testing.B) { benchmarkFunVW(b, addVW, 1e5) } + +type funVWW func(z, x []Word, y, r Word) (c Word) +type argVWW struct { + z, x nat + y, r Word + c Word +} + +var prodVWW = []argVWW{ + {}, + {nat{0}, nat{0}, 0, 0, 0}, + {nat{991}, nat{0}, 0, 991, 0}, + {nat{0}, nat{_M}, 0, 0, 0}, + {nat{991}, nat{_M}, 0, 991, 0}, + {nat{0}, nat{0}, _M, 0, 0}, + {nat{991}, nat{0}, _M, 991, 0}, + {nat{1}, nat{1}, 1, 0, 0}, + {nat{992}, nat{1}, 1, 991, 0}, + {nat{22793}, nat{991}, 23, 0, 0}, + {nat{22800}, nat{991}, 23, 7, 0}, + {nat{0, 0, 0, 22793}, nat{0, 0, 0, 991}, 23, 0, 0}, + {nat{7, 0, 0, 22793}, nat{0, 0, 0, 991}, 23, 7, 0}, + {nat{0, 0, 0, 0}, nat{7893475, 7395495, 798547395, 68943}, 0, 0, 0}, + {nat{991, 0, 0, 0}, nat{7893475, 7395495, 798547395, 68943}, 0, 991, 0}, + {nat{0, 0, 0, 0}, nat{0, 0, 0, 0}, 894375984, 0, 0}, + {nat{991, 0, 0, 0}, nat{0, 0, 0, 0}, 894375984, 991, 0}, + {nat{_M << 1 & _M}, nat{_M}, 1 << 1, 0, _M >> (_W - 1)}, + {nat{_M<<1&_M + 1}, nat{_M}, 1 << 1, 1, _M >> (_W - 1)}, + {nat{_M << 7 & _M}, nat{_M}, 1 << 7, 0, _M >> (_W - 7)}, + {nat{_M<<7&_M + 1<<6}, nat{_M}, 1 << 7, 1 << 6, _M >> (_W - 7)}, + {nat{_M << 7 & _M, _M, _M, _M}, nat{_M, _M, _M, _M}, 1 << 7, 0, _M >> (_W - 7)}, + {nat{_M<<7&_M + 1<<6, _M, _M, _M}, nat{_M, _M, _M, _M}, 1 << 7, 1 << 6, _M >> (_W - 7)}, +} + +func testFunVWW(t *testing.T, msg string, f funVWW, a argVWW) { + z := make(nat, len(a.z)) + c := f(z, a.x, a.y, a.r) + for i, zi := range z { + if zi != a.z[i] { + t.Errorf("%s%+v\n\tgot z[%d] = %#x; want %#x", msg, a, i, zi, a.z[i]) + break + } + } + if c != a.c { + t.Errorf("%s%+v\n\tgot c = %#x; want %#x", msg, a, c, a.c) + } +} + +// TODO(gri) mulAddVWW and divWVW are symmetric operations but +// their signature is not symmetric. Try to unify. + +type funWVW func(z []Word, xn Word, x []Word, y Word) (r Word) +type argWVW struct { + z nat + xn Word + x nat + y Word + r Word +} + +func testFunWVW(t *testing.T, msg string, f funWVW, a argWVW) { + z := make(nat, len(a.z)) + r := f(z, a.xn, a.x, a.y) + for i, zi := range z { + if zi != a.z[i] { + t.Errorf("%s%+v\n\tgot z[%d] = %#x; want %#x", msg, a, i, zi, a.z[i]) + break + } + } + if r != a.r { + t.Errorf("%s%+v\n\tgot r = %#x; want %#x", msg, a, r, a.r) + } +} + +func TestFunVWW(t *testing.T) { + for _, a := range prodVWW { + arg := a + testFunVWW(t, "mulAddVWW_g", mulAddVWW_g, arg) + testFunVWW(t, "mulAddVWW", mulAddVWW, arg) + + if a.y != 0 && a.r < a.y { + arg := argWVW{a.x, a.c, a.z, a.y, a.r} + testFunWVW(t, "divWVW_g", divWVW_g, arg) + testFunWVW(t, "divWVW", divWVW, arg) + } + } +} + +var mulWWTests = []struct { + x, y Word + q, r Word +}{ + {_M, _M, _M - 1, 1}, + // 32 bit only: {0xc47dfa8c, 50911, 0x98a4, 0x998587f4}, +} + +func TestMulWW(t *testing.T) { + for i, test := range mulWWTests { + q, r := mulWW_g(test.x, test.y) + if q != test.q || r != test.r { + t.Errorf("#%d got (%x, %x) want (%x, %x)", i, q, r, test.q, test.r) + } + } +} + +var mulAddWWWTests = []struct { + x, y, c Word + q, r Word +}{ + // TODO(agl): These will only work on 64-bit platforms. + // {15064310297182388543, 0xe7df04d2d35d5d80, 13537600649892366549, 13644450054494335067, 10832252001440893781}, + // {15064310297182388543, 0xdab2f18048baa68d, 13644450054494335067, 12869334219691522700, 14233854684711418382}, + {_M, _M, 0, _M - 1, 1}, + {_M, _M, _M, _M, 0}, +} + +func TestMulAddWWW(t *testing.T) { + for i, test := range mulAddWWWTests { + q, r := mulAddWWW_g(test.x, test.y, test.c) + if q != test.q || r != test.r { + t.Errorf("#%d got (%x, %x) want (%x, %x)", i, q, r, test.q, test.r) + } + } +} + +func benchmarkAddMulVVW(b *testing.B, n int) { + x := rndV(n) + y := rndW() + z := make([]Word, n) + b.SetBytes(int64(n * _W)) + b.ResetTimer() + for i := 0; i < b.N; i++ { + addMulVVW(z, x, y) + } +} + +func BenchmarkAddMulVVW_1(b *testing.B) { benchmarkAddMulVVW(b, 1) } +func BenchmarkAddMulVVW_2(b *testing.B) { benchmarkAddMulVVW(b, 2) } +func BenchmarkAddMulVVW_3(b *testing.B) { benchmarkAddMulVVW(b, 3) } +func BenchmarkAddMulVVW_4(b *testing.B) { benchmarkAddMulVVW(b, 4) } +func BenchmarkAddMulVVW_5(b *testing.B) { benchmarkAddMulVVW(b, 5) } +func BenchmarkAddMulVVW_1e1(b *testing.B) { benchmarkAddMulVVW(b, 1e1) } +func BenchmarkAddMulVVW_1e2(b *testing.B) { benchmarkAddMulVVW(b, 1e2) } +func BenchmarkAddMulVVW_1e3(b *testing.B) { benchmarkAddMulVVW(b, 1e3) } +func BenchmarkAddMulVVW_1e4(b *testing.B) { benchmarkAddMulVVW(b, 1e4) } +func BenchmarkAddMulVVW_1e5(b *testing.B) { benchmarkAddMulVVW(b, 1e5) } + +func testWordBitLen(t *testing.T, fname string, f func(Word) int) { + for i := 0; i <= _W; i++ { + x := Word(1) << uint(i-1) // i == 0 => x == 0 + n := f(x) + if n != i { + t.Errorf("got %d; want %d for %s(%#x)", n, i, fname, x) + } + } +} + +func TestWordBitLen(t *testing.T) { + testWordBitLen(t, "bitLen", bitLen) + testWordBitLen(t, "bitLen_g", bitLen_g) +} + +// runs b.N iterations of bitLen called on a Word containing (1 << nbits)-1. +func benchmarkBitLenN(b *testing.B, nbits uint) { + testword := Word((uint64(1) << nbits) - 1) + for i := 0; i < b.N; i++ { + bitLen(testword) + } +} + +// Individual bitLen tests. Numbers chosen to examine both sides +// of powers-of-two boundaries. +func BenchmarkBitLen0(b *testing.B) { benchmarkBitLenN(b, 0) } +func BenchmarkBitLen1(b *testing.B) { benchmarkBitLenN(b, 1) } +func BenchmarkBitLen2(b *testing.B) { benchmarkBitLenN(b, 2) } +func BenchmarkBitLen3(b *testing.B) { benchmarkBitLenN(b, 3) } +func BenchmarkBitLen4(b *testing.B) { benchmarkBitLenN(b, 4) } +func BenchmarkBitLen5(b *testing.B) { benchmarkBitLenN(b, 5) } +func BenchmarkBitLen8(b *testing.B) { benchmarkBitLenN(b, 8) } +func BenchmarkBitLen9(b *testing.B) { benchmarkBitLenN(b, 9) } +func BenchmarkBitLen16(b *testing.B) { benchmarkBitLenN(b, 16) } +func BenchmarkBitLen17(b *testing.B) { benchmarkBitLenN(b, 17) } +func BenchmarkBitLen31(b *testing.B) { benchmarkBitLenN(b, 31) } diff --git a/src/cmd/compile/internal/big/bits_test.go b/src/cmd/compile/internal/big/bits_test.go new file mode 100644 index 0000000000..3ce24222d7 --- /dev/null +++ b/src/cmd/compile/internal/big/bits_test.go @@ -0,0 +1,224 @@ +// 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. + +// This file implements the Bits type used for testing Float operations +// via an independent (albeit slower) representations for floating-point +// numbers. + +package big + +import ( + "fmt" + "sort" + "testing" +) + +// A Bits value b represents a finite floating-point number x of the form +// +// x = 2**b[0] + 2**b[1] + ... 2**b[len(b)-1] +// +// The order of slice elements is not significant. Negative elements may be +// used to form fractions. A Bits value is normalized if each b[i] occurs at +// most once. For instance Bits{0, 0, 1} is not normalized but represents the +// same floating-point number as Bits{2}, which is normalized. The zero (nil) +// value of Bits is a ready to use Bits value and represents the value 0. +type Bits []int + +func (x Bits) add(y Bits) Bits { + return append(x, y...) +} + +func (x Bits) mul(y Bits) Bits { + var p Bits + for _, x := range x { + for _, y := range y { + p = append(p, x+y) + } + } + return p +} + +func TestMulBits(t *testing.T) { + for _, test := range []struct { + x, y, want Bits + }{ + {nil, nil, nil}, + {Bits{}, Bits{}, nil}, + {Bits{0}, Bits{0}, Bits{0}}, + {Bits{0}, Bits{1}, Bits{1}}, + {Bits{1}, Bits{1, 2, 3}, Bits{2, 3, 4}}, + {Bits{-1}, Bits{1}, Bits{0}}, + {Bits{-10, -1, 0, 1, 10}, Bits{1, 2, 3}, Bits{-9, -8, -7, 0, 1, 2, 1, 2, 3, 2, 3, 4, 11, 12, 13}}, + } { + got := fmt.Sprintf("%v", test.x.mul(test.y)) + want := fmt.Sprintf("%v", test.want) + if got != want { + t.Errorf("%v * %v = %s; want %s", test.x, test.y, got, want) + } + + } +} + +// norm returns the normalized bits for x: It removes multiple equal entries +// by treating them as an addition (e.g., Bits{5, 5} => Bits{6}), and it sorts +// the result list for reproducible results. +func (x Bits) norm() Bits { + m := make(map[int]bool) + for _, b := range x { + for m[b] { + m[b] = false + b++ + } + m[b] = true + } + var z Bits + for b, set := range m { + if set { + z = append(z, b) + } + } + sort.Ints([]int(z)) + return z +} + +func TestNormBits(t *testing.T) { + for _, test := range []struct { + x, want Bits + }{ + {nil, nil}, + {Bits{}, Bits{}}, + {Bits{0}, Bits{0}}, + {Bits{0, 0}, Bits{1}}, + {Bits{3, 1, 1}, Bits{2, 3}}, + {Bits{10, 9, 8, 7, 6, 6}, Bits{11}}, + } { + got := fmt.Sprintf("%v", test.x.norm()) + want := fmt.Sprintf("%v", test.want) + if got != want { + t.Errorf("normBits(%v) = %s; want %s", test.x, got, want) + } + + } +} + +// round returns the Float value corresponding to x after rounding x +// to prec bits according to mode. +func (x Bits) round(prec uint, mode RoundingMode) *Float { + x = x.norm() + + // determine range + var min, max int + for i, b := range x { + if i == 0 || b < min { + min = b + } + if i == 0 || b > max { + max = b + } + } + prec0 := uint(max + 1 - min) + if prec >= prec0 { + return x.Float() + } + // prec < prec0 + + // determine bit 0, rounding, and sticky bit, and result bits z + var bit0, rbit, sbit uint + var z Bits + r := max - int(prec) + for _, b := range x { + switch { + case b == r: + rbit = 1 + case b < r: + sbit = 1 + default: + // b > r + if b == r+1 { + bit0 = 1 + } + z = append(z, b) + } + } + + // round + f := z.Float() // rounded to zero + if mode == ToNearestAway { + panic("not yet implemented") + } + if mode == ToNearestEven && rbit == 1 && (sbit == 1 || sbit == 0 && bit0 != 0) || mode == AwayFromZero { + // round away from zero + f.SetMode(ToZero).SetPrec(prec) + f.Add(f, Bits{int(r) + 1}.Float()) + } + return f +} + +// Float returns the *Float z of the smallest possible precision such that +// z = sum(2**bits[i]), with i = range bits. If multiple bits[i] are equal, +// they are added: Bits{0, 1, 0}.Float() == 2**0 + 2**1 + 2**0 = 4. +func (bits Bits) Float() *Float { + // handle 0 + if len(bits) == 0 { + return new(Float) + } + // len(bits) > 0 + + // determine lsb exponent + var min int + for i, b := range bits { + if i == 0 || b < min { + min = b + } + } + + // create bit pattern + x := NewInt(0) + for _, b := range bits { + badj := b - min + // propagate carry if necessary + for x.Bit(badj) != 0 { + x.SetBit(x, badj, 0) + badj++ + } + x.SetBit(x, badj, 1) + } + + // create corresponding float + z := new(Float).SetInt(x) // normalized + if e := int64(z.exp) + int64(min); MinExp <= e && e <= MaxExp { + z.exp = int32(e) + } else { + // this should never happen for our test cases + panic("exponent out of range") + } + return z +} + +func TestFromBits(t *testing.T) { + for _, test := range []struct { + bits Bits + want string + }{ + // 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"}, + + // 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 */}, + } { + f := test.bits.Float() + if got := f.Format('p', 0); got != test.want { + t.Errorf("setBits(%v) = %s; want %s", test.bits, got, test.want) + } + } +} diff --git a/src/cmd/compile/internal/big/calibrate_test.go b/src/cmd/compile/internal/big/calibrate_test.go new file mode 100644 index 0000000000..f69ffbf5cf --- /dev/null +++ b/src/cmd/compile/internal/big/calibrate_test.go @@ -0,0 +1,88 @@ +// Copyright 2009 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. + +// This file prints execution times for the Mul benchmark +// given different Karatsuba thresholds. The result may be +// used to manually fine-tune the threshold constant. The +// results are somewhat fragile; use repeated runs to get +// a clear picture. + +// Usage: go test -run=TestCalibrate -calibrate + +package big + +import ( + "flag" + "fmt" + "testing" + "time" +) + +var calibrate = flag.Bool("calibrate", false, "run calibration test") + +func karatsubaLoad(b *testing.B) { + BenchmarkMul(b) +} + +// measureKaratsuba returns the time to run a Karatsuba-relevant benchmark +// given Karatsuba threshold th. +func measureKaratsuba(th int) time.Duration { + th, karatsubaThreshold = karatsubaThreshold, th + res := testing.Benchmark(karatsubaLoad) + karatsubaThreshold = th + return time.Duration(res.NsPerOp()) +} + +func computeThresholds() { + fmt.Printf("Multiplication times for varying Karatsuba thresholds\n") + fmt.Printf("(run repeatedly for good results)\n") + + // determine Tk, the work load execution time using basic multiplication + Tb := measureKaratsuba(1e9) // th == 1e9 => Karatsuba multiplication disabled + fmt.Printf("Tb = %10s\n", Tb) + + // thresholds + th := 4 + th1 := -1 + th2 := -1 + + var deltaOld time.Duration + for count := -1; count != 0 && th < 128; count-- { + // determine Tk, the work load execution time using Karatsuba multiplication + Tk := measureKaratsuba(th) + + // improvement over Tb + delta := (Tb - Tk) * 100 / Tb + + fmt.Printf("th = %3d Tk = %10s %4d%%", th, Tk, delta) + + // determine break-even point + if Tk < Tb && th1 < 0 { + th1 = th + fmt.Print(" break-even point") + } + + // determine diminishing return + if 0 < delta && delta < deltaOld && th2 < 0 { + th2 = th + fmt.Print(" diminishing return") + } + deltaOld = delta + + fmt.Println() + + // trigger counter + if th1 >= 0 && th2 >= 0 && count < 0 { + count = 10 // this many extra measurements after we got both thresholds + } + + th++ + } +} + +func TestCalibrate(t *testing.T) { + if *calibrate { + computeThresholds() + } +} diff --git a/src/cmd/compile/internal/big/decimal.go b/src/cmd/compile/internal/big/decimal.go new file mode 100644 index 0000000000..3d024dce68 --- /dev/null +++ b/src/cmd/compile/internal/big/decimal.go @@ -0,0 +1,258 @@ +// 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. + +// This file implements multi-precision decimal numbers. +// The implementation is for float to decimal conversion only; +// not general purpose use. +// The only operations are precise conversion from binary to +// decimal and rounding. +// +// The key observation and some code (shr) is borrowed from +// strconv/decimal.go: conversion of binary fractional values can be done +// precisely in multi-precision decimal because 2 divides 10 (required for +// >> of mantissa); but conversion of decimal floating-point values cannot +// be done precisely in binary representation. +// +// In contrast to strconv/decimal.go, only right shift is implemented in +// decimal format - left shift can be done precisely in binary format. + +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. +type decimal struct { + mant []byte // mantissa ASCII digits, big-endian + exp int // exponent, valid if len(mant) > 0 +} + +// Maximum shift amount that can be done in one pass without overflow. +// A Word has _W bits and (1<= 0), or m >> -shift (for shift < 0). +func (x *decimal) init(m nat, shift int) { + // special case 0 + if len(m) == 0 { + x.mant = x.mant[:0] + return + } + + // Optimization: If we need to shift right, first remove any trailing + // zero bits from m to reduce shift amount that needs to be done in + // decimal format (since that is likely slower). + if shift < 0 { + ntz := m.trailingZeroBits() + s := uint(-shift) + if s >= ntz { + s = ntz // shift at most ntz bits + } + m = nat(nil).shr(m, s) + shift += int(s) + } + + // Do any shift left in binary representation. + if shift > 0 { + m = nat(nil).shl(m, uint(shift)) + shift = 0 + } + + // Convert mantissa into decimal representation. + s := m.decimalString() // TODO(gri) avoid string conversion here + n := len(s) + x.exp = n + // Trim trailing zeros; instead the exponent is tracking + // the decimal point independent of the number of digits. + for n > 0 && s[n-1] == '0' { + n-- + } + x.mant = append(x.mant[:0], s[:n]...) + + // Do any (remaining) shift right in decimal representation. + if shift < 0 { + for shift < -maxShift { + shr(x, maxShift) + shift += maxShift + } + shr(x, uint(-shift)) + } +} + +// Possibly optimization: The current implementation of nat.string takes +// a charset argument. When a right shift is needed, we could provide +// "\x00\x01...\x09" instead of "012..9" (as in nat.decimalString) and +// avoid the repeated +'0' and -'0' operations in decimal.shr (and do a +// single +'0' pass at the end). + +// shr implements x >> s, for s <= maxShift. +func shr(x *decimal, s uint) { + // Division by 1<>s == 0 && r < len(x.mant) { + ch := Word(x.mant[r]) + r++ + n = n*10 + ch - '0' + } + if n == 0 { + // x == 0; shouldn't get here, but handle anyway + x.mant = x.mant[:0] + return + } + for n>>s == 0 { + r++ + n *= 10 + } + x.exp += 1 - r + + // read a digit, write a digit + w := 0 // write index + for r < len(x.mant) { + ch := Word(x.mant[r]) + r++ + d := n >> s + n -= d << s + x.mant[w] = byte(d + '0') + w++ + n = n*10 + ch - '0' + } + + // write extra digits that still fit + for n > 0 && w < len(x.mant) { + d := n >> s + n -= d << s + x.mant[w] = byte(d + '0') + w++ + n = n * 10 + } + x.mant = x.mant[:w] // the number may be shorter (e.g. 1024 >> 10) + + // append additional digits that didn't fit + for n > 0 { + d := n >> s + n -= d << s + x.mant = append(x.mant, byte(d+'0')) + n = n * 10 + } + + trim(x) +} + +func (x *decimal) String() string { + if len(x.mant) == 0 { + return "0" + } + + var buf []byte + switch { + case x.exp <= 0: + // 0.00ddd + buf = append(buf, "0."...) + buf = appendZeros(buf, -x.exp) + buf = append(buf, x.mant...) + + case /* 0 < */ x.exp < len(x.mant): + // dd.ddd + buf = append(buf, x.mant[:x.exp]...) + buf = append(buf, '.') + buf = append(buf, x.mant[x.exp:]...) + + default: // len(x.mant) <= x.exp + // ddd00 + buf = append(buf, x.mant...) + buf = appendZeros(buf, x.exp-len(x.mant)) + } + + return string(buf) +} + +// appendZeros appends n 0 digits to buf and returns buf. +func appendZeros(buf []byte, n int) []byte { + for ; n > 0; n-- { + buf = append(buf, '0') + } + return buf +} + +// shouldRoundUp reports if x should be rounded up +// if shortened to n digits. n must be a valid index +// for x.mant. +func shouldRoundUp(x *decimal, n int) bool { + if x.mant[n] == '5' && n+1 == len(x.mant) { + // exactly halfway - round to even + return n > 0 && (x.mant[n-1]-'0')&1 != 0 + } + // not halfway - digit tells all (x.mant has no trailing zeros) + return x.mant[n] >= '5' +} + +// round sets x to (at most) n mantissa digits by rounding it +// to the nearest even value with n (or fever) mantissa digits. +// If n < 0, x remains unchanged. +func (x *decimal) round(n int) { + if n < 0 || n >= len(x.mant) { + return // nothing to do + } + + if shouldRoundUp(x, n) { + x.roundUp(n) + } else { + x.roundDown(n) + } +} + +func (x *decimal) roundUp(n int) { + if n < 0 || n >= len(x.mant) { + return // nothing to do + } + // 0 <= n < len(x.mant) + + // find first digit < '9' + for n > 0 && x.mant[n-1] >= '9' { + n-- + } + + if n == 0 { + // all digits are '9's => round up to '1' and update exponent + x.mant[0] = '1' // ok since len(x.mant) > n + x.mant = x.mant[:1] + x.exp++ + return + } + + // n > 0 && x.mant[n-1] < '9' + x.mant[n-1]++ + x.mant = x.mant[:n] + // x already trimmed +} + +func (x *decimal) roundDown(n int) { + if n < 0 || n >= len(x.mant) { + return // nothing to do + } + x.mant = x.mant[:n] + trim(x) +} + +// trim cuts off any trailing zeros from x's mantissa; +// they are meaningless for the value of x. +func trim(x *decimal) { + i := len(x.mant) + for i > 0 && x.mant[i-1] == '0' { + i-- + } + x.mant = x.mant[:i] +} diff --git a/src/cmd/compile/internal/big/decimal_test.go b/src/cmd/compile/internal/big/decimal_test.go new file mode 100644 index 0000000000..81e022a47d --- /dev/null +++ b/src/cmd/compile/internal/big/decimal_test.go @@ -0,0 +1,106 @@ +// 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 big + +import "testing" + +func TestDecimalString(t *testing.T) { + for _, test := range []struct { + x decimal + want string + }{ + {want: "0"}, + {decimal{nil, 1000}, "0"}, // exponent of 0 is ignored + {decimal{[]byte("12345"), 0}, "0.12345"}, + {decimal{[]byte("12345"), -3}, "0.00012345"}, + {decimal{[]byte("12345"), +3}, "123.45"}, + {decimal{[]byte("12345"), +10}, "1234500000"}, + } { + if got := test.x.String(); got != test.want { + t.Errorf("%v == %s; want %s", test.x, got, test.want) + } + } +} + +func TestDecimalInit(t *testing.T) { + for _, test := range []struct { + x Word + shift int + want string + }{ + {0, 0, "0"}, + {0, -100, "0"}, + {0, 100, "0"}, + {1, 0, "1"}, + {1, 10, "1024"}, + {1, 100, "1267650600228229401496703205376"}, + {1, -100, "0.0000000000000000000000000000007888609052210118054117285652827862296732064351090230047702789306640625"}, + {12345678, 8, "3160493568"}, + {12345678, -8, "48225.3046875"}, + {195312, 9, "99999744"}, + {1953125, 9, "1000000000"}, + } { + var d decimal + d.init(nat{test.x}.norm(), test.shift) + if got := d.String(); got != test.want { + t.Errorf("%d << %d == %s; want %s", test.x, test.shift, got, test.want) + } + } +} + +func TestDecimalRounding(t *testing.T) { + for _, test := range []struct { + x uint64 + n int + down, even, up string + }{ + {0, 0, "0", "0", "0"}, + {0, 1, "0", "0", "0"}, + + {1, 0, "0", "0", "10"}, + {5, 0, "0", "0", "10"}, + {9, 0, "0", "10", "10"}, + + {15, 1, "10", "20", "20"}, + {45, 1, "40", "40", "50"}, + {95, 1, "90", "100", "100"}, + + {12344999, 4, "12340000", "12340000", "12350000"}, + {12345000, 4, "12340000", "12340000", "12350000"}, + {12345001, 4, "12340000", "12350000", "12350000"}, + {23454999, 4, "23450000", "23450000", "23460000"}, + {23455000, 4, "23450000", "23460000", "23460000"}, + {23455001, 4, "23450000", "23460000", "23460000"}, + + {99994999, 4, "99990000", "99990000", "100000000"}, + {99995000, 4, "99990000", "100000000", "100000000"}, + {99999999, 4, "99990000", "100000000", "100000000"}, + + {12994999, 4, "12990000", "12990000", "13000000"}, + {12995000, 4, "12990000", "13000000", "13000000"}, + {12999999, 4, "12990000", "13000000", "13000000"}, + } { + x := nat(nil).setUint64(test.x) + + var d decimal + d.init(x, 0) + d.roundDown(test.n) + if got := d.String(); got != test.down { + t.Errorf("roundDown(%d, %d) = %s; want %s", test.x, test.n, got, test.down) + } + + d.init(x, 0) + d.round(test.n) + if got := d.String(); got != test.even { + t.Errorf("round(%d, %d) = %s; want %s", test.x, test.n, got, test.even) + } + + d.init(x, 0) + d.roundUp(test.n) + if got := d.String(); got != test.up { + t.Errorf("roundUp(%d, %d) = %s; want %s", test.x, test.n, got, test.up) + } + } +} diff --git a/src/cmd/compile/internal/big/example_test.go b/src/cmd/compile/internal/big/example_test.go new file mode 100644 index 0000000000..078be47f95 --- /dev/null +++ b/src/cmd/compile/internal/big/example_test.go @@ -0,0 +1,51 @@ +// Copyright 2012 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 big_test + +import ( + "fmt" + "log" + "math/big" +) + +func ExampleRat_SetString() { + r := new(big.Rat) + r.SetString("355/113") + fmt.Println(r.FloatString(3)) + // Output: 3.142 +} + +func ExampleInt_SetString() { + i := new(big.Int) + i.SetString("644", 8) // octal + fmt.Println(i) + // Output: 420 +} + +func ExampleRat_Scan() { + // The Scan function is rarely used directly; + // the fmt package recognizes it as an implementation of fmt.Scanner. + r := new(big.Rat) + _, err := fmt.Sscan("1.5000", r) + if err != nil { + log.Println("error scanning value:", err) + } else { + fmt.Println(r) + } + // Output: 3/2 +} + +func ExampleInt_Scan() { + // The Scan function is rarely used directly; + // the fmt package recognizes it as an implementation of fmt.Scanner. + i := new(big.Int) + _, err := fmt.Sscan("18446744073709551617", i) + if err != nil { + log.Println("error scanning value:", err) + } else { + fmt.Println(i) + } + // Output: 18446744073709551617 +} diff --git a/src/cmd/compile/internal/big/float.go b/src/cmd/compile/internal/big/float.go new file mode 100644 index 0000000000..ed55e8e513 --- /dev/null +++ b/src/cmd/compile/internal/big/float.go @@ -0,0 +1,1681 @@ +// Copyright 2014 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. + +// This file implements multi-precision floating-point numbers. +// Like in the GNU MPFR library (http://www.mpfr.org/), operands +// can be of mixed precision. Unlike MPFR, the rounding mode is +// not specified with each operation, but with each operand. The +// rounding mode of the result operand determines the rounding +// mode of an operation. This is a from-scratch implementation. + +package big + +import ( + "fmt" + "math" +) + +const debugFloat = true // enable for debugging + +// A nonzero finite Float represents a multi-precision floating point number +// +// sign × mantissa × 2**exponent +// +// with 0.5 <= mantissa < 1.0, and MinExp <= exponent <= MaxExp. +// A Float may also be zero (+0, -0) or infinite (+Inf, -Inf). +// All Floats are ordered, and the ordering of two Floats x and y +// is defined by x.Cmp(y). +// +// Each Float value also has a precision, rounding mode, and accuracy. +// The precision is the maximum number of mantissa bits available to +// represent the value. The rounding mode specifies how a result should +// be rounded to fit into the mantissa bits, and accuracy describes the +// rounding error with respect to the exact result. +// +// Unless specified otherwise, all operations (including setters) that +// specify a *Float variable for the result (usually via the receiver +// with the exception of MantExp), round the numeric result according +// to the precision and rounding mode of the result variable. +// +// If the provided result precision is 0 (see below), it is set to the +// precision of the argument with the largest precision value before any +// rounding takes place, and the rounding mode remains unchanged. Thus, +// uninitialized Floats provided as result arguments will have their +// precision set to a reasonable value determined by the operands and +// their mode is the zero value for RoundingMode (ToNearestEven). +// +// By setting the desired precision to 24 or 53 and using matching rounding +// mode (typically ToNearestEven), Float operations produce the same results +// as the corresponding float32 or float64 IEEE-754 arithmetic for operands +// that correspond to normal (i.e., not denormal) float32 or float64 numbers. +// Exponent underflow and overflow lead to a 0 or an Infinity for different +// values than IEEE-754 because Float exponents have a much larger range. +// +// The zero (uninitialized) value for a Float is ready to use and represents +// the number +0.0 exactly, with precision 0 and rounding mode ToNearestEven. +// +type Float struct { + prec uint32 + mode RoundingMode + acc Accuracy + form form + neg bool + mant nat + exp int32 +} + +// Float operations that would lead to a NaN under IEEE-754 rules cause +// a run-time panic of ErrNaN type. +type ErrNaN struct { + msg string +} + +// 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. +func NewFloat(x float64) *Float { + if math.IsNaN(x) { + panic(ErrNaN{"NewFloat(NaN)"}) + } + return new(Float).SetFloat64(x) +} + +// Exponent and precision limits. +const ( + MaxExp = math.MaxInt32 // largest supported exponent + MinExp = math.MinInt32 // smallest supported exponent + MaxPrec = math.MaxUint32 // largest (theoretically) supported precision; likely memory-limited +) + +// Internal representation: The mantissa bits x.mant of a nonzero finite +// Float x are stored in a nat slice long enough to hold up to x.prec bits; +// the slice may (but doesn't have to) be shorter if the mantissa contains +// trailing 0 bits. x.mant is normalized if the msb of x.mant == 1 (i.e., +// the msb is shifted all the way "to the left"). Thus, if the mantissa has +// trailing 0 bits or x.prec is not a multiple of the the Word size _W, +// x.mant[0] has trailing zero bits. The msb of the mantissa corresponds +// to the value 0.5; the exponent x.exp shifts the binary point as needed. +// +// A zero or non-finite Float x ignores x.mant and x.exp. +// +// x form neg mant exp +// ---------------------------------------------------------- +// ±0 zero sign - - +// 0 < |x| < +Inf finite sign mantissa exponent +// ±Inf inf sign - - + +// A form value describes the internal representation. +type form byte + +// The form value order is relevant - do not change! +const ( + zero form = iota + finite + inf +) + +// RoundingMode determines how a Float value is rounded to the +// desired precision. Rounding may change the Float value; the +// rounding error is described by the Float's Accuracy. +type RoundingMode byte + +// The following rounding modes are supported. +const ( + ToNearestEven RoundingMode = iota // == IEEE 754-2008 roundTiesToEven + ToNearestAway // == IEEE 754-2008 roundTiesToAway + ToZero // == IEEE 754-2008 roundTowardZero + AwayFromZero // no IEEE 754-2008 equivalent + ToNegativeInf // == IEEE 754-2008 roundTowardNegative + ToPositiveInf // == IEEE 754-2008 roundTowardPositive +) + +//go:generate stringer -type=RoundingMode + +// Accuracy describes the rounding error produced by the most recent +// operation that generated a Float value, relative to the exact value. +type Accuracy int8 + +// Constants describing the Accuracy of a Float. +const ( + Below Accuracy = -1 + Exact Accuracy = 0 + Above Accuracy = +1 +) + +//go:generate stringer -type=Accuracy + +// SetPrec sets z's precision to prec and returns the (possibly) rounded +// value of z. Rounding occurs according to z's rounding mode if the mantissa +// cannot be represented in prec bits without loss of precision. +// SetPrec(0) maps all finite values to ±0; infinite values remain unchanged. +// If prec > MaxPrec, it is set to MaxPrec. +func (z *Float) SetPrec(prec uint) *Float { + z.acc = Exact // optimistically assume no rounding is needed + + // special case + if prec == 0 { + z.prec = 0 + if z.form == finite { + // truncate z to 0 + z.acc = makeAcc(z.neg) + z.form = zero + } + return z + } + + // general case + if prec > MaxPrec { + prec = MaxPrec + } + old := z.prec + z.prec = uint32(prec) + if z.prec < old { + z.round(0) + } + return z +} + +func makeAcc(above bool) Accuracy { + if above { + return Above + } + return Below +} + +// SetMode sets z's rounding mode to mode and returns an exact z. +// z remains unchanged otherwise. +// z.SetMode(z.Mode()) is a cheap way to set z's accuracy to Exact. +func (z *Float) SetMode(mode RoundingMode) *Float { + z.mode = mode + z.acc = Exact + return z +} + +// Prec returns the mantissa precision of x in bits. +// The result may be 0 for |x| == 0 and |x| == Inf. +func (x *Float) Prec() uint { + return uint(x.prec) +} + +// MinPrec returns the minimum precision required to represent x exactly +// (i.e., the smallest prec before x.SetPrec(prec) would start rounding x). +// The result is 0 for |x| == 0 and |x| == Inf. +func (x *Float) MinPrec() uint { + if x.form != finite { + return 0 + } + return uint(len(x.mant))*_W - x.mant.trailingZeroBits() +} + +// Mode returns the rounding mode of x. +func (x *Float) Mode() RoundingMode { + return x.mode +} + +// Acc returns the accuracy of x produced by the most recent operation. +func (x *Float) Acc() Accuracy { + return x.acc +} + +// Sign returns: +// +// -1 if x < 0 +// 0 if x is ±0 +// +1 if x > 0 +// +func (x *Float) Sign() int { + if debugFloat { + x.validate() + } + if x.form == zero { + return 0 + } + if x.neg { + return -1 + } + return 1 +} + +// MantExp breaks x into its mantissa and exponent components +// and returns the exponent. If a non-nil mant argument is +// provided its value is set to the mantissa of x, with the +// same precision and rounding mode as x. The components +// satisfy x == mant × 2**exp, with 0.5 <= |mant| < 1.0. +// Calling MantExp with a nil argument is an efficient way to +// get the exponent of the receiver. +// +// Special cases are: +// +// ( ±0).MantExp(mant) = 0, with mant set to ±0 +// (±Inf).MantExp(mant) = 0, with mant set to ±Inf +// +// x and mant may be the same in which case x is set to its +// mantissa value. +func (x *Float) MantExp(mant *Float) (exp int) { + if debugFloat { + x.validate() + } + if x.form == finite { + exp = int(x.exp) + } + if mant != nil { + mant.Copy(x) + if mant.form == finite { + mant.exp = 0 + } + } + return +} + +func (z *Float) setExpAndRound(exp int64, sbit uint) { + if exp < MinExp { + // underflow + z.acc = makeAcc(z.neg) + z.form = zero + return + } + + if exp > MaxExp { + // overflow + z.acc = makeAcc(!z.neg) + z.form = inf + return + } + + z.form = finite + z.exp = int32(exp) + z.round(sbit) +} + +// SetMantExp sets z to mant × 2**exp and and returns z. +// The result z has the same precision and rounding mode +// as mant. SetMantExp is an inverse of MantExp but does +// not require 0.5 <= |mant| < 1.0. Specifically: +// +// mant := new(Float) +// new(Float).SetMantExp(mant, x.SetMantExp(mant)).Cmp(x).Eql() is true +// +// Special cases are: +// +// z.SetMantExp( ±0, exp) = ±0 +// z.SetMantExp(±Inf, exp) = ±Inf +// +// z and mant may be the same in which case z's exponent +// is set to exp. +func (z *Float) SetMantExp(mant *Float, exp int) *Float { + if debugFloat { + z.validate() + mant.validate() + } + z.Copy(mant) + if z.form != finite { + return z + } + z.setExpAndRound(int64(z.exp)+int64(exp), 0) + return z +} + +// Signbit returns true if x is negative or negative zero. +func (x *Float) Signbit() bool { + return x.neg +} + +// IsInf reports whether x is +Inf or -Inf. +func (x *Float) IsInf() bool { + return x.form == inf +} + +// IsInt reports whether x is an integer. +// ±Inf values are not integers. +func (x *Float) IsInt() bool { + if debugFloat { + x.validate() + } + // special cases + if x.form != finite { + return x.form == zero + } + // x.form == finite + if x.exp <= 0 { + return false + } + // x.exp > 0 + return x.prec <= uint32(x.exp) || x.MinPrec() <= uint(x.exp) // not enough bits for fractional mantissa +} + +// debugging support +func (x *Float) validate() { + if !debugFloat { + // avoid performance bugs + panic("validate called but debugFloat is not set") + } + if x.form != finite { + return + } + m := len(x.mant) + if m == 0 { + panic("nonzero finite number with empty mantissa") + } + const msb = 1 << (_W - 1) + if x.mant[m-1]&msb == 0 { + panic(fmt.Sprintf("msb not set in last word %#x of %s", x.mant[m-1], x.Format('p', 0))) + } + if x.prec == 0 { + panic("zero precision finite number") + } +} + +// round rounds z according to z.mode to z.prec bits and sets z.acc accordingly. +// sbit must be 0 or 1 and summarizes any "sticky bit" information one might +// have before calling round. z's mantissa must be normalized (with the msb set) +// or empty. +// +// CAUTION: The rounding modes ToNegativeInf, ToPositiveInf are affected by the +// sign of z. For correct rounding, the sign of z must be set correctly before +// calling round. +func (z *Float) round(sbit uint) { + if debugFloat { + z.validate() + if z.form > finite { + panic(fmt.Sprintf("round called for non-finite value %s", z)) + } + } + // z.form <= finite + + z.acc = Exact + if z.form == zero { + return + } + // z.form == finite && len(z.mant) > 0 + // m > 0 implies z.prec > 0 (checked by validate) + + m := uint32(len(z.mant)) // present mantissa length in words + bits := m * _W // present mantissa bits + if bits <= z.prec { + // mantissa fits => nothing to do + return + } + // bits > z.prec + + n := (z.prec + (_W - 1)) / _W // mantissa length in words for desired precision + + // Rounding is based on two bits: the rounding bit (rbit) and the + // sticky bit (sbit). The rbit is the bit immediately before the + // z.prec leading mantissa bits (the "0.5"). The sbit is set if any + // of the bits before the rbit are set (the "0.25", "0.125", etc.): + // + // rbit sbit => "fractional part" + // + // 0 0 == 0 + // 0 1 > 0 , < 0.5 + // 1 0 == 0.5 + // 1 1 > 0.5, < 1.0 + + // bits > z.prec: mantissa too large => round + r := uint(bits - z.prec - 1) // rounding bit position; r >= 0 + rbit := z.mant.bit(r) // rounding bit + if sbit == 0 { + sbit = z.mant.sticky(r) + } + if debugFloat && sbit&^1 != 0 { + panic(fmt.Sprintf("invalid sbit %#x", sbit)) + } + + // convert ToXInf rounding modes + mode := z.mode + switch mode { + case ToNegativeInf: + mode = ToZero + if z.neg { + mode = AwayFromZero + } + case ToPositiveInf: + mode = AwayFromZero + if z.neg { + mode = ToZero + } + } + + // cut off extra words + if m > n { + copy(z.mant, z.mant[m-n:]) // move n last words to front + z.mant = z.mant[:n] + } + + // determine number of trailing zero bits t + t := n*_W - z.prec // 0 <= t < _W + lsb := Word(1) << t + + // make rounding decision + // TODO(gri) This can be simplified (see Bits.round in bits_test.go). + switch mode { + case ToZero: + // nothing to do + case ToNearestEven, ToNearestAway: + if rbit == 0 { + // rounding bits == 0b0x + mode = ToZero + } else if sbit == 1 { + // rounding bits == 0b11 + mode = AwayFromZero + } + case AwayFromZero: + if rbit|sbit == 0 { + mode = ToZero + } + default: + // ToXInf modes have been converted to ToZero or AwayFromZero + panic("unreachable") + } + + // round and determine accuracy + switch mode { + case ToZero: + if rbit|sbit != 0 { + z.acc = Below + } + + case ToNearestEven, ToNearestAway: + if debugFloat && rbit != 1 { + panic("internal error in rounding") + } + if mode == ToNearestEven && sbit == 0 && z.mant[0]&lsb == 0 { + z.acc = Below + break + } + // mode == ToNearestAway || sbit == 1 || z.mant[0]&lsb != 0 + fallthrough + + case AwayFromZero: + // add 1 to mantissa + if addVW(z.mant, z.mant, lsb) != 0 { + // overflow => shift mantissa right by 1 and add msb + shrVU(z.mant, z.mant, 1) + z.mant[n-1] |= 1 << (_W - 1) + // adjust exponent + if z.exp < MaxExp { + z.exp++ + } else { + // exponent overflow + z.acc = makeAcc(!z.neg) + z.form = inf + return + } + } + z.acc = Above + } + + // zero out trailing bits in least-significant word + z.mant[0] &^= lsb - 1 + + // update accuracy + if z.acc != Exact && z.neg { + z.acc = -z.acc + } + + if debugFloat { + z.validate() + } + + 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 + } + z.acc = Exact + z.neg = neg + if x == 0 { + z.form = zero + return z + } + // x != 0 + z.form = finite + s := nlz64(x) + z.mant = z.mant.setUint64(x << s) + z.exp = int32(64 - s) // always fits + if z.prec < 64 { + z.round(0) + } + return z +} + +// SetUint64 sets z to the (possibly rounded) value of x and returns z. +// If z's precision is 0, it is changed to 64 (and rounding will have +// no effect). +func (z *Float) SetUint64(x uint64) *Float { + return z.setBits64(false, x) +} + +// SetInt64 sets z to the (possibly rounded) value of x and returns z. +// If z's precision is 0, it is changed to 64 (and rounding will have +// no effect). +func (z *Float) SetInt64(x int64) *Float { + u := x + if u < 0 { + u = -u + } + // We cannot simply call z.SetUint64(uint64(u)) and change + // the sign afterwards because the sign affects rounding. + return z.setBits64(x < 0, uint64(u)) +} + +// SetFloat64 sets z to the (possibly rounded) value of x and returns z. +// If z's precision is 0, it is changed to 53 (and rounding will have +// no effect). SetFloat64 panics with ErrNaN if x is a NaN. +func (z *Float) SetFloat64(x float64) *Float { + if z.prec == 0 { + z.prec = 53 + } + if math.IsNaN(x) { + panic(ErrNaN{"Float.SetFloat64(NaN)"}) + } + z.acc = Exact + z.neg = math.Signbit(x) // handle -0, -Inf correctly + if x == 0 { + z.form = zero + return z + } + if math.IsInf(x, 0) { + z.form = inf + return z + } + // normalized x != 0 + z.form = finite + fmant, exp := math.Frexp(x) // get normalized mantissa + z.mant = z.mant.setUint64(1<<63 | math.Float64bits(fmant)<<11) + z.exp = int32(exp) // always fits + if z.prec < 53 { + z.round(0) + } + return z +} + +// fnorm normalizes mantissa m by shifting it to the left +// such that the msb of the most-significant word (msw) is 1. +// It returns the shift amount. It assumes that len(m) != 0. +func fnorm(m nat) int64 { + if debugFloat && (len(m) == 0 || m[len(m)-1] == 0) { + panic("msw of mantissa is 0") + } + s := nlz(m[len(m)-1]) + if s > 0 { + c := shlVU(m, m, s) + if debugFloat && c != 0 { + panic("nlz or shlVU incorrect") + } + } + return int64(s) +} + +// SetInt sets z to the (possibly rounded) value of x and returns z. +// If z's precision is 0, it is changed to the larger of x.BitLen() +// or 64 (and rounding will have no effect). +func (z *Float) SetInt(x *Int) *Float { + // TODO(gri) can be more efficient if z.prec > 0 + // but small compared to the size of x, or if there + // are many trailing 0's. + bits := uint32(x.BitLen()) + if z.prec == 0 { + z.prec = umax32(bits, 64) + } + z.acc = Exact + z.neg = x.neg + if len(x.abs) == 0 { + z.form = zero + return z + } + // x != 0 + z.mant = z.mant.set(x.abs) + fnorm(z.mant) + z.setExpAndRound(int64(bits), 0) + return z +} + +// SetRat sets z to the (possibly rounded) value of x and returns z. +// If z's precision is 0, it is changed to the largest of a.BitLen(), +// b.BitLen(), or 64; with x = a/b. +func (z *Float) SetRat(x *Rat) *Float { + if x.IsInt() { + return z.SetInt(x.Num()) + } + var a, b Float + a.SetInt(x.Num()) + b.SetInt(x.Denom()) + if z.prec == 0 { + z.prec = umax32(a.prec, b.prec) + } + return z.Quo(&a, &b) +} + +// SetInf sets z to the infinite Float -Inf if signbit is +// set, or +Inf if signbit is not set, and returns z. The +// precision of z is unchanged and the result is always +// Exact. +func (z *Float) SetInf(signbit bool) *Float { + z.acc = Exact + z.form = inf + z.neg = signbit + return z +} + +// Set sets z to the (possibly rounded) value of x and returns z. +// If z's precision is 0, it is changed to the precision of x +// before setting z (and rounding will have no effect). +// Rounding is performed according to z's precision and rounding +// mode; and z's accuracy reports the result error relative to the +// exact (not rounded) result. +func (z *Float) Set(x *Float) *Float { + if debugFloat { + x.validate() + } + z.acc = Exact + if z != x { + z.form = x.form + z.neg = x.neg + if x.form == finite { + z.exp = x.exp + z.mant = z.mant.set(x.mant) + } + if z.prec == 0 { + z.prec = x.prec + } else if z.prec < x.prec { + z.round(0) + } + } + return z +} + +// Copy sets z to x, with the same precision, rounding mode, and +// accuracy as x, and returns z. x is not changed even if z and +// x are the same. +func (z *Float) Copy(x *Float) *Float { + if debugFloat { + x.validate() + } + if z != x { + z.prec = x.prec + z.mode = x.mode + z.acc = x.acc + z.form = x.form + z.neg = x.neg + if z.form == finite { + z.mant = z.mant.set(x.mant) + z.exp = x.exp + } + } + 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 { + return 0 + } + // i > 0 + v := uint64(x[i-1]) + if _W == 32 { + v <<= 32 + if i > 1 { + v |= uint64(x[i-2]) + } + } + return v +} + +// Uint64 returns the unsigned integer resulting from truncating x +// towards zero. If 0 <= x <= math.MaxUint64, the result is Exact +// if x is an integer and Below otherwise. +// The result is (0, Above) for x < 0, and (math.MaxUint64, Below) +// for x > math.MaxUint64. +func (x *Float) Uint64() (uint64, Accuracy) { + if debugFloat { + x.validate() + } + + switch x.form { + case finite: + if x.neg { + return 0, Above + } + // 0 < x < +Inf + if x.exp <= 0 { + // 0 < x < 1 + return 0, Below + } + // 1 <= x < Inf + if x.exp <= 64 { + // u = trunc(x) fits into a uint64 + u := high64(x.mant) >> (64 - uint32(x.exp)) + if x.MinPrec() <= 64 { + return u, Exact + } + return u, Below // x truncated + } + // x too large + return math.MaxUint64, Below + + case zero: + return 0, Exact + + case inf: + if x.neg { + return 0, Above + } + return math.MaxUint64, Below + } + + panic("unreachable") +} + +// Int64 returns the integer resulting from truncating x towards zero. +// If math.MinInt64 <= x <= math.MaxInt64, the result is Exact if x is +// an integer, and Above (x < 0) or Below (x > 0) otherwise. +// The result is (math.MinInt64, Above) for x < math.MinInt64, +// and (math.MaxInt64, Below) for x > math.MaxInt64. +func (x *Float) Int64() (int64, Accuracy) { + if debugFloat { + x.validate() + } + + switch x.form { + case finite: + // 0 < |x| < +Inf + acc := makeAcc(x.neg) + if x.exp <= 0 { + // 0 < |x| < 1 + return 0, acc + } + // x.exp > 0 + + // 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))) + if x.neg { + i = -i + } + if x.MinPrec() <= uint(x.exp) { + return i, Exact + } + return i, acc // x truncated + } + if x.neg { + // check for special case x == math.MinInt64 (i.e., x == -(0.5 << 64)) + if x.exp == 64 && x.MinPrec() == 1 { + acc = Exact + } + return math.MinInt64, acc + } + // x too large + return math.MaxInt64, Below + + case zero: + return 0, Exact + + case inf: + if x.neg { + return math.MinInt64, Above + } + return math.MaxInt64, Below + } + + 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. +// If x is too large to be represented by a float32 (|x| > math.MaxFloat32), +// the result is (+Inf, Above) or (-Inf, Below), depending on the sign of x. +func (x *Float) Float32() (float32, Accuracy) { + if debugFloat { + x.validate() + } + + switch x.form { + case finite: + // 0 < |x| < +Inf + + const ( + fbits = 32 // float size + mbits = 23 // mantissa size (excluding implicit msb) + ebits = fbits - mbits - 1 // 8 exponent size + bias = 1<<(ebits-1) - 1 // 127 exponent bias + dmin = 1 - bias - mbits // -149 smallest unbiased exponent (denormal) + emin = 1 - bias // -126 smallest unbiased exponent (normal) + 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) + + if x.exp < dmin+1 { + // underflow + if x.neg { + var z float32 + return -z, Above + } + return 0.0, Below + } + // x.exp >= dmin+1 + + 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.Set(x) + + // 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 + } + + if r.exp > emax+1 { + // 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) + } + + m := high32(r.mant) >> ebits & (1< math.MaxFloat64), +// the result is (+Inf, Above) or (-Inf, Below), depending on the sign of x. +func (x *Float) Float64() (float64, Accuracy) { + if debugFloat { + x.validate() + } + + switch x.form { + case finite: + // 0 < |x| < +Inf + + const ( + fbits = 64 // float size + mbits = 52 // mantissa size (excluding implicit msb) + ebits = fbits - mbits - 1 // 11 exponent size + bias = 1<<(ebits-1) - 1 // 1023 exponent bias + dmin = 1 - bias - mbits // -1074 smallest unbiased exponent (denormal) + emin = 1 - bias // -1022 smallest unbiased exponent (normal) + 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) + + if x.exp < dmin+1 { + // underflow + if x.neg { + var z float64 + return -z, Above + } + return 0.0, Below + } + // x.exp >= dmin+1 + + 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.Set(x) + + // 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 + } + + if r.exp > emax+1 { + // 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) + } + + m := high64(r.mant) >> ebits & (1< 0, and Above for x < 0. +// If a non-nil *Int argument z is provided, Int stores +// the result in z instead of allocating a new Int. +func (x *Float) Int(z *Int) (*Int, Accuracy) { + if debugFloat { + x.validate() + } + + if z == nil && x.form <= finite { + z = new(Int) + } + + switch x.form { + case finite: + // 0 < |x| < +Inf + acc := makeAcc(x.neg) + if x.exp <= 0 { + // 0 < |x| < 1 + return z.SetInt64(0), acc + } + // x.exp > 0 + + // 1 <= |x| < +Inf + // determine minimum required precision for x + allBits := uint(len(x.mant)) * _W + exp := uint(x.exp) + if x.MinPrec() <= exp { + acc = Exact + } + // shift mantissa as needed + if z == nil { + z = new(Int) + } + z.neg = x.neg + switch { + case exp > allBits: + z.abs = z.abs.shl(x.mant, exp-allBits) + default: + z.abs = z.abs.set(x.mant) + case exp < allBits: + z.abs = z.abs.shr(x.mant, allBits-exp) + } + return z, acc + + case zero: + return z.SetInt64(0), Exact + + case inf: + return nil, makeAcc(x.neg) + } + + panic("unreachable") +} + +// Rat returns the rational number corresponding to x; +// or nil if x is an infinity. +// The result is Exact is x is not an Inf. +// If a non-nil *Rat argument z is provided, Rat stores +// the result in z instead of allocating a new Rat. +func (x *Float) Rat(z *Rat) (*Rat, Accuracy) { + if debugFloat { + x.validate() + } + + if z == nil && x.form <= finite { + z = new(Rat) + } + + switch x.form { + case finite: + // 0 < |x| < +Inf + allBits := int32(len(x.mant)) * _W + // build up numerator and denominator + z.a.neg = x.neg + switch { + case x.exp > allBits: + z.a.abs = z.a.abs.shl(x.mant, uint(x.exp-allBits)) + z.b.abs = z.b.abs[:0] // == 1 (see Rat) + // z already in normal form + default: + z.a.abs = z.a.abs.set(x.mant) + z.b.abs = z.b.abs[:0] // == 1 (see Rat) + // z already in normal form + case x.exp < allBits: + z.a.abs = z.a.abs.set(x.mant) + t := z.b.abs.setUint64(1) + z.b.abs = t.shl(t, uint(allBits-x.exp)) + z.norm() + } + return z, Exact + + case zero: + return z.SetInt64(0), Exact + + case inf: + return nil, makeAcc(x.neg) + } + + panic("unreachable") +} + +// Abs sets z to the (possibly rounded) value |x| (the absolute value of x) +// and returns z. +func (z *Float) Abs(x *Float) *Float { + z.Set(x) + z.neg = false + return z +} + +// Neg sets z to the (possibly rounded) value of x with its sign negated, +// and returns z. +func (z *Float) Neg(x *Float) *Float { + z.Set(x) + z.neg = !z.neg + return z +} + +func validateBinaryOperands(x, y *Float) { + if !debugFloat { + // avoid performance bugs + panic("validateBinaryOperands called but debugFloat is not set") + } + if len(x.mant) == 0 { + panic("empty mantissa for x") + } + if len(y.mant) == 0 { + panic("empty mantissa for y") + } +} + +// z = x + y, ignoring signs of x and y for the addition +// but using the sign of z for rounding the result. +// x and y must have a non-empty mantissa and valid exponent. +func (z *Float) uadd(x, y *Float) { + // Note: This implementation requires 2 shifts most of the + // time. It is also inefficient if exponents or precisions + // differ by wide margins. The following article describes + // an efficient (but much more complicated) implementation + // compatible with the internal representation used here: + // + // Vincent Lefèvre: "The Generic Multiple-Precision Floating- + // Point Addition With Exact Rounding (as in the MPFR Library)" + // http://www.vinc17.net/research/papers/rnc6.pdf + + if debugFloat { + validateBinaryOperands(x, y) + } + + // compute exponents ex, ey for mantissa with "binary point" + // on the right (mantissa.0) - use int64 to avoid overflow + ex := int64(x.exp) - int64(len(x.mant))*_W + ey := int64(y.exp) - int64(len(y.mant))*_W + + // TODO(gri) having a combined add-and-shift primitive + // could make this code significantly faster + switch { + case ex < ey: + // cannot re-use z.mant w/o testing for aliasing + t := nat(nil).shl(y.mant, uint(ey-ex)) + z.mant = z.mant.add(x.mant, t) + default: + // ex == ey, no shift needed + z.mant = z.mant.add(x.mant, y.mant) + case ex > ey: + // cannot re-use z.mant w/o testing for aliasing + t := nat(nil).shl(x.mant, uint(ex-ey)) + z.mant = z.mant.add(t, y.mant) + ex = ey + } + // len(z.mant) > 0 + + z.setExpAndRound(ex+int64(len(z.mant))*_W-fnorm(z.mant), 0) +} + +// z = x - y for |x| > |y|, ignoring signs of x and y for the subtraction +// but using the sign of z for rounding the result. +// x and y must have a non-empty mantissa and valid exponent. +func (z *Float) usub(x, y *Float) { + // This code is symmetric to uadd. + // We have not factored the common code out because + // eventually uadd (and usub) should be optimized + // by special-casing, and the code will diverge. + + if debugFloat { + validateBinaryOperands(x, y) + } + + ex := int64(x.exp) - int64(len(x.mant))*_W + ey := int64(y.exp) - int64(len(y.mant))*_W + + switch { + case ex < ey: + // cannot re-use z.mant w/o testing for aliasing + t := nat(nil).shl(y.mant, uint(ey-ex)) + z.mant = t.sub(x.mant, t) + default: + // ex == ey, no shift needed + z.mant = z.mant.sub(x.mant, y.mant) + case ex > ey: + // cannot re-use z.mant w/o testing for aliasing + t := nat(nil).shl(x.mant, uint(ex-ey)) + z.mant = t.sub(t, y.mant) + ex = ey + } + + // operands may have cancelled each other out + if len(z.mant) == 0 { + z.acc = Exact + z.form = zero + z.neg = false + return + } + // len(z.mant) > 0 + + z.setExpAndRound(ex+int64(len(z.mant))*_W-fnorm(z.mant), 0) +} + +// z = x * y, ignoring signs of x and y for the multiplication +// but using the sign of z for rounding the result. +// x and y must have a non-empty mantissa and valid exponent. +func (z *Float) umul(x, y *Float) { + if debugFloat { + validateBinaryOperands(x, y) + } + + // Note: This is doing too much work if the precision + // of z is less than the sum of the precisions of x + // and y which is often the case (e.g., if all floats + // have the same precision). + // TODO(gri) Optimize this for the common case. + + e := int64(x.exp) + int64(y.exp) + z.mant = z.mant.mul(x.mant, y.mant) + + z.setExpAndRound(e-fnorm(z.mant), 0) +} + +// z = x / y, ignoring signs of x and y for the division +// but using the sign of z for rounding the result. +// x and y must have a non-empty mantissa and valid exponent. +func (z *Float) uquo(x, y *Float) { + if debugFloat { + validateBinaryOperands(x, y) + } + + // mantissa length in words for desired result precision + 1 + // (at least one extra bit so we get the rounding bit after + // the division) + n := int(z.prec/_W) + 1 + + // compute adjusted x.mant such that we get enough result precision + xadj := x.mant + if d := n - len(x.mant) + len(y.mant); d > 0 { + // d extra words needed => add d "0 digits" to x + xadj = make(nat, len(x.mant)+d) + copy(xadj[d:], x.mant) + } + // TODO(gri): If we have too many digits (d < 0), we should be able + // to shorten x for faster division. But we must be extra careful + // with rounding in that case. + + // Compute d before division since there may be aliasing of x.mant + // (via xadj) or y.mant with z.mant. + d := len(xadj) - len(y.mant) + + // divide + var r nat + z.mant, r = z.mant.div(nil, xadj, y.mant) + e := int64(x.exp) - int64(y.exp) - int64(d-len(z.mant))*_W + + // The result is long enough to include (at least) the rounding bit. + // If there's a non-zero remainder, the corresponding fractional part + // (if it were computed), would have a non-zero sticky bit (if it were + // zero, it couldn't have a non-zero remainder). + var sbit uint + if len(r) > 0 { + sbit = 1 + } + + z.setExpAndRound(e-fnorm(z.mant), sbit) +} + +// ucmp returns -1, 0, or +1, depending on whether +// |x| < |y|, |x| == |y|, or |x| > |y|. +// x and y must have a non-empty mantissa and valid exponent. +func (x *Float) ucmp(y *Float) int { + if debugFloat { + validateBinaryOperands(x, y) + } + + switch { + case x.exp < y.exp: + return -1 + case x.exp > y.exp: + return +1 + } + // x.exp == y.exp + + // compare mantissas + i := len(x.mant) + j := len(y.mant) + for i > 0 || j > 0 { + var xm, ym Word + if i > 0 { + i-- + xm = x.mant[i] + } + if j > 0 { + j-- + ym = y.mant[j] + } + switch { + case xm < ym: + return -1 + case xm > ym: + return +1 + } + } + + return 0 +} + +// Handling of sign bit as defined by IEEE 754-2008, section 6.3: +// +// When neither the inputs nor result are NaN, the sign of a product or +// quotient is the exclusive OR of the operands’ signs; the sign of a sum, +// or of a difference x−y regarded as a sum x+(−y), differs from at most +// one of the addends’ signs; and the sign of the result of conversions, +// the quantize operation, the roundToIntegral operations, and the +// roundToIntegralExact (see 5.3.1) is the sign of the first or only operand. +// These rules shall apply even when operands or results are zero or infinite. +// +// When the sum of two operands with opposite signs (or the difference of +// two operands with like signs) is exactly zero, the sign of that sum (or +// difference) shall be +0 in all rounding-direction attributes except +// roundTowardNegative; under that attribute, the sign of an exact zero +// sum (or difference) shall be −0. However, x+x = x−(−x) retains the same +// sign as x even when x is zero. +// +// See also: http://play.golang.org/p/RtH3UCt5IH + +// Add sets z to the rounded sum x+y and returns z. If z's precision is 0, +// it is changed to the larger of x's or y's precision before the operation. +// Rounding is performed according to z's precision and rounding mode; and +// z's accuracy reports the result error relative to the exact (not rounded) +// result. Add panics with ErrNaN if x and y are infinities with opposite +// signs. The value of z is undefined in that case. +// +// BUG(gri) When rounding ToNegativeInf, the sign of Float values rounded to 0 is incorrect. +func (z *Float) Add(x, y *Float) *Float { + if debugFloat { + x.validate() + y.validate() + } + + if z.prec == 0 { + z.prec = umax32(x.prec, y.prec) + } + + if x.form == finite && y.form == finite { + // x + y (commom case) + z.neg = x.neg + if x.neg == y.neg { + // x + y == x + y + // (-x) + (-y) == -(x + y) + z.uadd(x, y) + } else { + // x + (-y) == x - y == -(y - x) + // (-x) + y == y - x == -(x - y) + if x.ucmp(y) > 0 { + z.usub(x, y) + } else { + z.neg = !z.neg + z.usub(y, x) + } + } + return z + } + + if x.form == inf && y.form == inf && x.neg != y.neg { + // +Inf + -Inf + // -Inf + +Inf + // value of z is undefined but make sure it's valid + z.acc = Exact + z.form = zero + z.neg = false + panic(ErrNaN{"addition of infinities with opposite signs"}) + } + + if x.form == zero && y.form == zero { + // ±0 + ±0 + z.acc = Exact + z.form = zero + z.neg = x.neg && y.neg // -0 + -0 == -0 + return z + } + + if x.form == inf || y.form == zero { + // ±Inf + y + // x + ±0 + return z.Set(x) + } + + // ±0 + y + // x + ±Inf + return z.Set(y) +} + +// Sub sets z to the rounded difference x-y and returns z. +// Precision, rounding, and accuracy reporting are as for Add. +// Sub panics with ErrNaN if x and y are infinities with equal +// signs. The value of z is undefined in that case. +func (z *Float) Sub(x, y *Float) *Float { + if debugFloat { + x.validate() + y.validate() + } + + if z.prec == 0 { + z.prec = umax32(x.prec, y.prec) + } + + if x.form == finite && y.form == finite { + // x - y (common case) + z.neg = x.neg + if x.neg != y.neg { + // x - (-y) == x + y + // (-x) - y == -(x + y) + z.uadd(x, y) + } else { + // x - y == x - y == -(y - x) + // (-x) - (-y) == y - x == -(x - y) + if x.ucmp(y) > 0 { + z.usub(x, y) + } else { + z.neg = !z.neg + z.usub(y, x) + } + } + return z + } + + if x.form == inf && y.form == inf && x.neg == y.neg { + // +Inf - +Inf + // -Inf - -Inf + // value of z is undefined but make sure it's valid + z.acc = Exact + z.form = zero + z.neg = false + panic(ErrNaN{"subtraction of infinities with equal signs"}) + } + + if x.form == zero && y.form == zero { + // ±0 - ±0 + z.acc = Exact + z.form = zero + z.neg = x.neg && !y.neg // -0 - +0 == -0 + return z + } + + if x.form == inf || y.form == zero { + // ±Inf - y + // x - ±0 + return z.Set(x) + } + + // ±0 - y + // x - ±Inf + return z.Neg(y) +} + +// Mul sets z to the rounded product x*y and returns z. +// Precision, rounding, and accuracy reporting are as for Add. +// Mul panics with ErrNaN if one operand is zero and the other +// operand an infinity. The value of z is undefined in that case. +func (z *Float) Mul(x, y *Float) *Float { + if debugFloat { + x.validate() + y.validate() + } + + if z.prec == 0 { + z.prec = umax32(x.prec, y.prec) + } + + z.neg = x.neg != y.neg + + if x.form == finite && y.form == finite { + // x * y (common case) + z.umul(x, y) + return z + } + + z.acc = Exact + if x.form == zero && y.form == inf || x.form == inf && y.form == zero { + // ±0 * ±Inf + // ±Inf * ±0 + // value of z is undefined but make sure it's valid + z.form = zero + z.neg = false + panic(ErrNaN{"multiplication of zero with infinity"}) + } + + if x.form == inf || y.form == inf { + // ±Inf * y + // x * ±Inf + z.form = inf + return z + } + + // ±0 * y + // x * ±0 + z.form = zero + return z +} + +// Quo sets z to the rounded quotient x/y and returns z. +// Precision, rounding, and accuracy reporting are as for Add. +// Quo panics with ErrNaN if both operands are zero or infinities. +// The value of z is undefined in that case. +func (z *Float) Quo(x, y *Float) *Float { + if debugFloat { + x.validate() + y.validate() + } + + if z.prec == 0 { + z.prec = umax32(x.prec, y.prec) + } + + z.neg = x.neg != y.neg + + if x.form == finite && y.form == finite { + // x / y (common case) + z.uquo(x, y) + return z + } + + z.acc = Exact + if x.form == zero && y.form == zero || x.form == inf && y.form == inf { + // ±0 / ±0 + // ±Inf / ±Inf + // value of z is undefined but make sure it's valid + z.form = zero + z.neg = false + panic(ErrNaN{"division of zero by zero or infinity by infinity"}) + } + + if x.form == zero || y.form == inf { + // ±0 / y + // x / ±Inf + z.form = zero + return z + } + + // x / ±0 + // ±Inf / y + z.form = inf + return z +} + +// Cmp compares x and y and returns: +// +// -1 if x < y +// 0 if x == y (incl. -0 == 0, -Inf == -Inf, and +Inf == +Inf) +// +1 if x > y +// +func (x *Float) Cmp(y *Float) int { + if debugFloat { + x.validate() + y.validate() + } + + mx := x.ord() + my := y.ord() + switch { + case mx < my: + return -1 + case mx > my: + return +1 + } + // mx == my + + // only if |mx| == 1 we have to compare the mantissae + switch mx { + case -1: + return y.ucmp(x) + case +1: + return x.ucmp(y) + } + + return 0 +} + +// ord classifies x and returns: +// +// -2 if -Inf == x +// -1 if -Inf < x < 0 +// 0 if x == 0 (signed or unsigned) +// +1 if 0 < x < +Inf +// +2 if x == +Inf +// +func (x *Float) ord() int { + var m int + switch x.form { + case finite: + m = 1 + case zero: + return 0 + case inf: + m = 2 + } + if x.neg { + m = -m + } + return m +} + +func umax32(x, y uint32) uint32 { + if x > y { + return x + } + return y +} diff --git a/src/cmd/compile/internal/big/float_test.go b/src/cmd/compile/internal/big/float_test.go new file mode 100644 index 0000000000..de79b07aaf --- /dev/null +++ b/src/cmd/compile/internal/big/float_test.go @@ -0,0 +1,1664 @@ +// Copyright 2014 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 big + +import ( + "fmt" + "math" + "strconv" + "strings" + "testing" +) + +func (x *Float) uint64() uint64 { + u, acc := x.Uint64() + if acc != Exact { + panic(fmt.Sprintf("%s is not a uint64", x.Format('g', 10))) + } + return u +} + +func (x *Float) int64() int64 { + i, acc := x.Int64() + if acc != Exact { + panic(fmt.Sprintf("%s is not an int64", x.Format('g', 10))) + } + return i +} + +func TestFloatZeroValue(t *testing.T) { + // zero (uninitialized) value is a ready-to-use 0.0 + var x Float + if s := x.Format('f', 1); s != "0.0" { + t.Errorf("zero value = %s; want 0.0", s) + } + + // zero value has precision 0 + if prec := x.Prec(); prec != 0 { + t.Errorf("prec = %d; want 0", prec) + } + + // zero value can be used in any and all positions of binary operations + make := func(x int) *Float { + var f Float + if x != 0 { + f.SetInt64(int64(x)) + } + // x == 0 translates into the zero value + return &f + } + for _, test := range []struct { + z, x, y, want int + opname rune + op func(z, x, y *Float) *Float + }{ + {0, 0, 0, 0, '+', (*Float).Add}, + {0, 1, 2, 3, '+', (*Float).Add}, + {1, 2, 0, 2, '+', (*Float).Add}, + {2, 0, 1, 1, '+', (*Float).Add}, + + {0, 0, 0, 0, '-', (*Float).Sub}, + {0, 1, 2, -1, '-', (*Float).Sub}, + {1, 2, 0, 2, '-', (*Float).Sub}, + {2, 0, 1, -1, '-', (*Float).Sub}, + + {0, 0, 0, 0, '*', (*Float).Mul}, + {0, 1, 2, 2, '*', (*Float).Mul}, + {1, 2, 0, 0, '*', (*Float).Mul}, + {2, 0, 1, 0, '*', (*Float).Mul}, + + // {0, 0, 0, 0, '/', (*Float).Quo}, // panics + {0, 2, 1, 2, '/', (*Float).Quo}, + {1, 2, 0, 0, '/', (*Float).Quo}, // = +Inf + {2, 0, 1, 0, '/', (*Float).Quo}, + } { + z := make(test.z) + test.op(z, make(test.x), make(test.y)) + got := 0 + if !z.IsInf() { + got = int(z.int64()) + } + if got != test.want { + t.Errorf("%d %c %d = %d; want %d", test.x, test.opname, test.y, got, test.want) + } + } + + // TODO(gri) test how precision is set for zero value results +} + +func makeFloat(s string) *Float { + var x Float + + switch s { + case "0": + return &x + case "-0": + return x.Neg(&x) + case "Inf", "+Inf": + return x.SetInf(false) + case "-Inf": + return x.SetInf(true) + } + + x.SetPrec(1000) + if _, ok := x.SetString(s); !ok { + panic(fmt.Sprintf("%q is not a valid float", s)) + } + return &x +} + +func TestFloatSetPrec(t *testing.T) { + for _, test := range []struct { + x string + prec uint + want string + acc Accuracy + }{ + // prec 0 + {"0", 0, "0", Exact}, + {"-0", 0, "-0", Exact}, + {"-Inf", 0, "-Inf", Exact}, + {"+Inf", 0, "+Inf", Exact}, + {"123", 0, "0", Below}, + {"-123", 0, "-0", Above}, + + // prec at upper limit + {"0", MaxPrec, "0", Exact}, + {"-0", MaxPrec, "-0", Exact}, + {"-Inf", MaxPrec, "-Inf", Exact}, + {"+Inf", MaxPrec, "+Inf", Exact}, + + // just a few regular cases - general rounding is tested elsewhere + {"1.5", 1, "2", Above}, + {"-1.5", 1, "-2", Below}, + {"123", 1e6, "123", Exact}, + {"-123", 1e6, "-123", Exact}, + } { + x := makeFloat(test.x).SetPrec(test.prec) + prec := test.prec + if prec > MaxPrec { + prec = MaxPrec + } + if got := x.Prec(); got != prec { + t.Errorf("%s.SetPrec(%d).Prec() == %d; want %d", test.x, test.prec, got, prec) + } + if got, acc := x.String(), x.Acc(); got != test.want || acc != test.acc { + t.Errorf("%s.SetPrec(%d) = %s (%s); want %s (%s)", test.x, test.prec, got, acc, test.want, test.acc) + } + } +} + +func TestFloatMinPrec(t *testing.T) { + const max = 100 + for _, test := range []struct { + x string + want uint + }{ + {"0", 0}, + {"-0", 0}, + {"+Inf", 0}, + {"-Inf", 0}, + {"1", 1}, + {"2", 1}, + {"3", 2}, + {"0x8001", 16}, + {"0x8001p-1000", 16}, + {"0x8001p+1000", 16}, + {"0.1", max}, + } { + x := makeFloat(test.x).SetPrec(max) + if got := x.MinPrec(); got != test.want { + t.Errorf("%s.MinPrec() = %d; want %d", test.x, got, test.want) + } + } +} + +func TestFloatSign(t *testing.T) { + for _, test := range []struct { + x string + s int + }{ + {"-Inf", -1}, + {"-1", -1}, + {"-0", 0}, + {"+0", 0}, + {"+1", +1}, + {"+Inf", +1}, + } { + x := makeFloat(test.x) + s := x.Sign() + if s != test.s { + t.Errorf("%s.Sign() = %d; want %d", test.x, s, test.s) + } + } +} + +// alike(x, y) is like x.Cmp(y) == 0 but also considers the sign of 0 (0 != -0). +func alike(x, y *Float) bool { + return x.Cmp(y) == 0 && x.Signbit() == y.Signbit() +} + +func TestFloatMantExp(t *testing.T) { + for _, test := range []struct { + x string + mant string + exp int + }{ + {"0", "0", 0}, + {"+0", "0", 0}, + {"-0", "-0", 0}, + {"Inf", "+Inf", 0}, + {"+Inf", "+Inf", 0}, + {"-Inf", "-Inf", 0}, + {"1.5", "0.75", 1}, + {"1.024e3", "0.5", 11}, + {"-0.125", "-0.5", -2}, + } { + x := makeFloat(test.x) + mant := makeFloat(test.mant) + m := new(Float) + e := x.MantExp(m) + if !alike(m, mant) || e != test.exp { + t.Errorf("%s.MantExp() = %s, %d; want %s, %d", test.x, m.Format('g', 10), e, test.mant, test.exp) + } + } +} + +func TestFloatMantExpAliasing(t *testing.T) { + x := makeFloat("0.5p10") + if e := x.MantExp(x); e != 10 { + t.Fatalf("Float.MantExp aliasing error: got %d; want 10", e) + } + if want := makeFloat("0.5"); !alike(x, want) { + t.Fatalf("Float.MantExp aliasing error: got %s; want %s", x.Format('g', 10), want.Format('g', 10)) + } +} + +func TestFloatSetMantExp(t *testing.T) { + for _, test := range []struct { + frac string + exp int + z string + }{ + {"0", 0, "0"}, + {"+0", 0, "0"}, + {"-0", 0, "-0"}, + {"Inf", 1234, "+Inf"}, + {"+Inf", -1234, "+Inf"}, + {"-Inf", -1234, "-Inf"}, + {"0", MinExp, "0"}, + {"0.25", MinExp, "+0"}, // exponent underflow + {"-0.25", MinExp, "-0"}, // exponent underflow + {"1", MaxExp, "+Inf"}, // exponent overflow + {"2", MaxExp - 1, "+Inf"}, // exponent overflow + {"0.75", 1, "1.5"}, + {"0.5", 11, "1024"}, + {"-0.5", -2, "-0.125"}, + {"32", 5, "1024"}, + {"1024", -10, "1"}, + } { + frac := makeFloat(test.frac) + want := makeFloat(test.z) + var z Float + z.SetMantExp(frac, test.exp) + if !alike(&z, want) { + t.Errorf("SetMantExp(%s, %d) = %s; want %s", test.frac, test.exp, z.Format('g', 10), test.z) + } + // test inverse property + mant := new(Float) + if z.SetMantExp(mant, want.MantExp(mant)).Cmp(want) != 0 { + t.Errorf("Inverse property not satisfied: got %s; want %s", z.Format('g', 10), test.z) + } + } +} + +func TestFloatPredicates(t *testing.T) { + for _, test := range []struct { + x string + sign int + signbit, inf bool + }{ + {x: "-Inf", sign: -1, signbit: true, inf: true}, + {x: "-1", sign: -1, signbit: true}, + {x: "-0", signbit: true}, + {x: "0"}, + {x: "1", sign: 1}, + {x: "+Inf", sign: 1, inf: true}, + } { + x := makeFloat(test.x) + if got := x.Signbit(); got != test.signbit { + t.Errorf("(%s).Signbit() = %v; want %v", test.x, got, test.signbit) + } + if got := x.Sign(); got != test.sign { + t.Errorf("(%s).Sign() = %d; want %d", test.x, got, test.sign) + } + if got := x.IsInf(); got != test.inf { + t.Errorf("(%s).IsInf() = %v; want %v", test.x, got, test.inf) + } + } +} + +func TestFloatIsInt(t *testing.T) { + for _, test := range []string{ + "0 int", + "-0 int", + "1 int", + "-1 int", + "0.5", + "1.23", + "1.23e1", + "1.23e2 int", + "0.000000001e+8", + "0.000000001e+9 int", + "1.2345e200 int", + "Inf", + "+Inf", + "-Inf", + } { + s := strings.TrimSuffix(test, " int") + want := s != test + if got := makeFloat(s).IsInt(); got != want { + t.Errorf("%s.IsInt() == %t", s, got) + } + } +} + +func fromBinary(s string) int64 { + x, err := strconv.ParseInt(s, 2, 64) + if err != nil { + panic(err) + } + return x +} + +func toBinary(x int64) string { + return strconv.FormatInt(x, 2) +} + +func testFloatRound(t *testing.T, x, r int64, prec uint, mode RoundingMode) { + // verify test data + var ok bool + switch mode { + case ToNearestEven, ToNearestAway: + ok = true // nothing to do for now + case ToZero: + if x < 0 { + ok = r >= x + } else { + ok = r <= x + } + case AwayFromZero: + if x < 0 { + ok = r <= x + } else { + ok = r >= x + } + case ToNegativeInf: + ok = r <= x + case ToPositiveInf: + ok = r >= x + default: + panic("unreachable") + } + if !ok { + t.Fatalf("incorrect test data for prec = %d, %s: x = %s, r = %s", prec, mode, toBinary(x), toBinary(r)) + } + + // compute expected accuracy + a := Exact + switch { + case r < x: + a = Below + case r > x: + a = Above + } + + // round + f := new(Float).SetMode(mode).SetInt64(x).SetPrec(prec) + + // check result + r1 := f.int64() + p1 := f.Prec() + a1 := f.Acc() + if r1 != r || p1 != prec || a1 != a { + t.Errorf("round %s (%d bits, %s) incorrect: got %s (%d bits, %s); want %s (%d bits, %s)", + toBinary(x), prec, mode, + toBinary(r1), p1, a1, + toBinary(r), prec, a) + return + } + + // g and f should be the same + // (rounding by SetPrec after SetInt64 using default precision + // should be the same as rounding by SetInt64 after setting the + // precision) + g := new(Float).SetMode(mode).SetPrec(prec).SetInt64(x) + if !alike(g, f) { + t.Errorf("round %s (%d bits, %s) not symmetric: got %s and %s; want %s", + toBinary(x), prec, mode, + toBinary(g.int64()), + toBinary(r1), + toBinary(r), + ) + return + } + + // h and f should be the same + // (repeated rounding should be idempotent) + h := new(Float).SetMode(mode).SetPrec(prec).Set(f) + if !alike(h, f) { + t.Errorf("round %s (%d bits, %s) not idempotent: got %s and %s; want %s", + toBinary(x), prec, mode, + toBinary(h.int64()), + toBinary(r1), + toBinary(r), + ) + return + } +} + +// TestFloatRound tests basic rounding. +func TestFloatRound(t *testing.T) { + for _, test := range []struct { + prec uint + x, zero, neven, naway, away string // input, results rounded to prec bits + }{ + {5, "1000", "1000", "1000", "1000", "1000"}, + {5, "1001", "1001", "1001", "1001", "1001"}, + {5, "1010", "1010", "1010", "1010", "1010"}, + {5, "1011", "1011", "1011", "1011", "1011"}, + {5, "1100", "1100", "1100", "1100", "1100"}, + {5, "1101", "1101", "1101", "1101", "1101"}, + {5, "1110", "1110", "1110", "1110", "1110"}, + {5, "1111", "1111", "1111", "1111", "1111"}, + + {4, "1000", "1000", "1000", "1000", "1000"}, + {4, "1001", "1001", "1001", "1001", "1001"}, + {4, "1010", "1010", "1010", "1010", "1010"}, + {4, "1011", "1011", "1011", "1011", "1011"}, + {4, "1100", "1100", "1100", "1100", "1100"}, + {4, "1101", "1101", "1101", "1101", "1101"}, + {4, "1110", "1110", "1110", "1110", "1110"}, + {4, "1111", "1111", "1111", "1111", "1111"}, + + {3, "1000", "1000", "1000", "1000", "1000"}, + {3, "1001", "1000", "1000", "1010", "1010"}, + {3, "1010", "1010", "1010", "1010", "1010"}, + {3, "1011", "1010", "1100", "1100", "1100"}, + {3, "1100", "1100", "1100", "1100", "1100"}, + {3, "1101", "1100", "1100", "1110", "1110"}, + {3, "1110", "1110", "1110", "1110", "1110"}, + {3, "1111", "1110", "10000", "10000", "10000"}, + + {3, "1000001", "1000000", "1000000", "1000000", "1010000"}, + {3, "1001001", "1000000", "1010000", "1010000", "1010000"}, + {3, "1010001", "1010000", "1010000", "1010000", "1100000"}, + {3, "1011001", "1010000", "1100000", "1100000", "1100000"}, + {3, "1100001", "1100000", "1100000", "1100000", "1110000"}, + {3, "1101001", "1100000", "1110000", "1110000", "1110000"}, + {3, "1110001", "1110000", "1110000", "1110000", "10000000"}, + {3, "1111001", "1110000", "10000000", "10000000", "10000000"}, + + {2, "1000", "1000", "1000", "1000", "1000"}, + {2, "1001", "1000", "1000", "1000", "1100"}, + {2, "1010", "1000", "1000", "1100", "1100"}, + {2, "1011", "1000", "1100", "1100", "1100"}, + {2, "1100", "1100", "1100", "1100", "1100"}, + {2, "1101", "1100", "1100", "1100", "10000"}, + {2, "1110", "1100", "10000", "10000", "10000"}, + {2, "1111", "1100", "10000", "10000", "10000"}, + + {2, "1000001", "1000000", "1000000", "1000000", "1100000"}, + {2, "1001001", "1000000", "1000000", "1000000", "1100000"}, + {2, "1010001", "1000000", "1100000", "1100000", "1100000"}, + {2, "1011001", "1000000", "1100000", "1100000", "1100000"}, + {2, "1100001", "1100000", "1100000", "1100000", "10000000"}, + {2, "1101001", "1100000", "1100000", "1100000", "10000000"}, + {2, "1110001", "1100000", "10000000", "10000000", "10000000"}, + {2, "1111001", "1100000", "10000000", "10000000", "10000000"}, + + {1, "1000", "1000", "1000", "1000", "1000"}, + {1, "1001", "1000", "1000", "1000", "10000"}, + {1, "1010", "1000", "1000", "1000", "10000"}, + {1, "1011", "1000", "1000", "1000", "10000"}, + {1, "1100", "1000", "10000", "10000", "10000"}, + {1, "1101", "1000", "10000", "10000", "10000"}, + {1, "1110", "1000", "10000", "10000", "10000"}, + {1, "1111", "1000", "10000", "10000", "10000"}, + + {1, "1000001", "1000000", "1000000", "1000000", "10000000"}, + {1, "1001001", "1000000", "1000000", "1000000", "10000000"}, + {1, "1010001", "1000000", "1000000", "1000000", "10000000"}, + {1, "1011001", "1000000", "1000000", "1000000", "10000000"}, + {1, "1100001", "1000000", "10000000", "10000000", "10000000"}, + {1, "1101001", "1000000", "10000000", "10000000", "10000000"}, + {1, "1110001", "1000000", "10000000", "10000000", "10000000"}, + {1, "1111001", "1000000", "10000000", "10000000", "10000000"}, + } { + x := fromBinary(test.x) + z := fromBinary(test.zero) + e := fromBinary(test.neven) + n := fromBinary(test.naway) + a := fromBinary(test.away) + prec := test.prec + + testFloatRound(t, x, z, prec, ToZero) + testFloatRound(t, x, e, prec, ToNearestEven) + testFloatRound(t, x, n, prec, ToNearestAway) + testFloatRound(t, x, a, prec, AwayFromZero) + + testFloatRound(t, x, z, prec, ToNegativeInf) + testFloatRound(t, x, a, prec, ToPositiveInf) + + testFloatRound(t, -x, -a, prec, ToNegativeInf) + testFloatRound(t, -x, -z, prec, ToPositiveInf) + } +} + +// TestFloatRound24 tests that rounding a float64 to 24 bits +// matches IEEE-754 rounding to nearest when converting a +// float64 to a float32 (excluding denormal numbers). +func TestFloatRound24(t *testing.T) { + const x0 = 1<<26 - 0x10 // 11...110000 (26 bits) + for d := 0; d <= 0x10; d++ { + x := float64(x0 + d) + f := new(Float).SetPrec(24).SetFloat64(x) + got, _ := f.Float32() + want := float32(x) + if got != want { + t.Errorf("Round(%g, 24) = %g; want %g", x, got, want) + } + } +} + +func TestFloatSetUint64(t *testing.T) { + for _, want := range []uint64{ + 0, + 1, + 2, + 10, + 100, + 1<<32 - 1, + 1 << 32, + 1<<64 - 1, + } { + var f Float + f.SetUint64(want) + if got := f.uint64(); got != want { + t.Errorf("got %#x (%s); want %#x", got, f.Format('p', 0), want) + } + } + + // test basic rounding behavior (exhaustive rounding testing is done elsewhere) + const x uint64 = 0x8765432187654321 // 64 bits needed + for prec := uint(1); prec <= 64; prec++ { + f := new(Float).SetPrec(prec).SetMode(ToZero).SetUint64(x) + got := f.uint64() + want := x &^ (1<<(64-prec) - 1) // cut off (round to zero) low 64-prec bits + if got != want { + t.Errorf("got %#x (%s); want %#x", got, f.Format('p', 0), want) + } + } +} + +func TestFloatSetInt64(t *testing.T) { + for _, want := range []int64{ + 0, + 1, + 2, + 10, + 100, + 1<<32 - 1, + 1 << 32, + 1<<63 - 1, + } { + for i := range [2]int{} { + if i&1 != 0 { + want = -want + } + var f Float + f.SetInt64(want) + if got := f.int64(); got != want { + t.Errorf("got %#x (%s); want %#x", got, f.Format('p', 0), want) + } + } + } + + // test basic rounding behavior (exhaustive rounding testing is done elsewhere) + const x int64 = 0x7654321076543210 // 63 bits needed + for prec := uint(1); prec <= 63; prec++ { + f := new(Float).SetPrec(prec).SetMode(ToZero).SetInt64(x) + got := f.int64() + want := x &^ (1<<(63-prec) - 1) // cut off (round to zero) low 63-prec bits + if got != want { + t.Errorf("got %#x (%s); want %#x", got, f.Format('p', 0), want) + } + } +} + +func TestFloatSetFloat64(t *testing.T) { + for _, want := range []float64{ + 0, + 1, + 2, + 12345, + 1e10, + 1e100, + 3.14159265e10, + 2.718281828e-123, + 1.0 / 3, + math.MaxFloat32, + math.MaxFloat64, + math.SmallestNonzeroFloat32, + math.SmallestNonzeroFloat64, + math.Inf(-1), + math.Inf(0), + -math.Inf(1), + } { + for i := range [2]int{} { + if i&1 != 0 { + want = -want + } + var f Float + f.SetFloat64(want) + if got, acc := f.Float64(); got != want || acc != Exact { + t.Errorf("got %g (%s, %s); want %g (Exact)", got, f.Format('p', 0), acc, want) + } + } + } + + // test basic rounding behavior (exhaustive rounding testing is done elsewhere) + const x uint64 = 0x8765432143218 // 53 bits needed + for prec := uint(1); prec <= 52; prec++ { + f := new(Float).SetPrec(prec).SetMode(ToZero).SetFloat64(float64(x)) + got, _ := f.Float64() + want := float64(x &^ (1<<(52-prec) - 1)) // cut off (round to zero) low 53-prec bits + if got != want { + t.Errorf("got %g (%s); want %g", got, f.Format('p', 0), want) + } + } + + // test NaN + defer func() { + if p, ok := recover().(ErrNaN); !ok { + t.Errorf("got %v; want ErrNaN panic", p) + } + }() + var f Float + f.SetFloat64(math.NaN()) + // should not reach here + t.Errorf("got %s; want ErrNaN panic", f.Format('p', 0)) +} + +func TestFloatSetInt(t *testing.T) { + for _, want := range []string{ + "0", + "1", + "-1", + "1234567890", + "123456789012345678901234567890", + "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", + } { + var x Int + _, ok := x.SetString(want, 0) + if !ok { + t.Errorf("invalid integer %s", want) + continue + } + n := x.BitLen() + + var f Float + f.SetInt(&x) + + // check precision + if n < 64 { + n = 64 + } + if prec := f.Prec(); prec != uint(n) { + t.Errorf("got prec = %d; want %d", prec, n) + } + + // check value + got := f.Format('g', 100) + if got != want { + t.Errorf("got %s (%s); want %s", got, f.Format('p', 0), want) + } + } + + // TODO(gri) test basic rounding behavior +} + +func TestFloatSetRat(t *testing.T) { + for _, want := range []string{ + "0", + "1", + "-1", + "1234567890", + "123456789012345678901234567890", + "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", + "1.2", + "3.14159265", + // TODO(gri) expand + } { + var x Rat + _, ok := x.SetString(want) + if !ok { + t.Errorf("invalid fraction %s", want) + continue + } + n := max(x.Num().BitLen(), x.Denom().BitLen()) + + var f1, f2 Float + f2.SetPrec(1000) + f1.SetRat(&x) + f2.SetRat(&x) + + // check precision when set automatically + if n < 64 { + n = 64 + } + if prec := f1.Prec(); prec != uint(n) { + t.Errorf("got prec = %d; want %d", prec, n) + } + + got := f2.Format('g', 100) + if got != want { + t.Errorf("got %s (%s); want %s", got, f2.Format('p', 0), want) + } + } +} + +func TestFloatSetInf(t *testing.T) { + var f Float + for _, test := range []struct { + signbit bool + prec uint + want string + }{ + {false, 0, "+Inf"}, + {true, 0, "-Inf"}, + {false, 10, "+Inf"}, + {true, 30, "-Inf"}, + } { + x := f.SetPrec(test.prec).SetInf(test.signbit) + if got := x.String(); got != test.want || x.Prec() != test.prec { + t.Errorf("SetInf(%v) = %s (prec = %d); want %s (prec = %d)", test.signbit, got, x.Prec(), test.want, test.prec) + } + } +} + +func TestFloatUint64(t *testing.T) { + for _, test := range []struct { + x string + out uint64 + acc Accuracy + }{ + {"-Inf", 0, Above}, + {"-1", 0, Above}, + {"-1e-1000", 0, Above}, + {"-0", 0, Exact}, + {"0", 0, Exact}, + {"1e-1000", 0, Below}, + {"1", 1, Exact}, + {"1.000000000000000000001", 1, Below}, + {"12345.0", 12345, Exact}, + {"12345.000000000000000000001", 12345, Below}, + {"18446744073709551615", 18446744073709551615, Exact}, + {"18446744073709551615.000000000000000000001", math.MaxUint64, Below}, + {"18446744073709551616", math.MaxUint64, Below}, + {"1e10000", math.MaxUint64, Below}, + {"+Inf", math.MaxUint64, Below}, + } { + x := makeFloat(test.x) + out, acc := x.Uint64() + if out != test.out || acc != test.acc { + t.Errorf("%s: got %d (%s); want %d (%s)", test.x, out, acc, test.out, test.acc) + } + } +} + +func TestFloatInt64(t *testing.T) { + for _, test := range []struct { + x string + out int64 + acc Accuracy + }{ + {"-Inf", math.MinInt64, Above}, + {"-1e10000", math.MinInt64, Above}, + {"-9223372036854775809", math.MinInt64, Above}, + {"-9223372036854775808.000000000000000000001", math.MinInt64, Above}, + {"-9223372036854775808", -9223372036854775808, Exact}, + {"-9223372036854775807.000000000000000000001", -9223372036854775807, Above}, + {"-9223372036854775807", -9223372036854775807, Exact}, + {"-12345.000000000000000000001", -12345, Above}, + {"-12345.0", -12345, Exact}, + {"-1.000000000000000000001", -1, Above}, + {"-1.5", -1, Above}, + {"-1", -1, Exact}, + {"-1e-1000", 0, Above}, + {"0", 0, Exact}, + {"1e-1000", 0, Below}, + {"1", 1, Exact}, + {"1.000000000000000000001", 1, Below}, + {"1.5", 1, Below}, + {"12345.0", 12345, Exact}, + {"12345.000000000000000000001", 12345, Below}, + {"9223372036854775807", 9223372036854775807, Exact}, + {"9223372036854775807.000000000000000000001", math.MaxInt64, Below}, + {"9223372036854775808", math.MaxInt64, Below}, + {"1e10000", math.MaxInt64, Below}, + {"+Inf", math.MaxInt64, Below}, + } { + x := makeFloat(test.x) + out, acc := x.Int64() + if out != test.out || acc != test.acc { + t.Errorf("%s: got %d (%s); want %d (%s)", test.x, out, acc, test.out, test.acc) + } + } +} + +func TestFloatFloat32(t *testing.T) { + for _, test := range []struct { + x string + out float32 + acc Accuracy + }{ + {"-Inf", float32(math.Inf(-1)), Exact}, + {"-0x1.ffffff0p2147483646", float32(-math.Inf(+1)), Below}, // overflow in rounding + {"-1e10000", float32(math.Inf(-1)), Below}, // overflow + {"-0x1p128", float32(math.Inf(-1)), Below}, // overflow + {"-0x1.ffffff0p127", float32(-math.Inf(+1)), Below}, // overflow + {"-0x1.fffffe8p127", -math.MaxFloat32, Above}, + {"-0x1.fffffe0p127", -math.MaxFloat32, Exact}, + {"-12345.000000000000000000001", -12345, Above}, + {"-12345.0", -12345, Exact}, + {"-1.000000000000000000001", -1, Above}, + {"-1", -1, Exact}, + {"-0x0.000002p-126", -math.SmallestNonzeroFloat32, Exact}, + {"-0x0.000002p-127", -0, Above}, // underflow + {"-1e-1000", -0, Above}, // underflow + {"0", 0, Exact}, + {"1e-1000", 0, Below}, // underflow + {"0x0.000002p-127", 0, Below}, // underflow + {"0x0.000002p-126", math.SmallestNonzeroFloat32, Exact}, + {"1", 1, Exact}, + {"1.000000000000000000001", 1, Below}, + {"12345.0", 12345, Exact}, + {"12345.000000000000000000001", 12345, Below}, + {"0x1.fffffe0p127", math.MaxFloat32, Exact}, + {"0x1.fffffe8p127", math.MaxFloat32, Below}, + {"0x1.ffffff0p127", float32(math.Inf(+1)), Above}, // overflow + {"0x1p128", float32(math.Inf(+1)), Above}, // overflow + {"1e10000", float32(math.Inf(+1)), Above}, // overflow + {"0x1.ffffff0p2147483646", float32(math.Inf(+1)), Above}, // overflow in rounding + {"+Inf", float32(math.Inf(+1)), Exact}, + } { + // conversion should match strconv where syntax is agreeable + if f, err := strconv.ParseFloat(test.x, 32); err == nil && float32(f) != test.out { + t.Errorf("%s: got %g; want %g (incorrect test data)", test.x, f, test.out) + } + + x := makeFloat(test.x) + out, acc := x.Float32() + if out != test.out || acc != test.acc { + t.Errorf("%s: got %g (%#x, %s); want %g (%#x, %s)", test.x, out, math.Float32bits(out), acc, test.out, math.Float32bits(test.out), test.acc) + } + + // test that x.SetFloat64(float64(f)).Float32() == f + var x2 Float + out2, acc2 := x2.SetFloat64(float64(out)).Float32() + if out2 != out || acc2 != Exact { + t.Errorf("idempotency test: got %g (%s); want %g (Exact)", out2, acc2, out) + } + } +} + +func TestFloatFloat64(t *testing.T) { + const smallestNormalFloat64 = 2.2250738585072014e-308 // 1p-1022 + for _, test := range []struct { + x string + 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}, + {"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 + {"0x1.fffffffffffff8p2147483646", math.Inf(+1), Above}, // overflow in rounding + {"+Inf", math.Inf(+1), Exact}, + + // selected denormalized values that were handled incorrectly in the past + {"0x.fffffffffffffp-1022", smallestNormalFloat64 - math.SmallestNonzeroFloat64, Exact}, + {"4503599627370495p-1074", smallestNormalFloat64 - math.SmallestNonzeroFloat64, Exact}, + + // http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/ + {"2.2250738585072011e-308", 2.225073858507201e-308, Below}, + // 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) + } + + 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) + } + + // 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) + } + } +} + +func TestFloatInt(t *testing.T) { + for _, test := range []struct { + x string + want string + acc Accuracy + }{ + {"0", "0", Exact}, + {"+0", "0", Exact}, + {"-0", "0", Exact}, + {"Inf", "nil", Below}, + {"+Inf", "nil", Below}, + {"-Inf", "nil", Above}, + {"1", "1", Exact}, + {"-1", "-1", Exact}, + {"1.23", "1", Below}, + {"-1.23", "-1", Above}, + {"123e-2", "1", Below}, + {"123e-3", "0", Below}, + {"123e-4", "0", Below}, + {"1e-1000", "0", Below}, + {"-1e-1000", "0", Above}, + {"1e+10", "10000000000", Exact}, + {"1e+100", "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", Exact}, + } { + x := makeFloat(test.x) + res, acc := x.Int(nil) + got := "nil" + if res != nil { + got = res.String() + } + if got != test.want || acc != test.acc { + t.Errorf("%s: got %s (%s); want %s (%s)", test.x, got, acc, test.want, test.acc) + } + } + + // check that supplied *Int is used + for _, f := range []string{"0", "1", "-1", "1234"} { + x := makeFloat(f) + i := new(Int) + if res, _ := x.Int(i); res != i { + t.Errorf("(%s).Int is not using supplied *Int", f) + } + } +} + +func TestFloatRat(t *testing.T) { + for _, test := range []struct { + x, want string + acc Accuracy + }{ + {"0", "0/1", Exact}, + {"+0", "0/1", Exact}, + {"-0", "0/1", Exact}, + {"Inf", "nil", Below}, + {"+Inf", "nil", Below}, + {"-Inf", "nil", Above}, + {"1", "1/1", Exact}, + {"-1", "-1/1", Exact}, + {"1.25", "5/4", Exact}, + {"-1.25", "-5/4", Exact}, + {"1e10", "10000000000/1", Exact}, + {"1p10", "1024/1", Exact}, + {"-1p-10", "-1/1024", Exact}, + {"3.14159265", "7244019449799623199/2305843009213693952", Exact}, + } { + x := makeFloat(test.x).SetPrec(64) + res, acc := x.Rat(nil) + got := "nil" + if res != nil { + got = res.String() + } + if got != test.want { + t.Errorf("%s: got %s; want %s", test.x, got, test.want) + continue + } + if acc != test.acc { + t.Errorf("%s: got %s; want %s", test.x, acc, test.acc) + continue + } + + // inverse conversion + if res != nil { + got := new(Float).SetPrec(64).SetRat(res) + if got.Cmp(x) != 0 { + t.Errorf("%s: got %s; want %s", test.x, got, x) + } + } + } + + // check that supplied *Rat is used + for _, f := range []string{"0", "1", "-1", "1234"} { + x := makeFloat(f) + r := new(Rat) + if res, _ := x.Rat(r); res != r { + t.Errorf("(%s).Rat is not using supplied *Rat", f) + } + } +} + +func TestFloatAbs(t *testing.T) { + for _, test := range []string{ + "0", + "1", + "1234", + "1.23e-2", + "1e-1000", + "1e1000", + "Inf", + } { + p := makeFloat(test) + a := new(Float).Abs(p) + if !alike(a, p) { + t.Errorf("%s: got %s; want %s", test, a.Format('g', 10), test) + } + + n := makeFloat("-" + test) + a.Abs(n) + if !alike(a, p) { + t.Errorf("-%s: got %s; want %s", test, a.Format('g', 10), test) + } + } +} + +func TestFloatNeg(t *testing.T) { + for _, test := range []string{ + "0", + "1", + "1234", + "1.23e-2", + "1e-1000", + "1e1000", + "Inf", + } { + p1 := makeFloat(test) + n1 := makeFloat("-" + test) + n2 := new(Float).Neg(p1) + p2 := new(Float).Neg(n2) + if !alike(n2, n1) { + t.Errorf("%s: got %s; want %s", test, n2.Format('g', 10), n1.Format('g', 10)) + } + if !alike(p2, p1) { + t.Errorf("%s: got %s; want %s", test, p2.Format('g', 10), p1.Format('g', 10)) + } + } +} + +func TestFloatInc(t *testing.T) { + const n = 10 + for _, prec := range precList { + if 1< y: + want = +1 + } + if got != want { + t.Errorf("(%g).Cmp(%g) = %v; want %v", x, y, got, want) + } + } + } + } +} diff --git a/src/cmd/compile/internal/big/floatconv.go b/src/cmd/compile/internal/big/floatconv.go new file mode 100644 index 0000000000..b929d1202c --- /dev/null +++ b/src/cmd/compile/internal/big/floatconv.go @@ -0,0 +1,373 @@ +// 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. + +// This file implements float-to-string conversion functions. + +package big + +import ( + "fmt" + "io" + "strconv" + "strings" +) + +// SetString sets z to the value of s and returns z and a boolean indicating +// success. s must be a floating-point number of the same format as accepted +// by Scan, with number prefixes permitted. +func (z *Float) SetString(s string) (*Float, bool) { + r := strings.NewReader(s) + + f, _, err := z.Scan(r, 0) + if err != nil { + return nil, false + } + + // there should be no unread characters left + if _, err = r.ReadByte(); err != io.EOF { + return nil, false + } + + return f, true +} + +// Scan scans the number corresponding to the longest possible prefix +// of r representing a floating-point number with a mantissa in the +// given conversion base (the exponent is always a decimal number). +// It sets z to the (possibly rounded) value of the corresponding +// floating-point number, and returns z, the actual base b, and an +// error err, if any. If z's precision is 0, it is changed to 64 +// before rounding takes effect. The number must be of the form: +// +// number = [ sign ] [ prefix ] mantissa [ exponent ] . +// sign = "+" | "-" . +// prefix = "0" ( "x" | "X" | "b" | "B" ) . +// mantissa = digits | digits "." [ digits ] | "." digits . +// exponent = ( "E" | "e" | "p" ) [ sign ] digits . +// digits = digit { digit } . +// digit = "0" ... "9" | "a" ... "z" | "A" ... "Z" . +// +// The base argument must be 0, 2, 10, or 16. Providing an invalid base +// argument will lead to a run-time panic. +// +// For base 0, the number prefix determines the actual base: A prefix of +// "0x" or "0X" selects base 16, and a "0b" or "0B" prefix selects +// base 2; otherwise, the actual base is 10 and no prefix is accepted. +// The octal prefix "0" is not supported (a leading "0" is simply +// considered a "0"). +// +// A "p" exponent indicates a binary (rather then decimal) exponent; +// for instance "0x1.fffffffffffffp1023" (using base 0) represents the +// maximum float64 value. For hexadecimal mantissae, the exponent must +// be binary, if present (an "e" or "E" exponent indicator cannot be +// distinguished from a mantissa digit). +// +// The returned *Float f is nil and the value of z is valid but not +// defined if an error is reported. +// +// BUG(gri) The Float.Scan signature conflicts with Scan(s fmt.ScanState, ch rune) error. +func (z *Float) Scan(r io.ByteScanner, base int) (f *Float, b int, err error) { + prec := z.prec + if prec == 0 { + prec = 64 + } + + // A reasonable value in case of an error. + z.form = zero + + // sign + z.neg, err = scanSign(r) + if err != nil { + return + } + + // mantissa + var fcount int // fractional digit count; valid if <= 0 + z.mant, b, fcount, err = z.mant.scan(r, base, true) + if err != nil { + return + } + + // exponent + var exp int64 + var ebase int + exp, ebase, err = scanExponent(r, true) + if err != nil { + return + } + + // special-case 0 + if len(z.mant) == 0 { + z.prec = prec + z.acc = Exact + z.form = zero + f = z + return + } + // len(z.mant) > 0 + + // The mantissa may have a decimal point (fcount <= 0) and there + // may be a nonzero exponent exp. The decimal point amounts to a + // division by b**(-fcount). An exponent means multiplication by + // ebase**exp. Finally, mantissa normalization (shift left) requires + // a correcting multiplication by 2**(-shiftcount). Multiplications + // are commutative, so we can apply them in any order as long as there + // is no loss of precision. We only have powers of 2 and 10; keep + // track via separate exponents exp2 and exp10. + + // normalize mantissa and get initial binary exponent + var exp2 = int64(len(z.mant))*_W - fnorm(z.mant) + + // determine binary or decimal exponent contribution of decimal point + var exp10 int64 + if fcount < 0 { + // The mantissa has a "decimal" point ddd.dddd; and + // -fcount is the number of digits to the right of '.'. + // Adjust relevant exponent accodingly. + switch b { + case 16: + fcount *= 4 // hexadecimal digits are 4 bits each + fallthrough + case 2: + exp2 += int64(fcount) + default: // b == 10 + exp10 = int64(fcount) + } + // we don't need fcount anymore + } + + // take actual exponent into account + if ebase == 2 { + exp2 += exp + } else { // ebase == 10 + exp10 += exp + } + // we don't need exp anymore + + // apply 2**exp2 + if MinExp <= exp2 && exp2 <= MaxExp { + z.prec = prec + z.form = finite + z.exp = int32(exp2) + f = z + } else { + err = fmt.Errorf("exponent overflow") + return + } + + if exp10 == 0 { + // no decimal exponent to consider + z.round(0) + return + } + // exp10 != 0 + + // apply 10**exp10 + p := new(Float).SetPrec(z.Prec() + 64) // use more bits for p -- TODO(gri) what is the right number? + if exp10 < 0 { + z.uquo(z, p.pow10(-exp10)) + } else { + z.umul(z, p.pow10(exp10)) + } + + return +} + +// These powers of 10 can be represented exactly as a float64. +var pow10tab = [...]float64{ + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, +} + +// pow10 sets z to 10**n and returns z. +// n must not be negative. +func (z *Float) pow10(n int64) *Float { + if n < 0 { + panic("pow10 called with negative argument") + } + + const m = int64(len(pow10tab) - 1) + if n <= m { + return z.SetFloat64(pow10tab[n]) + } + // n > m + + z.SetFloat64(pow10tab[m]) + n -= m + + // use more bits for f than for z + // TODO(gri) what is the right number? + f := new(Float).SetPrec(z.Prec() + 64).SetInt64(10) + + for n > 0 { + if n&1 != 0 { + z.Mul(z, f) + } + f.Mul(f, f) + n >>= 1 + } + + return z +} + +// Parse is like z.Scan(r, base), but instead of reading from an +// io.ByteScanner, it parses the string s. An error is also returned +// if the string contains invalid or trailing bytes not belonging to +// the number. +func (z *Float) Parse(s string, base int) (f *Float, b int, err error) { + r := strings.NewReader(s) + + if f, b, err = z.Scan(r, base); err != nil { + return + } + + // entire string must have been consumed + if ch, err2 := r.ReadByte(); err2 == nil { + err = fmt.Errorf("expected end of string, found %q", ch) + } else if err2 != io.EOF { + err = err2 + } + + return +} + +// ScanFloat is like f.Scan(r, base) with f set to the given precision +// and rounding mode. +func ScanFloat(r io.ByteScanner, base int, prec uint, mode RoundingMode) (f *Float, b int, err error) { + return new(Float).SetPrec(prec).SetMode(mode).Scan(r, base) +} + +// ParseFloat is like f.Parse(s, base) with f set to the given precision +// and rounding mode. +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. +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') + return strconv.AppendInt(buf, int64(x.exp), 10) +} diff --git a/src/cmd/compile/internal/big/floatconv_test.go b/src/cmd/compile/internal/big/floatconv_test.go new file mode 100644 index 0000000000..96c01eed81 --- /dev/null +++ b/src/cmd/compile/internal/big/floatconv_test.go @@ -0,0 +1,397 @@ +// 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 big + +import ( + "math" + "strconv" + "testing" +) + +func TestFloatSetFloat64String(t *testing.T) { + for _, test := range []struct { + s string + x float64 + }{ + // basics + {"0", 0}, + {"-0", -0}, + {"+0", 0}, + {"1", 1}, + {"-1", -1}, + {"+1", 1}, + {"1.234", 1.234}, + {"-1.234", -1.234}, + {"+1.234", 1.234}, + {".1", 0.1}, + {"1.", 1}, + {"+1.", 1}, + + // various zeros + {"0e100", 0}, + {"-0e+100", 0}, + {"+0e-100", 0}, + {"0E100", 0}, + {"-0E+100", 0}, + {"+0E-100", 0}, + + // various decimal exponent formats + {"1.e10", 1e10}, + {"1e+10", 1e10}, + {"+1e-10", 1e-10}, + {"1E10", 1e10}, + {"1.E+10", 1e10}, + {"+1E-10", 1e-10}, + + // misc decimal values + {"3.14159265", 3.14159265}, + {"-687436.79457e-245", -687436.79457e-245}, + {"-687436.79457E245", -687436.79457e245}, + {".0000000000000000000000000000000000000001", 1e-40}, + {"+10000000000000000000000000000000000000000e-0", 1e40}, + + // decimal mantissa, binary exponent + {"0p0", 0}, + {"-0p0", -0}, + {"1p10", 1 << 10}, + {"1p+10", 1 << 10}, + {"+1p-10", 1.0 / (1 << 10)}, + {"1024p-12", 0.25}, + {"-1p10", -1024}, + {"1.5p1", 3}, + + // binary mantissa, decimal exponent + {"0b0", 0}, + {"-0b0", -0}, + {"0b0e+10", 0}, + {"-0b0e-10", -0}, + {"0b1010", 10}, + {"0B1010E2", 1000}, + {"0b.1", 0.5}, + {"0b.001", 0.125}, + {"0b.001e3", 125}, + + // binary mantissa, binary exponent + {"0b0p+10", 0}, + {"-0b0p-10", -0}, + {"0b.1010p4", 10}, + {"0b1p-1", 0.5}, + {"0b001p-3", 0.125}, + {"0b.001p3", 1}, + {"0b0.01p2", 1}, + + // hexadecimal mantissa and exponent + {"0x0", 0}, + {"-0x0", -0}, + {"0x0p+10", 0}, + {"-0x0p-10", -0}, + {"0xff", 255}, + {"0X.8p1", 1}, + {"-0X0.00008p16", -0.5}, + {"0x0.0000000000001p-1022", math.SmallestNonzeroFloat64}, + {"0x1.fffffffffffffp1023", math.MaxFloat64}, + } { + var x Float + x.SetPrec(53) + _, ok := x.SetString(test.s) + if !ok { + t.Errorf("%s: parse error", test.s) + continue + } + f, _ := x.Float64() + want := new(Float).SetFloat64(test.x) + if x.Cmp(want) != 0 { + t.Errorf("%s: got %s (%v); want %v", test.s, &x, f, test.x) + } + } +} + +const ( + below1e23 = 99999999999999974834176 + above1e23 = 100000000000000008388608 +) + +func TestFloat64Format(t *testing.T) { + for _, test := range []struct { + x float64 + format byte + prec int + want string + }{ + {0, 'f', 0, "0"}, + {math.Copysign(0, -1), 'f', 0, "-0"}, + {1, 'f', 0, "1"}, + {-1, 'f', 0, "-1"}, + + {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"}, + + {1.459, 'f', 0, "1"}, + {2.459, 'f', 1, "2.5"}, + {3.459, 'f', 2, "3.46"}, + {4.459, 'f', 3, "4.459"}, + {5.459, 'f', 4, "5.4590"}, + + {0, 'b', 0, "0"}, + {math.Copysign(0, -1), 'b', 0, "-0"}, + {1.0, 'b', 0, "4503599627370496p-52"}, + {-1.0, 'b', 0, "-4503599627370496p-52"}, + {4503599627370496, 'b', 0, "4503599627370496p+0"}, + + {0, 'p', 0, "0"}, + {math.Copysign(0, -1), 'p', 0, "-0"}, + {1024.0, 'p', 0, "0x.8p11"}, + {-1024.0, 'p', 0, "-0x.8p11"}, + + // all test cases below from strconv/ftoa_test.go + {1, 'e', 5, "1.00000e+00"}, + {1, 'f', 5, "1.00000"}, + {1, 'g', 5, "1"}, + // {1, 'g', -1, "1"}, + // {20, 'g', -1, "20"}, + // {1234567.8, 'g', -1, "1.2345678e+06"}, + // {200000, 'g', -1, "200000"}, + // {2000000, 'g', -1, "2e+06"}, + + // g conversion and zero suppression + {400, 'g', 2, "4e+02"}, + {40, 'g', 2, "40"}, + {4, 'g', 2, "4"}, + {.4, 'g', 2, "0.4"}, + {.04, 'g', 2, "0.04"}, + {.004, 'g', 2, "0.004"}, + {.0004, 'g', 2, "0.0004"}, + {.00004, 'g', 2, "4e-05"}, + {.000004, 'g', 2, "4e-06"}, + + {0, 'e', 5, "0.00000e+00"}, + {0, 'f', 5, "0.00000"}, + {0, 'g', 5, "0"}, + // {0, 'g', -1, "0"}, + + {-1, 'e', 5, "-1.00000e+00"}, + {-1, 'f', 5, "-1.00000"}, + {-1, 'g', 5, "-1"}, + // {-1, 'g', -1, "-1"}, + + {12, 'e', 5, "1.20000e+01"}, + {12, 'f', 5, "12.00000"}, + {12, 'g', 5, "12"}, + // {12, 'g', -1, "12"}, + + {123456700, 'e', 5, "1.23457e+08"}, + {123456700, 'f', 5, "123456700.00000"}, + {123456700, 'g', 5, "1.2346e+08"}, + // {123456700, 'g', -1, "1.234567e+08"}, + + {1.2345e6, 'e', 5, "1.23450e+06"}, + {1.2345e6, 'f', 5, "1234500.00000"}, + {1.2345e6, 'g', 5, "1.2345e+06"}, + + {1e23, 'e', 17, "9.99999999999999916e+22"}, + {1e23, 'f', 17, "99999999999999991611392.00000000000000000"}, + {1e23, 'g', 17, "9.9999999999999992e+22"}, + + // {1e23, 'e', -1, "1e+23"}, + // {1e23, 'f', -1, "100000000000000000000000"}, + // {1e23, 'g', -1, "1e+23"}, + + {below1e23, 'e', 17, "9.99999999999999748e+22"}, + {below1e23, 'f', 17, "99999999999999974834176.00000000000000000"}, + {below1e23, 'g', 17, "9.9999999999999975e+22"}, + + // {below1e23, 'e', -1, "9.999999999999997e+22"}, + // {below1e23, 'f', -1, "99999999999999970000000"}, + // {below1e23, 'g', -1, "9.999999999999997e+22"}, + + {above1e23, 'e', 17, "1.00000000000000008e+23"}, + {above1e23, 'f', 17, "100000000000000008388608.00000000000000000"}, + // {above1e23, 'g', 17, "1.0000000000000001e+23"}, + + // {above1e23, 'e', -1, "1.0000000000000001e+23"}, + // {above1e23, 'f', -1, "100000000000000010000000"}, + // {above1e23, 'g', -1, "1.0000000000000001e+23"}, + + // {fdiv(5e-304, 1e20), 'g', -1, "5e-324"}, + // {fdiv(-5e-304, 1e20), 'g', -1, "-5e-324"}, + + // {32, 'g', -1, "32"}, + // {32, 'g', 0, "3e+01"}, + + // {100, 'x', -1, "%x"}, + + // {math.NaN(), 'g', -1, "NaN"}, + // {-math.NaN(), 'g', -1, "NaN"}, + {math.Inf(0), 'g', -1, "+Inf"}, + {math.Inf(-1), 'g', -1, "-Inf"}, + {-math.Inf(0), 'g', -1, "-Inf"}, + + {-1, 'b', -1, "-4503599627370496p-52"}, + + // fixed bugs + {0.9, 'f', 1, "0.9"}, + {0.09, 'f', 1, "0.1"}, + {0.0999, 'f', 1, "0.1"}, + {0.05, 'f', 1, "0.1"}, + {0.05, 'f', 0, "0"}, + {0.5, 'f', 1, "0.5"}, + {0.5, 'f', 0, "0"}, + {1.5, 'f', 0, "2"}, + + // http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/ + // {2.2250738585072012e-308, 'g', -1, "2.2250738585072014e-308"}, + // http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/ + // {2.2250738585072011e-308, 'g', -1, "2.225073858507201e-308"}, + + // Issue 2625. + {383260575764816448, 'f', 0, "383260575764816448"}, + // {383260575764816448, 'g', -1, "3.8326057576481645e+17"}, + } { + f := new(Float).SetFloat64(test.x) + got := f.Format(test.format, test.prec) + if got != test.want { + t.Errorf("%v: got %s; want %s", test, got, test.want) + } + + if test.format == 'b' && test.x == 0 { + continue // 'b' format in strconv.Float requires knowledge of bias for 0.0 + } + if test.format == 'p' { + continue // 'p' format not supported in strconv.Format + } + + // verify that Float format matches strconv format + want := strconv.FormatFloat(test.x, test.format, test.prec, 64) + if got != want { + t.Errorf("%v: got %s; want %s (strconv)", test, got, want) + } + } +} + +func TestFloatFormat(t *testing.T) { + for _, test := range []struct { + x string + prec uint + format byte + digits int + want string + }{ + {"0", 10, 'f', 0, "0"}, + {"-0", 10, 'f', 0, "-0"}, + {"1", 10, 'f', 0, "1"}, + {"-1", 10, 'f', 0, "-1"}, + + {"1.459", 100, 'e', 0, "1e+00"}, + {"2.459", 100, 'e', 1, "2.5e+00"}, + {"3.459", 100, 'e', 2, "3.46e+00"}, + {"4.459", 100, 'e', 3, "4.459e+00"}, + {"5.459", 100, 'e', 4, "5.4590e+00"}, + + {"1.459", 100, 'E', 0, "1E+00"}, + {"2.459", 100, 'E', 1, "2.5E+00"}, + {"3.459", 100, 'E', 2, "3.46E+00"}, + {"4.459", 100, 'E', 3, "4.459E+00"}, + {"5.459", 100, 'E', 4, "5.4590E+00"}, + + {"1.459", 100, 'f', 0, "1"}, + {"2.459", 100, 'f', 1, "2.5"}, + {"3.459", 100, 'f', 2, "3.46"}, + {"4.459", 100, 'f', 3, "4.459"}, + {"5.459", 100, 'f', 4, "5.4590"}, + + {"1.459", 100, 'g', 0, "1"}, + {"2.459", 100, 'g', 1, "2"}, + {"3.459", 100, 'g', 2, "3.5"}, + {"4.459", 100, 'g', 3, "4.46"}, + {"5.459", 100, 'g', 4, "5.459"}, + + {"1459", 53, 'g', 0, "1e+03"}, + {"2459", 53, 'g', 1, "2e+03"}, + {"3459", 53, 'g', 2, "3.5e+03"}, + {"4459", 53, 'g', 3, "4.46e+03"}, + {"5459", 53, 'g', 4, "5459"}, + + {"1459", 53, 'G', 0, "1E+03"}, + {"2459", 53, 'G', 1, "2E+03"}, + {"3459", 53, 'G', 2, "3.5E+03"}, + {"4459", 53, 'G', 3, "4.46E+03"}, + {"5459", 53, 'G', 4, "5459"}, + + {"3", 10, 'e', 40, "3.0000000000000000000000000000000000000000e+00"}, + {"3", 10, 'f', 40, "3.0000000000000000000000000000000000000000"}, + {"3", 10, 'g', 40, "3"}, + + {"3e40", 100, 'e', 40, "3.0000000000000000000000000000000000000000e+40"}, + {"3e40", 100, 'f', 4, "30000000000000000000000000000000000000000.0000"}, + {"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"}, + {"1e-1000000", 64, 'p', 0, "0x.efb4542cc8ca418ap-3321928"}, + {"1e-1000000000", 64, 'p', 0, "0x.8a64dd983a4c7dabp-1538481528"}, + + // TODO(gri) need tests for actual large Floats + + {"0", 53, 'b', 0, "0"}, + {"-0", 53, 'b', 0, "-0"}, + {"1.0", 53, 'b', 0, "4503599627370496p-52"}, + {"-1.0", 53, 'b', 0, "-4503599627370496p-52"}, + {"4503599627370496", 53, 'b', 0, "4503599627370496p+0"}, + + // issue 9939 + {"3", 350, 'b', 0, "1720123961992553633708115671476565205597423741876210842803191629540192157066363606052513914832594264915968p-348"}, + {"03", 350, 'b', 0, "1720123961992553633708115671476565205597423741876210842803191629540192157066363606052513914832594264915968p-348"}, + {"3.", 350, 'b', 0, "1720123961992553633708115671476565205597423741876210842803191629540192157066363606052513914832594264915968p-348"}, + {"3.0", 350, 'b', 0, "1720123961992553633708115671476565205597423741876210842803191629540192157066363606052513914832594264915968p-348"}, + {"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"}, + + {"0", 64, 'p', 0, "0"}, + {"-0", 64, 'p', 0, "-0"}, + {"1024.0", 64, 'p', 0, "0x.8p11"}, + {"-1024.0", 64, 'p', 0, "-0x.8p11"}, + + // unsupported format + {"3.14", 64, 'x', 0, "%x"}, + } { + f, _, err := ParseFloat(test.x, 0, test.prec, ToNearestEven) + if err != nil { + t.Errorf("%v: %s", test, err) + continue + } + + got := f.Format(test.format, test.digits) + if got != test.want { + t.Errorf("%v: got %s; want %s", test, got, test.want) + } + + // compare with strconv.FormatFloat output if possible + // ('p' format is not supported by strconv.FormatFloat, + // and its output for 0.0 prints a biased exponent value + // as in 0p-1074 which makes no sense to emulate here) + if test.prec == 53 && test.format != 'p' && f.Sign() != 0 { + f64, acc := f.Float64() + if acc != Exact { + t.Errorf("%v: expected exact conversion to float64", test) + continue + } + got := strconv.FormatFloat(f64, test.format, test.digits, 64) + if got != test.want { + t.Errorf("%v: got %s; want %s", test, got, test.want) + } + } + } +} diff --git a/src/cmd/compile/internal/big/floatexample_test.go b/src/cmd/compile/internal/big/floatexample_test.go new file mode 100644 index 0000000000..7db10238bc --- /dev/null +++ b/src/cmd/compile/internal/big/floatexample_test.go @@ -0,0 +1,111 @@ +// 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 big_test + +import ( + "fmt" + "math" + "math/big" +) + +func ExampleFloat_Add() { + // Operating on numbers of different precision. + var x, y, z big.Float + x.SetInt64(1000) // x is automatically set to 64bit precision + y.SetFloat64(2.718281828) // y is automatically set to 53bit precision + z.SetPrec(32) + z.Add(&x, &y) + fmt.Printf("x = %s (%s, prec = %d, acc = %s)\n", &x, x.Format('p', 0), x.Prec(), x.Acc()) + 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) +} + +func Example_Shift() { + // Implementing Float "shift" by modifying the (binary) exponents directly. + for s := -5; s <= 5; s++ { + x := big.NewFloat(0.5) + x.SetMantExp(x, x.MantExp(nil)+s) // shift x by s + fmt.Println(x) + } + // Output: + // 0.015625 + // 0.03125 + // 0.0625 + // 0.125 + // 0.25 + // 0.5 + // 1 + // 2 + // 4 + // 8 + // 16 +} + +func ExampleFloat_Cmp() { + inf := math.Inf(1) + zero := 0.0 + + operands := []float64{-inf, -1.2, -zero, 0, +1.2, +inf} + + fmt.Println(" x y cmp") + fmt.Println("---------------") + for _, x64 := range operands { + x := big.NewFloat(x64) + for _, y64 := range operands { + y := big.NewFloat(y64) + fmt.Printf("%4s %4s %3d\n", x, y, x.Cmp(y)) + } + fmt.Println() + } + + // Output: + // x y cmp + // --------------- + // -Inf -Inf 0 + // -Inf -1.2 -1 + // -Inf -0 -1 + // -Inf 0 -1 + // -Inf 1.2 -1 + // -Inf +Inf -1 + // + // -1.2 -Inf 1 + // -1.2 -1.2 0 + // -1.2 -0 -1 + // -1.2 0 -1 + // -1.2 1.2 -1 + // -1.2 +Inf -1 + // + // -0 -Inf 1 + // -0 -1.2 1 + // -0 -0 0 + // -0 0 0 + // -0 1.2 -1 + // -0 +Inf -1 + // + // 0 -Inf 1 + // 0 -1.2 1 + // 0 -0 0 + // 0 0 0 + // 0 1.2 -1 + // 0 +Inf -1 + // + // 1.2 -Inf 1 + // 1.2 -1.2 1 + // 1.2 -0 1 + // 1.2 0 1 + // 1.2 1.2 0 + // 1.2 +Inf -1 + // + // +Inf -Inf 1 + // +Inf -1.2 1 + // +Inf -0 1 + // +Inf 0 1 + // +Inf 1.2 1 + // +Inf +Inf 0 +} diff --git a/src/cmd/compile/internal/big/ftoa.go b/src/cmd/compile/internal/big/ftoa.go new file mode 100644 index 0000000000..0a9edfd7b2 --- /dev/null +++ b/src/cmd/compile/internal/big/ftoa.go @@ -0,0 +1,190 @@ +// 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. + +// 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 + +package big + +import "strconv" + +// TODO(gri) Consider moving sign into decimal - could make the signatures below cleaner. + +// 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") + } + + // 1) convert Float to multiprecision decimal + var mant nat + if f.form == finite { + mant = f.mant + } + var d decimal + d.init(mant, int(f.exp)-f.mant.bitLen()) + + // 2) round to desired precision + shortest := false + if prec < 0 { + shortest = true + panic("unimplemented") + // TODO(gri) complete this + // roundShortest(&d, f.mant, int(f.exp)) + // Precision for shortest representation mode. + switch fmt { + case 'e', 'E': + prec = len(d.mant) - 1 + case 'f': + prec = max(len(d.mant)-d.exp, 0) + case 'g', 'G': + prec = len(d.mant) + } + } else { + // round appropriately + switch fmt { + case 'e', 'E': + // one digit before and number of digits after decimal point + d.round(1 + prec) + case 'f': + // number of digits before and after decimal point + d.round(d.exp + prec) + case 'g', 'G': + if prec == 0 { + prec = 1 + } + d.round(prec) + } + } + + // 3) read digits out and format + switch fmt { + case 'e', 'E': + return fmtE(buf, fmt, prec, f.neg, d) + case 'f': + return fmtF(buf, prec, f.neg, d) + case 'g', 'G': + // trim trailing fractional zeros in %e format + eprec := prec + if eprec > len(d.mant) && len(d.mant) >= d.exp { + eprec = len(d.mant) + } + // %e is used if the exponent from the conversion + // is less than -4 or greater than or equal to the precision. + // If precision was the shortest possible, use eprec = 6 for + // this decision. + if shortest { + eprec = 6 + } + exp := d.exp - 1 + if exp < -4 || exp >= eprec { + if prec > len(d.mant) { + prec = len(d.mant) + } + return fmtE(buf, fmt+'e'-'g', prec-1, f.neg, d) + } + if prec > d.exp { + prec = len(d.mant) + } + return fmtF(buf, max(prec-d.exp, 0), f.neg, d) + } + + // unknown format + 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, '-') + } + + // first digit + ch := byte('0') + if len(d.mant) > 0 { + ch = d.mant[0] + } + buf = append(buf, ch) + + // .moredigits + if prec > 0 { + buf = append(buf, '.') + i := 1 + m := min(len(d.mant), prec+1) + if i < m { + buf = append(buf, d.mant[i:m]...) + i = m + } + for ; i <= prec; i++ { + buf = append(buf, '0') + } + } + + // e± + buf = append(buf, fmt) + var exp int64 + if len(d.mant) > 0 { + exp = int64(d.exp) - 1 // -1 because first digit was printed before '.' + } + if exp < 0 { + ch = '-' + exp = -exp + } else { + ch = '+' + } + buf = append(buf, ch) + + // dd...d + if exp < 10 { + buf = append(buf, '0') // at least 2 exponent digits + } + 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, '-') + } + + // integer, padded with zeros as needed + if d.exp > 0 { + m := min(len(d.mant), d.exp) + buf = append(buf, d.mant[:m]...) + for ; m < d.exp; m++ { + buf = append(buf, '0') + } + } else { + buf = append(buf, '0') + } + + // fraction + if prec > 0 { + buf = append(buf, '.') + for i := 0; i < prec; i++ { + ch := byte('0') + if j := d.exp + i; 0 <= j && j < len(d.mant) { + ch = d.mant[j] + } + buf = append(buf, ch) + } + } + + return buf +} + +func min(x, y int) int { + if x < y { + return x + } + return y +} diff --git a/src/cmd/compile/internal/big/gcd_test.go b/src/cmd/compile/internal/big/gcd_test.go new file mode 100644 index 0000000000..c0b9f58300 --- /dev/null +++ b/src/cmd/compile/internal/big/gcd_test.go @@ -0,0 +1,47 @@ +// Copyright 2012 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. + +// This file implements a GCD benchmark. +// Usage: go test math/big -test.bench GCD + +package big + +import ( + "math/rand" + "testing" +) + +// randInt returns a pseudo-random Int in the range [1<<(size-1), (1< 1<<(size-1) +} + +func runGCD(b *testing.B, aSize, bSize uint) { + b.StopTimer() + var r = rand.New(rand.NewSource(1234)) + aa := randInt(r, aSize) + bb := randInt(r, bSize) + b.StartTimer() + for i := 0; i < b.N; i++ { + new(Int).GCD(nil, nil, aa, bb) + } +} + +func BenchmarkGCD10x10(b *testing.B) { runGCD(b, 10, 10) } +func BenchmarkGCD10x100(b *testing.B) { runGCD(b, 10, 100) } +func BenchmarkGCD10x1000(b *testing.B) { runGCD(b, 10, 1000) } +func BenchmarkGCD10x10000(b *testing.B) { runGCD(b, 10, 10000) } +func BenchmarkGCD10x100000(b *testing.B) { runGCD(b, 10, 100000) } +func BenchmarkGCD100x100(b *testing.B) { runGCD(b, 100, 100) } +func BenchmarkGCD100x1000(b *testing.B) { runGCD(b, 100, 1000) } +func BenchmarkGCD100x10000(b *testing.B) { runGCD(b, 100, 10000) } +func BenchmarkGCD100x100000(b *testing.B) { runGCD(b, 100, 100000) } +func BenchmarkGCD1000x1000(b *testing.B) { runGCD(b, 1000, 1000) } +func BenchmarkGCD1000x10000(b *testing.B) { runGCD(b, 1000, 10000) } +func BenchmarkGCD1000x100000(b *testing.B) { runGCD(b, 1000, 100000) } +func BenchmarkGCD10000x10000(b *testing.B) { runGCD(b, 10000, 10000) } +func BenchmarkGCD10000x100000(b *testing.B) { runGCD(b, 10000, 100000) } +func BenchmarkGCD100000x100000(b *testing.B) { runGCD(b, 100000, 100000) } diff --git a/src/cmd/compile/internal/big/hilbert_test.go b/src/cmd/compile/internal/big/hilbert_test.go new file mode 100644 index 0000000000..1a84341b3c --- /dev/null +++ b/src/cmd/compile/internal/big/hilbert_test.go @@ -0,0 +1,160 @@ +// Copyright 2009 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. + +// A little test program and benchmark for rational arithmetics. +// Computes a Hilbert matrix, its inverse, multiplies them +// and verifies that the product is the identity matrix. + +package big + +import ( + "fmt" + "testing" +) + +type matrix struct { + n, m int + a []*Rat +} + +func (a *matrix) at(i, j int) *Rat { + if !(0 <= i && i < a.n && 0 <= j && j < a.m) { + panic("index out of range") + } + return a.a[i*a.m+j] +} + +func (a *matrix) set(i, j int, x *Rat) { + if !(0 <= i && i < a.n && 0 <= j && j < a.m) { + panic("index out of range") + } + a.a[i*a.m+j] = x +} + +func newMatrix(n, m int) *matrix { + if !(0 <= n && 0 <= m) { + panic("illegal matrix") + } + a := new(matrix) + a.n = n + a.m = m + a.a = make([]*Rat, n*m) + return a +} + +func newUnit(n int) *matrix { + a := newMatrix(n, n) + for i := 0; i < n; i++ { + for j := 0; j < n; j++ { + x := NewRat(0, 1) + if i == j { + x.SetInt64(1) + } + a.set(i, j, x) + } + } + return a +} + +func newHilbert(n int) *matrix { + a := newMatrix(n, n) + for i := 0; i < n; i++ { + for j := 0; j < n; j++ { + a.set(i, j, NewRat(1, int64(i+j+1))) + } + } + return a +} + +func newInverseHilbert(n int) *matrix { + a := newMatrix(n, n) + for i := 0; i < n; i++ { + for j := 0; j < n; j++ { + x1 := new(Rat).SetInt64(int64(i + j + 1)) + x2 := new(Rat).SetInt(new(Int).Binomial(int64(n+i), int64(n-j-1))) + x3 := new(Rat).SetInt(new(Int).Binomial(int64(n+j), int64(n-i-1))) + x4 := new(Rat).SetInt(new(Int).Binomial(int64(i+j), int64(i))) + + x1.Mul(x1, x2) + x1.Mul(x1, x3) + x1.Mul(x1, x4) + x1.Mul(x1, x4) + + if (i+j)&1 != 0 { + x1.Neg(x1) + } + + a.set(i, j, x1) + } + } + return a +} + +func (a *matrix) mul(b *matrix) *matrix { + if a.m != b.n { + panic("illegal matrix multiply") + } + c := newMatrix(a.n, b.m) + for i := 0; i < c.n; i++ { + for j := 0; j < c.m; j++ { + x := NewRat(0, 1) + for k := 0; k < a.m; k++ { + x.Add(x, new(Rat).Mul(a.at(i, k), b.at(k, j))) + } + c.set(i, j, x) + } + } + return c +} + +func (a *matrix) eql(b *matrix) bool { + if a.n != b.n || a.m != b.m { + return false + } + for i := 0; i < a.n; i++ { + for j := 0; j < a.m; j++ { + if a.at(i, j).Cmp(b.at(i, j)) != 0 { + return false + } + } + } + return true +} + +func (a *matrix) String() string { + s := "" + for i := 0; i < a.n; i++ { + for j := 0; j < a.m; j++ { + s += fmt.Sprintf("\t%s", a.at(i, j)) + } + s += "\n" + } + return s +} + +func doHilbert(t *testing.T, n int) { + a := newHilbert(n) + b := newInverseHilbert(n) + I := newUnit(n) + ab := a.mul(b) + if !ab.eql(I) { + if t == nil { + panic("Hilbert failed") + } + t.Errorf("a = %s\n", a) + t.Errorf("b = %s\n", b) + t.Errorf("a*b = %s\n", ab) + t.Errorf("I = %s\n", I) + } +} + +func TestHilbert(t *testing.T) { + doHilbert(t, 10) +} + +func BenchmarkHilbert(b *testing.B) { + for i := 0; i < b.N; i++ { + doHilbert(nil, 10) + } +} diff --git a/src/cmd/compile/internal/big/int.go b/src/cmd/compile/internal/big/int.go new file mode 100644 index 0000000000..7b419bf688 --- /dev/null +++ b/src/cmd/compile/internal/big/int.go @@ -0,0 +1,848 @@ +// Copyright 2009 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. + +// This file implements signed multi-precision integers. + +package big + +import ( + "fmt" + "io" + "math/rand" + "strings" +) + +// An Int represents a signed multi-precision integer. +// The zero value for an Int represents the value 0. +type Int struct { + neg bool // sign + abs nat // absolute value of the integer +} + +var intOne = &Int{false, natOne} + +// Sign returns: +// +// -1 if x < 0 +// 0 if x == 0 +// +1 if x > 0 +// +func (x *Int) Sign() int { + if len(x.abs) == 0 { + return 0 + } + if x.neg { + return -1 + } + return 1 +} + +// SetInt64 sets z to x and returns z. +func (z *Int) SetInt64(x int64) *Int { + neg := false + if x < 0 { + neg = true + x = -x + } + z.abs = z.abs.setUint64(uint64(x)) + z.neg = neg + return z +} + +// SetUint64 sets z to x and returns z. +func (z *Int) SetUint64(x uint64) *Int { + z.abs = z.abs.setUint64(x) + z.neg = false + return z +} + +// NewInt allocates and returns a new Int set to x. +func NewInt(x int64) *Int { + return new(Int).SetInt64(x) +} + +// Set sets z to x and returns z. +func (z *Int) Set(x *Int) *Int { + if z != x { + z.abs = z.abs.set(x.abs) + z.neg = x.neg + } + return z +} + +// Bits provides raw (unchecked but fast) access to x by returning its +// absolute value as a little-endian Word slice. The result and x share +// the same underlying array. +// Bits is intended to support implementation of missing low-level Int +// functionality outside this package; it should be avoided otherwise. +func (x *Int) Bits() []Word { + return x.abs +} + +// SetBits provides raw (unchecked but fast) access to z by setting its +// value to abs, interpreted as a little-endian Word slice, and returning +// z. The result and abs share the same underlying array. +// SetBits is intended to support implementation of missing low-level Int +// functionality outside this package; it should be avoided otherwise. +func (z *Int) SetBits(abs []Word) *Int { + z.abs = nat(abs).norm() + z.neg = false + return z +} + +// Abs sets z to |x| (the absolute value of x) and returns z. +func (z *Int) Abs(x *Int) *Int { + z.Set(x) + z.neg = false + return z +} + +// Neg sets z to -x and returns z. +func (z *Int) Neg(x *Int) *Int { + z.Set(x) + z.neg = len(z.abs) > 0 && !z.neg // 0 has no sign + return z +} + +// Add sets z to the sum x+y and returns z. +func (z *Int) Add(x, y *Int) *Int { + neg := x.neg + if x.neg == y.neg { + // x + y == x + y + // (-x) + (-y) == -(x + y) + z.abs = z.abs.add(x.abs, y.abs) + } else { + // x + (-y) == x - y == -(y - x) + // (-x) + y == y - x == -(x - y) + if x.abs.cmp(y.abs) >= 0 { + z.abs = z.abs.sub(x.abs, y.abs) + } else { + neg = !neg + z.abs = z.abs.sub(y.abs, x.abs) + } + } + z.neg = len(z.abs) > 0 && neg // 0 has no sign + return z +} + +// Sub sets z to the difference x-y and returns z. +func (z *Int) Sub(x, y *Int) *Int { + neg := x.neg + if x.neg != y.neg { + // x - (-y) == x + y + // (-x) - y == -(x + y) + z.abs = z.abs.add(x.abs, y.abs) + } else { + // x - y == x - y == -(y - x) + // (-x) - (-y) == y - x == -(x - y) + if x.abs.cmp(y.abs) >= 0 { + z.abs = z.abs.sub(x.abs, y.abs) + } else { + neg = !neg + z.abs = z.abs.sub(y.abs, x.abs) + } + } + z.neg = len(z.abs) > 0 && neg // 0 has no sign + return z +} + +// Mul sets z to the product x*y and returns z. +func (z *Int) Mul(x, y *Int) *Int { + // x * y == x * y + // x * (-y) == -(x * y) + // (-x) * y == -(x * y) + // (-x) * (-y) == x * y + z.abs = z.abs.mul(x.abs, y.abs) + z.neg = len(z.abs) > 0 && x.neg != y.neg // 0 has no sign + return z +} + +// MulRange sets z to the product of all integers +// in the range [a, b] inclusively and returns z. +// If a > b (empty range), the result is 1. +func (z *Int) MulRange(a, b int64) *Int { + switch { + case a > b: + return z.SetInt64(1) // empty range + case a <= 0 && b >= 0: + return z.SetInt64(0) // range includes 0 + } + // a <= b && (b < 0 || a > 0) + + neg := false + if a < 0 { + neg = (b-a)&1 == 0 + a, b = -b, -a + } + + z.abs = z.abs.mulRange(uint64(a), uint64(b)) + z.neg = neg + return z +} + +// Binomial sets z to the binomial coefficient of (n, k) and returns z. +func (z *Int) Binomial(n, k int64) *Int { + // reduce the number of multiplications by reducing k + if n/2 < k && k <= n { + k = n - k // Binomial(n, k) == Binomial(n, n-k) + } + var a, b Int + a.MulRange(n-k+1, n) + b.MulRange(1, k) + return z.Quo(&a, &b) +} + +// Quo sets z to the quotient x/y for y != 0 and returns z. +// If y == 0, a division-by-zero run-time panic occurs. +// Quo implements truncated division (like Go); see QuoRem for more details. +func (z *Int) Quo(x, y *Int) *Int { + z.abs, _ = z.abs.div(nil, x.abs, y.abs) + z.neg = len(z.abs) > 0 && x.neg != y.neg // 0 has no sign + return z +} + +// Rem sets z to the remainder x%y for y != 0 and returns z. +// If y == 0, a division-by-zero run-time panic occurs. +// Rem implements truncated modulus (like Go); see QuoRem for more details. +func (z *Int) Rem(x, y *Int) *Int { + _, z.abs = nat(nil).div(z.abs, x.abs, y.abs) + z.neg = len(z.abs) > 0 && x.neg // 0 has no sign + return z +} + +// QuoRem sets z to the quotient x/y and r to the remainder x%y +// and returns the pair (z, r) for y != 0. +// If y == 0, a division-by-zero run-time panic occurs. +// +// QuoRem implements T-division and modulus (like Go): +// +// q = x/y with the result truncated to zero +// r = x - y*q +// +// (See Daan Leijen, ``Division and Modulus for Computer Scientists''.) +// See DivMod for Euclidean division and modulus (unlike Go). +// +func (z *Int) QuoRem(x, y, r *Int) (*Int, *Int) { + z.abs, r.abs = z.abs.div(r.abs, x.abs, y.abs) + z.neg, r.neg = len(z.abs) > 0 && x.neg != y.neg, len(r.abs) > 0 && x.neg // 0 has no sign + return z, r +} + +// Div sets z to the quotient x/y for y != 0 and returns z. +// If y == 0, a division-by-zero run-time panic occurs. +// Div implements Euclidean division (unlike Go); see DivMod for more details. +func (z *Int) Div(x, y *Int) *Int { + y_neg := y.neg // z may be an alias for y + var r Int + z.QuoRem(x, y, &r) + if r.neg { + if y_neg { + z.Add(z, intOne) + } else { + z.Sub(z, intOne) + } + } + return z +} + +// Mod sets z to the modulus x%y for y != 0 and returns z. +// If y == 0, a division-by-zero run-time panic occurs. +// Mod implements Euclidean modulus (unlike Go); see DivMod for more details. +func (z *Int) Mod(x, y *Int) *Int { + y0 := y // save y + if z == y || alias(z.abs, y.abs) { + y0 = new(Int).Set(y) + } + var q Int + q.QuoRem(x, y, z) + if z.neg { + if y0.neg { + z.Sub(z, y0) + } else { + z.Add(z, y0) + } + } + return z +} + +// DivMod sets z to the quotient x div y and m to the modulus x mod y +// and returns the pair (z, m) for y != 0. +// If y == 0, a division-by-zero run-time panic occurs. +// +// DivMod implements Euclidean division and modulus (unlike Go): +// +// q = x div y such that +// m = x - y*q with 0 <= m < |q| +// +// (See Raymond T. Boute, ``The Euclidean definition of the functions +// div and mod''. ACM Transactions on Programming Languages and +// Systems (TOPLAS), 14(2):127-144, New York, NY, USA, 4/1992. +// ACM press.) +// See QuoRem for T-division and modulus (like Go). +// +func (z *Int) DivMod(x, y, m *Int) (*Int, *Int) { + y0 := y // save y + if z == y || alias(z.abs, y.abs) { + y0 = new(Int).Set(y) + } + z.QuoRem(x, y, m) + if m.neg { + if y0.neg { + z.Add(z, intOne) + m.Sub(m, y0) + } else { + z.Sub(z, intOne) + m.Add(m, y0) + } + } + return z, m +} + +// Cmp compares x and y and returns: +// +// -1 if x < y +// 0 if x == y +// +1 if x > y +// +func (x *Int) Cmp(y *Int) (r int) { + // x cmp y == x cmp y + // x cmp (-y) == x + // (-x) cmp y == y + // (-x) cmp (-y) == -(x cmp y) + switch { + case x.neg == y.neg: + r = x.abs.cmp(y.abs) + if x.neg { + r = -r + } + case x.neg: + r = -1 + default: + r = 1 + } + return +} + +// low32 returns the least significant 32 bits of z. +func low32(z nat) uint32 { + if len(z) == 0 { + return 0 + } + return uint32(z[0]) +} + +// low64 returns the least significant 64 bits of z. +func low64(z nat) uint64 { + if len(z) == 0 { + return 0 + } + v := uint64(z[0]) + if _W == 32 && len(z) > 1 { + v |= uint64(z[1]) << 32 + } + return v +} + +// Int64 returns the int64 representation of x. +// If x cannot be represented in an int64, the result is undefined. +func (x *Int) Int64() int64 { + v := int64(low64(x.abs)) + if x.neg { + v = -v + } + return v +} + +// Uint64 returns the uint64 representation of x. +// If x cannot be represented in a uint64, the result is undefined. +func (x *Int) Uint64() uint64 { + return low64(x.abs) +} + +// SetString sets z to the value of s, interpreted in the given base, +// and returns z and a boolean indicating success. If SetString fails, +// the value of z is undefined but the returned value is nil. +// +// The base argument must be 0 or a value between 2 and MaxBase. If the base +// is 0, the string prefix determines the actual conversion base. A prefix of +// ``0x'' or ``0X'' selects base 16; the ``0'' prefix selects base 8, and a +// ``0b'' or ``0B'' prefix selects base 2. Otherwise the selected base is 10. +// +func (z *Int) SetString(s string, base int) (*Int, bool) { + r := strings.NewReader(s) + _, _, err := z.scan(r, base) + if err != nil { + return nil, false + } + _, err = r.ReadByte() + if err != io.EOF { + return nil, false + } + return z, true // err == io.EOF => scan consumed all of s +} + +// SetBytes interprets buf as the bytes of a big-endian unsigned +// integer, sets z to that value, and returns z. +func (z *Int) SetBytes(buf []byte) *Int { + z.abs = z.abs.setBytes(buf) + z.neg = false + return z +} + +// Bytes returns the absolute value of x as a big-endian byte slice. +func (x *Int) Bytes() []byte { + buf := make([]byte, len(x.abs)*_S) + return buf[x.abs.bytes(buf):] +} + +// BitLen returns the length of the absolute value of x in bits. +// The bit length of 0 is 0. +func (x *Int) BitLen() int { + return x.abs.bitLen() +} + +// Exp sets z = x**y mod |m| (i.e. the sign of m is ignored), and returns z. +// If y <= 0, the result is 1 mod |m|; if m == nil or m == 0, z = x**y. +// See Knuth, volume 2, section 4.6.3. +func (z *Int) Exp(x, y, m *Int) *Int { + var yWords nat + if !y.neg { + yWords = y.abs + } + // y >= 0 + + var mWords nat + if m != nil { + mWords = m.abs // m.abs may be nil for m == 0 + } + + z.abs = z.abs.expNN(x.abs, yWords, mWords) + z.neg = len(z.abs) > 0 && x.neg && len(yWords) > 0 && yWords[0]&1 == 1 // 0 has no sign + if z.neg && len(mWords) > 0 { + // make modulus result positive + z.abs = z.abs.sub(mWords, z.abs) // z == x**y mod |m| && 0 <= z < |m| + z.neg = false + } + + return z +} + +// GCD sets z to the greatest common divisor of a and b, which both must +// be > 0, and returns z. +// If x and y are not nil, GCD sets x and y such that z = a*x + b*y. +// If either a or b is <= 0, GCD sets z = x = y = 0. +func (z *Int) GCD(x, y, a, b *Int) *Int { + if a.Sign() <= 0 || b.Sign() <= 0 { + z.SetInt64(0) + if x != nil { + x.SetInt64(0) + } + if y != nil { + y.SetInt64(0) + } + return z + } + if x == nil && y == nil { + return z.binaryGCD(a, b) + } + + A := new(Int).Set(a) + B := new(Int).Set(b) + + X := new(Int) + Y := new(Int).SetInt64(1) + + lastX := new(Int).SetInt64(1) + lastY := new(Int) + + q := new(Int) + temp := new(Int) + + for len(B.abs) > 0 { + r := new(Int) + q, r = q.QuoRem(A, B, r) + + A, B = B, r + + temp.Set(X) + X.Mul(X, q) + X.neg = !X.neg + X.Add(X, lastX) + lastX.Set(temp) + + temp.Set(Y) + Y.Mul(Y, q) + Y.neg = !Y.neg + Y.Add(Y, lastY) + lastY.Set(temp) + } + + if x != nil { + *x = *lastX + } + + if y != nil { + *y = *lastY + } + + *z = *A + return z +} + +// binaryGCD sets z to the greatest common divisor of a and b, which both must +// be > 0, and returns z. +// See Knuth, The Art of Computer Programming, Vol. 2, Section 4.5.2, Algorithm B. +func (z *Int) binaryGCD(a, b *Int) *Int { + u := z + v := new(Int) + + // use one Euclidean iteration to ensure that u and v are approx. the same size + switch { + case len(a.abs) > len(b.abs): + u.Set(b) + v.Rem(a, b) + case len(a.abs) < len(b.abs): + u.Set(a) + v.Rem(b, a) + default: + u.Set(a) + v.Set(b) + } + + // v might be 0 now + if len(v.abs) == 0 { + return u + } + // u > 0 && v > 0 + + // determine largest k such that u = u' << k, v = v' << k + k := u.abs.trailingZeroBits() + if vk := v.abs.trailingZeroBits(); vk < k { + k = vk + } + u.Rsh(u, k) + v.Rsh(v, k) + + // determine t (we know that u > 0) + t := new(Int) + if u.abs[0]&1 != 0 { + // u is odd + t.Neg(v) + } else { + t.Set(u) + } + + for len(t.abs) > 0 { + // reduce t + t.Rsh(t, t.abs.trailingZeroBits()) + if t.neg { + v, t = t, v + v.neg = len(v.abs) > 0 && !v.neg // 0 has no sign + } else { + u, t = t, u + } + t.Sub(u, v) + } + + return z.Lsh(u, k) +} + +// ProbablyPrime performs n Miller-Rabin tests to check whether x is prime. +// If it returns true, x is prime with probability 1 - 1/4^n. +// If it returns false, x is not prime. n must be > 0. +func (x *Int) ProbablyPrime(n int) bool { + if n <= 0 { + panic("non-positive n for ProbablyPrime") + } + return !x.neg && x.abs.probablyPrime(n) +} + +// Rand sets z to a pseudo-random number in [0, n) and returns z. +func (z *Int) Rand(rnd *rand.Rand, n *Int) *Int { + z.neg = false + if n.neg == true || len(n.abs) == 0 { + z.abs = nil + return z + } + z.abs = z.abs.random(rnd, n.abs, n.abs.bitLen()) + return z +} + +// ModInverse sets z to the multiplicative inverse of g in the ring ℤ/nℤ +// and returns z. If g and n are not relatively prime, the result is undefined. +func (z *Int) ModInverse(g, n *Int) *Int { + var d Int + d.GCD(z, nil, g, n) + // x and y are such that g*x + n*y = d. Since g and n are + // relatively prime, d = 1. Taking that modulo n results in + // g*x = 1, therefore x is the inverse element. + if z.neg { + z.Add(z, n) + } + return z +} + +// 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) + z.neg = x.neg + return z +} + +// Rsh sets z = x >> n and returns z. +func (z *Int) Rsh(x *Int, n uint) *Int { + if x.neg { + // (-x) >> s == ^(x-1) >> s == ^((x-1) >> s) == -(((x-1) >> s) + 1) + t := z.abs.sub(x.abs, natOne) // no underflow because |x| > 0 + t = t.shr(t, n) + z.abs = t.add(t, natOne) + z.neg = true // z cannot be zero if x is negative + return z + } + + z.abs = z.abs.shr(x.abs, n) + z.neg = false + return z +} + +// Bit returns the value of the i'th bit of x. That is, it +// returns (x>>i)&1. The bit index i must be >= 0. +func (x *Int) Bit(i int) uint { + if i == 0 { + // optimization for common case: odd/even test of x + if len(x.abs) > 0 { + return uint(x.abs[0] & 1) // bit 0 is same for -x + } + return 0 + } + if i < 0 { + panic("negative bit index") + } + if x.neg { + t := nat(nil).sub(x.abs, natOne) + return t.bit(uint(i)) ^ 1 + } + + return x.abs.bit(uint(i)) +} + +// SetBit sets z to x, with x's i'th bit set to b (0 or 1). +// That is, if b is 1 SetBit sets z = x | (1 << i); +// if b is 0 SetBit sets z = x &^ (1 << i). If b is not 0 or 1, +// SetBit will panic. +func (z *Int) SetBit(x *Int, i int, b uint) *Int { + if i < 0 { + panic("negative bit index") + } + if x.neg { + t := z.abs.sub(x.abs, natOne) + t = t.setBit(t, uint(i), b^1) + z.abs = t.add(t, natOne) + z.neg = len(z.abs) > 0 + return z + } + z.abs = z.abs.setBit(x.abs, uint(i), b) + z.neg = false + return z +} + +// And sets z = x & y and returns z. +func (z *Int) And(x, y *Int) *Int { + if x.neg == y.neg { + if x.neg { + // (-x) & (-y) == ^(x-1) & ^(y-1) == ^((x-1) | (y-1)) == -(((x-1) | (y-1)) + 1) + x1 := nat(nil).sub(x.abs, natOne) + y1 := nat(nil).sub(y.abs, natOne) + z.abs = z.abs.add(z.abs.or(x1, y1), natOne) + z.neg = true // z cannot be zero if x and y are negative + return z + } + + // x & y == x & y + z.abs = z.abs.and(x.abs, y.abs) + z.neg = false + return z + } + + // x.neg != y.neg + if x.neg { + x, y = y, x // & is symmetric + } + + // x & (-y) == x & ^(y-1) == x &^ (y-1) + y1 := nat(nil).sub(y.abs, natOne) + z.abs = z.abs.andNot(x.abs, y1) + z.neg = false + return z +} + +// AndNot sets z = x &^ y and returns z. +func (z *Int) AndNot(x, y *Int) *Int { + if x.neg == y.neg { + if x.neg { + // (-x) &^ (-y) == ^(x-1) &^ ^(y-1) == ^(x-1) & (y-1) == (y-1) &^ (x-1) + x1 := nat(nil).sub(x.abs, natOne) + y1 := nat(nil).sub(y.abs, natOne) + z.abs = z.abs.andNot(y1, x1) + z.neg = false + return z + } + + // x &^ y == x &^ y + z.abs = z.abs.andNot(x.abs, y.abs) + z.neg = false + return z + } + + if x.neg { + // (-x) &^ y == ^(x-1) &^ y == ^(x-1) & ^y == ^((x-1) | y) == -(((x-1) | y) + 1) + x1 := nat(nil).sub(x.abs, natOne) + z.abs = z.abs.add(z.abs.or(x1, y.abs), natOne) + z.neg = true // z cannot be zero if x is negative and y is positive + return z + } + + // x &^ (-y) == x &^ ^(y-1) == x & (y-1) + y1 := nat(nil).sub(y.abs, natOne) + z.abs = z.abs.and(x.abs, y1) + z.neg = false + return z +} + +// Or sets z = x | y and returns z. +func (z *Int) Or(x, y *Int) *Int { + if x.neg == y.neg { + if x.neg { + // (-x) | (-y) == ^(x-1) | ^(y-1) == ^((x-1) & (y-1)) == -(((x-1) & (y-1)) + 1) + x1 := nat(nil).sub(x.abs, natOne) + y1 := nat(nil).sub(y.abs, natOne) + z.abs = z.abs.add(z.abs.and(x1, y1), natOne) + z.neg = true // z cannot be zero if x and y are negative + return z + } + + // x | y == x | y + z.abs = z.abs.or(x.abs, y.abs) + z.neg = false + return z + } + + // x.neg != y.neg + if x.neg { + x, y = y, x // | is symmetric + } + + // x | (-y) == x | ^(y-1) == ^((y-1) &^ x) == -(^((y-1) &^ x) + 1) + y1 := nat(nil).sub(y.abs, natOne) + z.abs = z.abs.add(z.abs.andNot(y1, x.abs), natOne) + z.neg = true // z cannot be zero if one of x or y is negative + return z +} + +// Xor sets z = x ^ y and returns z. +func (z *Int) Xor(x, y *Int) *Int { + if x.neg == y.neg { + if x.neg { + // (-x) ^ (-y) == ^(x-1) ^ ^(y-1) == (x-1) ^ (y-1) + x1 := nat(nil).sub(x.abs, natOne) + y1 := nat(nil).sub(y.abs, natOne) + z.abs = z.abs.xor(x1, y1) + z.neg = false + return z + } + + // x ^ y == x ^ y + z.abs = z.abs.xor(x.abs, y.abs) + z.neg = false + return z + } + + // x.neg != y.neg + if x.neg { + x, y = y, x // ^ is symmetric + } + + // x ^ (-y) == x ^ ^(y-1) == ^(x ^ (y-1)) == -((x ^ (y-1)) + 1) + y1 := nat(nil).sub(y.abs, natOne) + z.abs = z.abs.add(z.abs.xor(x.abs, y1), natOne) + z.neg = true // z cannot be zero if only one of x or y is negative + return z +} + +// Not sets z = ^x and returns z. +func (z *Int) Not(x *Int) *Int { + if x.neg { + // ^(-x) == ^(^(x-1)) == x-1 + z.abs = z.abs.sub(x.abs, natOne) + z.neg = false + return z + } + + // ^x == -x-1 == -(x+1) + z.abs = z.abs.add(x.abs, natOne) + z.neg = true // z cannot be zero if x is positive + return z +} + +// Gob codec version. Permits backward-compatible changes to the encoding. +const intGobVersion byte = 1 + +// GobEncode implements the gob.GobEncoder interface. +func (x *Int) GobEncode() ([]byte, error) { + if x == nil { + return nil, nil + } + buf := make([]byte, 1+len(x.abs)*_S) // extra byte for version and sign bit + i := x.abs.bytes(buf) - 1 // i >= 0 + b := intGobVersion << 1 // make space for sign bit + if x.neg { + b |= 1 + } + buf[i] = b + return buf[i:], nil +} + +// GobDecode implements the gob.GobDecoder interface. +func (z *Int) GobDecode(buf []byte) error { + if len(buf) == 0 { + // Other side sent a nil or default value. + *z = Int{} + return nil + } + b := buf[0] + if b>>1 != intGobVersion { + return fmt.Errorf("Int.GobDecode: encoding version %d not supported", b>>1) + } + z.neg = b&1 != 0 + z.abs = z.abs.setBytes(buf[1:]) + return nil +} + +// MarshalJSON implements the json.Marshaler interface. +func (z *Int) MarshalJSON() ([]byte, error) { + // TODO(gri): get rid of the []byte/string conversions + return []byte(z.String()), nil +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (z *Int) UnmarshalJSON(text []byte) error { + // TODO(gri): get rid of the []byte/string conversions + if _, ok := z.SetString(string(text), 0); !ok { + return fmt.Errorf("math/big: cannot unmarshal %q into a *big.Int", text) + } + return nil +} + +// MarshalText implements the encoding.TextMarshaler interface. +func (z *Int) MarshalText() (text []byte, err error) { + return []byte(z.String()), nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +func (z *Int) UnmarshalText(text []byte) error { + if _, ok := z.SetString(string(text), 0); !ok { + return fmt.Errorf("math/big: cannot unmarshal %q into a *big.Int", text) + } + return nil +} diff --git a/src/cmd/compile/internal/big/int_test.go b/src/cmd/compile/internal/big/int_test.go new file mode 100644 index 0000000000..a972a7249b --- /dev/null +++ b/src/cmd/compile/internal/big/int_test.go @@ -0,0 +1,1387 @@ +// Copyright 2009 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 big + +import ( + "bytes" + "encoding/gob" + "encoding/hex" + "encoding/json" + "encoding/xml" + "fmt" + "math/rand" + "testing" + "testing/quick" +) + +func isNormalized(x *Int) bool { + if len(x.abs) == 0 { + return !x.neg + } + // len(x.abs) > 0 + return x.abs[len(x.abs)-1] != 0 +} + +type funZZ func(z, x, y *Int) *Int +type argZZ struct { + z, x, y *Int +} + +var sumZZ = []argZZ{ + {NewInt(0), NewInt(0), NewInt(0)}, + {NewInt(1), NewInt(1), NewInt(0)}, + {NewInt(1111111110), NewInt(123456789), NewInt(987654321)}, + {NewInt(-1), NewInt(-1), NewInt(0)}, + {NewInt(864197532), NewInt(-123456789), NewInt(987654321)}, + {NewInt(-1111111110), NewInt(-123456789), NewInt(-987654321)}, +} + +var prodZZ = []argZZ{ + {NewInt(0), NewInt(0), NewInt(0)}, + {NewInt(0), NewInt(1), NewInt(0)}, + {NewInt(1), NewInt(1), NewInt(1)}, + {NewInt(-991 * 991), NewInt(991), NewInt(-991)}, + // TODO(gri) add larger products +} + +func TestSignZ(t *testing.T) { + var zero Int + for _, a := range sumZZ { + s := a.z.Sign() + e := a.z.Cmp(&zero) + if s != e { + t.Errorf("got %d; want %d for z = %v", s, e, a.z) + } + } +} + +func TestSetZ(t *testing.T) { + for _, a := range sumZZ { + var z Int + z.Set(a.z) + if !isNormalized(&z) { + t.Errorf("%v is not normalized", z) + } + if (&z).Cmp(a.z) != 0 { + t.Errorf("got z = %v; want %v", z, a.z) + } + } +} + +func TestAbsZ(t *testing.T) { + var zero Int + for _, a := range sumZZ { + var z Int + z.Abs(a.z) + var e Int + e.Set(a.z) + if e.Cmp(&zero) < 0 { + e.Sub(&zero, &e) + } + if z.Cmp(&e) != 0 { + t.Errorf("got z = %v; want %v", z, e) + } + } +} + +func testFunZZ(t *testing.T, msg string, f funZZ, a argZZ) { + var z Int + f(&z, a.x, a.y) + if !isNormalized(&z) { + t.Errorf("%s%v is not normalized", msg, z) + } + if (&z).Cmp(a.z) != 0 { + t.Errorf("%s%+v\n\tgot z = %v; want %v", msg, a, &z, a.z) + } +} + +func TestSumZZ(t *testing.T) { + AddZZ := func(z, x, y *Int) *Int { return z.Add(x, y) } + SubZZ := func(z, x, y *Int) *Int { return z.Sub(x, y) } + for _, a := range sumZZ { + arg := a + testFunZZ(t, "AddZZ", AddZZ, arg) + + arg = argZZ{a.z, a.y, a.x} + testFunZZ(t, "AddZZ symmetric", AddZZ, arg) + + arg = argZZ{a.x, a.z, a.y} + testFunZZ(t, "SubZZ", SubZZ, arg) + + arg = argZZ{a.y, a.z, a.x} + testFunZZ(t, "SubZZ symmetric", SubZZ, arg) + } +} + +func TestProdZZ(t *testing.T) { + MulZZ := func(z, x, y *Int) *Int { return z.Mul(x, y) } + for _, a := range prodZZ { + arg := a + testFunZZ(t, "MulZZ", MulZZ, arg) + + arg = argZZ{a.z, a.y, a.x} + testFunZZ(t, "MulZZ symmetric", MulZZ, arg) + } +} + +// mulBytes returns x*y via grade school multiplication. Both inputs +// and the result are assumed to be in big-endian representation (to +// match the semantics of Int.Bytes and Int.SetBytes). +func mulBytes(x, y []byte) []byte { + z := make([]byte, len(x)+len(y)) + + // multiply + k0 := len(z) - 1 + for j := len(y) - 1; j >= 0; j-- { + d := int(y[j]) + if d != 0 { + k := k0 + carry := 0 + for i := len(x) - 1; i >= 0; i-- { + t := int(z[k]) + int(x[i])*d + carry + z[k], carry = byte(t), t>>8 + k-- + } + z[k] = byte(carry) + } + k0-- + } + + // normalize (remove leading 0's) + i := 0 + for i < len(z) && z[i] == 0 { + i++ + } + + return z[i:] +} + +func checkMul(a, b []byte) bool { + var x, y, z1 Int + x.SetBytes(a) + y.SetBytes(b) + z1.Mul(&x, &y) + + var z2 Int + z2.SetBytes(mulBytes(a, b)) + + return z1.Cmp(&z2) == 0 +} + +func TestMul(t *testing.T) { + if err := quick.Check(checkMul, nil); err != nil { + t.Error(err) + } +} + +var mulRangesZ = []struct { + a, b int64 + prod string +}{ + // entirely positive ranges are covered by mulRangesN + {-1, 1, "0"}, + {-2, -1, "2"}, + {-3, -2, "6"}, + {-3, -1, "-6"}, + {1, 3, "6"}, + {-10, -10, "-10"}, + {0, -1, "1"}, // empty range + {-1, -100, "1"}, // empty range + {-1, 1, "0"}, // range includes 0 + {-1e9, 0, "0"}, // range includes 0 + {-1e9, 1e9, "0"}, // range includes 0 + {-10, -1, "3628800"}, // 10! + {-20, -2, "-2432902008176640000"}, // -20! + {-99, -1, + "-933262154439441526816992388562667004907159682643816214685929" + + "638952175999932299156089414639761565182862536979208272237582" + + "511852109168640000000000000000000000", // -99! + }, +} + +func TestMulRangeZ(t *testing.T) { + var tmp Int + // test entirely positive ranges + for i, r := range mulRangesN { + prod := tmp.MulRange(int64(r.a), int64(r.b)).String() + if prod != r.prod { + t.Errorf("#%da: got %s; want %s", i, prod, r.prod) + } + } + // test other ranges + for i, r := range mulRangesZ { + prod := tmp.MulRange(r.a, r.b).String() + if prod != r.prod { + t.Errorf("#%db: got %s; want %s", i, prod, r.prod) + } + } +} + +func TestBinomial(t *testing.T) { + var z Int + for _, test := range []struct { + n, k int64 + want string + }{ + {0, 0, "1"}, + {0, 1, "0"}, + {1, 0, "1"}, + {1, 1, "1"}, + {1, 10, "0"}, + {4, 0, "1"}, + {4, 1, "4"}, + {4, 2, "6"}, + {4, 3, "4"}, + {4, 4, "1"}, + {10, 1, "10"}, + {10, 9, "10"}, + {10, 5, "252"}, + {11, 5, "462"}, + {11, 6, "462"}, + {100, 10, "17310309456440"}, + {100, 90, "17310309456440"}, + {1000, 10, "263409560461970212832400"}, + {1000, 990, "263409560461970212832400"}, + } { + if got := z.Binomial(test.n, test.k).String(); got != test.want { + t.Errorf("Binomial(%d, %d) = %s; want %s", test.n, test.k, got, test.want) + } + } +} + +func BenchmarkBinomial(b *testing.B) { + var z Int + for i := b.N - 1; i >= 0; i-- { + z.Binomial(1000, 990) + } +} + +// Examples from the Go Language Spec, section "Arithmetic operators" +var divisionSignsTests = []struct { + x, y int64 + q, r int64 // T-division + d, m int64 // Euclidian division +}{ + {5, 3, 1, 2, 1, 2}, + {-5, 3, -1, -2, -2, 1}, + {5, -3, -1, 2, -1, 2}, + {-5, -3, 1, -2, 2, 1}, + {1, 2, 0, 1, 0, 1}, + {8, 4, 2, 0, 2, 0}, +} + +func TestDivisionSigns(t *testing.T) { + for i, test := range divisionSignsTests { + x := NewInt(test.x) + y := NewInt(test.y) + q := NewInt(test.q) + r := NewInt(test.r) + d := NewInt(test.d) + m := NewInt(test.m) + + q1 := new(Int).Quo(x, y) + r1 := new(Int).Rem(x, y) + if !isNormalized(q1) { + t.Errorf("#%d Quo: %v is not normalized", i, *q1) + } + if !isNormalized(r1) { + t.Errorf("#%d Rem: %v is not normalized", i, *r1) + } + if q1.Cmp(q) != 0 || r1.Cmp(r) != 0 { + t.Errorf("#%d QuoRem: got (%s, %s), want (%s, %s)", i, q1, r1, q, r) + } + + q2, r2 := new(Int).QuoRem(x, y, new(Int)) + if !isNormalized(q2) { + t.Errorf("#%d Quo: %v is not normalized", i, *q2) + } + if !isNormalized(r2) { + t.Errorf("#%d Rem: %v is not normalized", i, *r2) + } + if q2.Cmp(q) != 0 || r2.Cmp(r) != 0 { + t.Errorf("#%d QuoRem: got (%s, %s), want (%s, %s)", i, q2, r2, q, r) + } + + d1 := new(Int).Div(x, y) + m1 := new(Int).Mod(x, y) + if !isNormalized(d1) { + t.Errorf("#%d Div: %v is not normalized", i, *d1) + } + if !isNormalized(m1) { + t.Errorf("#%d Mod: %v is not normalized", i, *m1) + } + if d1.Cmp(d) != 0 || m1.Cmp(m) != 0 { + t.Errorf("#%d DivMod: got (%s, %s), want (%s, %s)", i, d1, m1, d, m) + } + + d2, m2 := new(Int).DivMod(x, y, new(Int)) + if !isNormalized(d2) { + t.Errorf("#%d Div: %v is not normalized", i, *d2) + } + if !isNormalized(m2) { + t.Errorf("#%d Mod: %v is not normalized", i, *m2) + } + if d2.Cmp(d) != 0 || m2.Cmp(m) != 0 { + t.Errorf("#%d DivMod: got (%s, %s), want (%s, %s)", i, d2, m2, d, m) + } + } +} + +func norm(x nat) nat { + i := len(x) + for i > 0 && x[i-1] == 0 { + i-- + } + return x[:i] +} + +func TestBits(t *testing.T) { + for _, test := range []nat{ + nil, + {0}, + {1}, + {0, 1, 2, 3, 4}, + {4, 3, 2, 1, 0}, + {4, 3, 2, 1, 0, 0, 0, 0}, + } { + var z Int + z.neg = true + got := z.SetBits(test) + want := norm(test) + if got.abs.cmp(want) != 0 { + t.Errorf("SetBits(%v) = %v; want %v", test, got.abs, want) + } + + if got.neg { + t.Errorf("SetBits(%v): got negative result", test) + } + + bits := nat(z.Bits()) + if bits.cmp(want) != 0 { + t.Errorf("%v.Bits() = %v; want %v", z.abs, bits, want) + } + } +} + +func checkSetBytes(b []byte) bool { + hex1 := hex.EncodeToString(new(Int).SetBytes(b).Bytes()) + hex2 := hex.EncodeToString(b) + + for len(hex1) < len(hex2) { + hex1 = "0" + hex1 + } + + for len(hex1) > len(hex2) { + hex2 = "0" + hex2 + } + + return hex1 == hex2 +} + +func TestSetBytes(t *testing.T) { + if err := quick.Check(checkSetBytes, nil); err != nil { + t.Error(err) + } +} + +func checkBytes(b []byte) bool { + b2 := new(Int).SetBytes(b).Bytes() + return bytes.Equal(b, b2) +} + +func TestBytes(t *testing.T) { + if err := quick.Check(checkBytes, nil); err != nil { + t.Error(err) + } +} + +func checkQuo(x, y []byte) bool { + u := new(Int).SetBytes(x) + v := new(Int).SetBytes(y) + + if len(v.abs) == 0 { + return true + } + + r := new(Int) + q, r := new(Int).QuoRem(u, v, r) + + if r.Cmp(v) >= 0 { + return false + } + + uprime := new(Int).Set(q) + uprime.Mul(uprime, v) + uprime.Add(uprime, r) + + return uprime.Cmp(u) == 0 +} + +var quoTests = []struct { + x, y string + q, r string +}{ + { + "476217953993950760840509444250624797097991362735329973741718102894495832294430498335824897858659711275234906400899559094370964723884706254265559534144986498357", + "9353930466774385905609975137998169297361893554149986716853295022578535724979483772383667534691121982974895531435241089241440253066816724367338287092081996", + "50911", + "1", + }, + { + "11510768301994997771168", + "1328165573307167369775", + "8", + "885443715537658812968", + }, +} + +func TestQuo(t *testing.T) { + if err := quick.Check(checkQuo, nil); err != nil { + t.Error(err) + } + + for i, test := range quoTests { + x, _ := new(Int).SetString(test.x, 10) + y, _ := new(Int).SetString(test.y, 10) + expectedQ, _ := new(Int).SetString(test.q, 10) + expectedR, _ := new(Int).SetString(test.r, 10) + + r := new(Int) + q, r := new(Int).QuoRem(x, y, r) + + if q.Cmp(expectedQ) != 0 || r.Cmp(expectedR) != 0 { + t.Errorf("#%d got (%s, %s) want (%s, %s)", i, q, r, expectedQ, expectedR) + } + } +} + +func TestQuoStepD6(t *testing.T) { + // See Knuth, Volume 2, section 4.3.1, exercise 21. This code exercises + // a code path which only triggers 1 in 10^{-19} cases. + + u := &Int{false, nat{0, 0, 1 + 1<<(_W-1), _M ^ (1 << (_W - 1))}} + v := &Int{false, nat{5, 2 + 1<<(_W-1), 1 << (_W - 1)}} + + r := new(Int) + q, r := new(Int).QuoRem(u, v, r) + const expectedQ64 = "18446744073709551613" + const expectedR64 = "3138550867693340382088035895064302439801311770021610913807" + const expectedQ32 = "4294967293" + const expectedR32 = "39614081266355540837921718287" + if q.String() != expectedQ64 && q.String() != expectedQ32 || + r.String() != expectedR64 && r.String() != expectedR32 { + t.Errorf("got (%s, %s) want (%s, %s) or (%s, %s)", q, r, expectedQ64, expectedR64, expectedQ32, expectedR32) + } +} + +var bitLenTests = []struct { + in string + out int +}{ + {"-1", 1}, + {"0", 0}, + {"1", 1}, + {"2", 2}, + {"4", 3}, + {"0xabc", 12}, + {"0x8000", 16}, + {"0x80000000", 32}, + {"0x800000000000", 48}, + {"0x8000000000000000", 64}, + {"0x80000000000000000000", 80}, + {"-0x4000000000000000000000", 87}, +} + +func TestBitLen(t *testing.T) { + for i, test := range bitLenTests { + x, ok := new(Int).SetString(test.in, 0) + if !ok { + t.Errorf("#%d test input invalid: %s", i, test.in) + continue + } + + if n := x.BitLen(); n != test.out { + t.Errorf("#%d got %d want %d", i, n, test.out) + } + } +} + +var expTests = []struct { + x, y, m string + out string +}{ + // y <= 0 + {"0", "0", "", "1"}, + {"1", "0", "", "1"}, + {"-10", "0", "", "1"}, + {"1234", "-1", "", "1"}, + + // m == 1 + {"0", "0", "1", "0"}, + {"1", "0", "1", "0"}, + {"-10", "0", "1", "0"}, + {"1234", "-1", "1", "0"}, + + // misc + {"5", "-7", "", "1"}, + {"-5", "-7", "", "1"}, + {"5", "0", "", "1"}, + {"-5", "0", "", "1"}, + {"5", "1", "", "5"}, + {"-5", "1", "", "-5"}, + {"-5", "1", "7", "2"}, + {"-2", "3", "2", "0"}, + {"5", "2", "", "25"}, + {"1", "65537", "2", "1"}, + {"0x8000000000000000", "2", "", "0x40000000000000000000000000000000"}, + {"0x8000000000000000", "2", "6719", "4944"}, + {"0x8000000000000000", "3", "6719", "5447"}, + {"0x8000000000000000", "1000", "6719", "1603"}, + {"0x8000000000000000", "1000000", "6719", "3199"}, + {"0x8000000000000000", "-1000000", "6719", "1"}, + { + "2938462938472983472983659726349017249287491026512746239764525612965293865296239471239874193284792387498274256129746192347", + "298472983472983471903246121093472394872319615612417471234712061", + "29834729834729834729347290846729561262544958723956495615629569234729836259263598127342374289365912465901365498236492183464", + "23537740700184054162508175125554701713153216681790245129157191391322321508055833908509185839069455749219131480588829346291", + }, + // test case for issue 8822 + { + "-0x1BCE04427D8032319A89E5C4136456671AC620883F2C4139E57F91307C485AD2D6204F4F87A58262652DB5DBBAC72B0613E51B835E7153BEC6068F5C8D696B74DBD18FEC316AEF73985CF0475663208EB46B4F17DD9DA55367B03323E5491A70997B90C059FB34809E6EE55BCFBD5F2F52233BFE62E6AA9E4E26A1D4C2439883D14F2633D55D8AA66A1ACD5595E778AC3A280517F1157989E70C1A437B849F1877B779CC3CDDEDE2DAA6594A6C66D181A00A5F777EE60596D8773998F6E988DEAE4CCA60E4DDCF9590543C89F74F603259FCAD71660D30294FBBE6490300F78A9D63FA660DC9417B8B9DDA28BEB3977B621B988E23D4D954F322C3540541BC649ABD504C50FADFD9F0987D58A2BF689313A285E773FF02899A6EF887D1D4A0D2", + "0xB08FFB20760FFED58FADA86DFEF71AD72AA0FA763219618FE022C197E54708BB1191C66470250FCE8879487507CEE41381CA4D932F81C2B3F1AB20B539D50DCD", + "0xAC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73", + "21484252197776302499639938883777710321993113097987201050501182909581359357618579566746556372589385361683610524730509041328855066514963385522570894839035884713051640171474186548713546686476761306436434146475140156284389181808675016576845833340494848283681088886584219750554408060556769486628029028720727393293111678826356480455433909233520504112074401376133077150471237549474149190242010469539006449596611576612573955754349042329130631128234637924786466585703488460540228477440853493392086251021228087076124706778899179648655221663765993962724699135217212118535057766739392069738618682722216712319320435674779146070442", + }, +} + +func TestExp(t *testing.T) { + for i, test := range expTests { + x, ok1 := new(Int).SetString(test.x, 0) + y, ok2 := new(Int).SetString(test.y, 0) + out, ok3 := new(Int).SetString(test.out, 0) + + var ok4 bool + var m *Int + + if len(test.m) == 0 { + m, ok4 = nil, true + } else { + m, ok4 = new(Int).SetString(test.m, 0) + } + + if !ok1 || !ok2 || !ok3 || !ok4 { + t.Errorf("#%d: error in input", i) + continue + } + + z1 := new(Int).Exp(x, y, m) + if !isNormalized(z1) { + t.Errorf("#%d: %v is not normalized", i, *z1) + } + if z1.Cmp(out) != 0 { + t.Errorf("#%d: got %s want %s", i, z1, out) + } + + if m == nil { + // The result should be the same as for m == 0; + // specifically, there should be no div-zero panic. + m = &Int{abs: nat{}} // m != nil && len(m.abs) == 0 + z2 := new(Int).Exp(x, y, m) + if z2.Cmp(z1) != 0 { + t.Errorf("#%d: got %s want %s", i, z2, z1) + } + } + } +} + +func checkGcd(aBytes, bBytes []byte) bool { + x := new(Int) + y := new(Int) + a := new(Int).SetBytes(aBytes) + b := new(Int).SetBytes(bBytes) + + d := new(Int).GCD(x, y, a, b) + x.Mul(x, a) + y.Mul(y, b) + x.Add(x, y) + + return x.Cmp(d) == 0 +} + +var gcdTests = []struct { + d, x, y, a, b string +}{ + // a <= 0 || b <= 0 + {"0", "0", "0", "0", "0"}, + {"0", "0", "0", "0", "7"}, + {"0", "0", "0", "11", "0"}, + {"0", "0", "0", "-77", "35"}, + {"0", "0", "0", "64515", "-24310"}, + {"0", "0", "0", "-64515", "-24310"}, + + {"1", "-9", "47", "120", "23"}, + {"7", "1", "-2", "77", "35"}, + {"935", "-3", "8", "64515", "24310"}, + {"935000000000000000", "-3", "8", "64515000000000000000", "24310000000000000000"}, + {"1", "-221", "22059940471369027483332068679400581064239780177629666810348940098015901108344", "98920366548084643601728869055592650835572950932266967461790948584315647051443", "991"}, + + // test early exit (after one Euclidean iteration) in binaryGCD + {"1", "", "", "1", "98920366548084643601728869055592650835572950932266967461790948584315647051443"}, +} + +func testGcd(t *testing.T, d, x, y, a, b *Int) { + var X *Int + if x != nil { + X = new(Int) + } + var Y *Int + if y != nil { + Y = new(Int) + } + + D := new(Int).GCD(X, Y, a, b) + if D.Cmp(d) != 0 { + t.Errorf("GCD(%s, %s): got d = %s, want %s", a, b, D, d) + } + if x != nil && X.Cmp(x) != 0 { + t.Errorf("GCD(%s, %s): got x = %s, want %s", a, b, X, x) + } + if y != nil && Y.Cmp(y) != 0 { + t.Errorf("GCD(%s, %s): got y = %s, want %s", a, b, Y, y) + } + + // binaryGCD requires a > 0 && b > 0 + if a.Sign() <= 0 || b.Sign() <= 0 { + return + } + + D.binaryGCD(a, b) + if D.Cmp(d) != 0 { + t.Errorf("binaryGcd(%s, %s): got d = %s, want %s", a, b, D, d) + } +} + +func TestGcd(t *testing.T) { + for _, test := range gcdTests { + d, _ := new(Int).SetString(test.d, 0) + x, _ := new(Int).SetString(test.x, 0) + y, _ := new(Int).SetString(test.y, 0) + a, _ := new(Int).SetString(test.a, 0) + b, _ := new(Int).SetString(test.b, 0) + + testGcd(t, d, nil, nil, a, b) + testGcd(t, d, x, nil, a, b) + testGcd(t, d, nil, y, a, b) + testGcd(t, d, x, y, a, b) + } + + quick.Check(checkGcd, nil) +} + +var primes = []string{ + "2", + "3", + "5", + "7", + "11", + + "13756265695458089029", + "13496181268022124907", + "10953742525620032441", + "17908251027575790097", + + // http://golang.org/issue/638 + "18699199384836356663", + + "98920366548084643601728869055592650835572950932266967461790948584315647051443", + "94560208308847015747498523884063394671606671904944666360068158221458669711639", + + // http://primes.utm.edu/lists/small/small3.html + "449417999055441493994709297093108513015373787049558499205492347871729927573118262811508386655998299074566974373711472560655026288668094291699357843464363003144674940345912431129144354948751003607115263071543163", + "230975859993204150666423538988557839555560243929065415434980904258310530753006723857139742334640122533598517597674807096648905501653461687601339782814316124971547968912893214002992086353183070342498989426570593", + "5521712099665906221540423207019333379125265462121169655563495403888449493493629943498064604536961775110765377745550377067893607246020694972959780839151452457728855382113555867743022746090187341871655890805971735385789993", + "203956878356401977405765866929034577280193993314348263094772646453283062722701277632936616063144088173312372882677123879538709400158306567338328279154499698366071906766440037074217117805690872792848149112022286332144876183376326512083574821647933992961249917319836219304274280243803104015000563790123", +} + +var composites = []string{ + "0", + "1", + "21284175091214687912771199898307297748211672914763848041968395774954376176754", + "6084766654921918907427900243509372380954290099172559290432744450051395395951", + "84594350493221918389213352992032324280367711247940675652888030554255915464401", + "82793403787388584738507275144194252681", +} + +func TestProbablyPrime(t *testing.T) { + nreps := 20 + if testing.Short() { + nreps = 1 + } + for i, s := range primes { + p, _ := new(Int).SetString(s, 10) + if !p.ProbablyPrime(nreps) { + t.Errorf("#%d prime found to be non-prime (%s)", i, s) + } + } + + for i, s := range composites { + c, _ := new(Int).SetString(s, 10) + if c.ProbablyPrime(nreps) { + t.Errorf("#%d composite found to be prime (%s)", i, s) + } + if testing.Short() { + break + } + } + + // check that ProbablyPrime panics if n <= 0 + c := NewInt(11) // a prime + for _, n := range []int{-1, 0, 1} { + func() { + defer func() { + if n <= 0 && recover() == nil { + t.Fatalf("expected panic from ProbablyPrime(%d)", n) + } + }() + if !c.ProbablyPrime(n) { + t.Fatalf("%v should be a prime", c) + } + }() + } +} + +type intShiftTest struct { + in string + shift uint + out string +} + +var rshTests = []intShiftTest{ + {"0", 0, "0"}, + {"-0", 0, "0"}, + {"0", 1, "0"}, + {"0", 2, "0"}, + {"1", 0, "1"}, + {"1", 1, "0"}, + {"1", 2, "0"}, + {"2", 0, "2"}, + {"2", 1, "1"}, + {"-1", 0, "-1"}, + {"-1", 1, "-1"}, + {"-1", 10, "-1"}, + {"-100", 2, "-25"}, + {"-100", 3, "-13"}, + {"-100", 100, "-1"}, + {"4294967296", 0, "4294967296"}, + {"4294967296", 1, "2147483648"}, + {"4294967296", 2, "1073741824"}, + {"18446744073709551616", 0, "18446744073709551616"}, + {"18446744073709551616", 1, "9223372036854775808"}, + {"18446744073709551616", 2, "4611686018427387904"}, + {"18446744073709551616", 64, "1"}, + {"340282366920938463463374607431768211456", 64, "18446744073709551616"}, + {"340282366920938463463374607431768211456", 128, "1"}, +} + +func TestRsh(t *testing.T) { + for i, test := range rshTests { + in, _ := new(Int).SetString(test.in, 10) + expected, _ := new(Int).SetString(test.out, 10) + out := new(Int).Rsh(in, test.shift) + + if !isNormalized(out) { + t.Errorf("#%d: %v is not normalized", i, *out) + } + if out.Cmp(expected) != 0 { + t.Errorf("#%d: got %s want %s", i, out, expected) + } + } +} + +func TestRshSelf(t *testing.T) { + for i, test := range rshTests { + z, _ := new(Int).SetString(test.in, 10) + expected, _ := new(Int).SetString(test.out, 10) + z.Rsh(z, test.shift) + + if !isNormalized(z) { + t.Errorf("#%d: %v is not normalized", i, *z) + } + if z.Cmp(expected) != 0 { + t.Errorf("#%d: got %s want %s", i, z, expected) + } + } +} + +var lshTests = []intShiftTest{ + {"0", 0, "0"}, + {"0", 1, "0"}, + {"0", 2, "0"}, + {"1", 0, "1"}, + {"1", 1, "2"}, + {"1", 2, "4"}, + {"2", 0, "2"}, + {"2", 1, "4"}, + {"2", 2, "8"}, + {"-87", 1, "-174"}, + {"4294967296", 0, "4294967296"}, + {"4294967296", 1, "8589934592"}, + {"4294967296", 2, "17179869184"}, + {"18446744073709551616", 0, "18446744073709551616"}, + {"9223372036854775808", 1, "18446744073709551616"}, + {"4611686018427387904", 2, "18446744073709551616"}, + {"1", 64, "18446744073709551616"}, + {"18446744073709551616", 64, "340282366920938463463374607431768211456"}, + {"1", 128, "340282366920938463463374607431768211456"}, +} + +func TestLsh(t *testing.T) { + for i, test := range lshTests { + in, _ := new(Int).SetString(test.in, 10) + expected, _ := new(Int).SetString(test.out, 10) + out := new(Int).Lsh(in, test.shift) + + if !isNormalized(out) { + t.Errorf("#%d: %v is not normalized", i, *out) + } + if out.Cmp(expected) != 0 { + t.Errorf("#%d: got %s want %s", i, out, expected) + } + } +} + +func TestLshSelf(t *testing.T) { + for i, test := range lshTests { + z, _ := new(Int).SetString(test.in, 10) + expected, _ := new(Int).SetString(test.out, 10) + z.Lsh(z, test.shift) + + if !isNormalized(z) { + t.Errorf("#%d: %v is not normalized", i, *z) + } + if z.Cmp(expected) != 0 { + t.Errorf("#%d: got %s want %s", i, z, expected) + } + } +} + +func TestLshRsh(t *testing.T) { + for i, test := range rshTests { + in, _ := new(Int).SetString(test.in, 10) + out := new(Int).Lsh(in, test.shift) + out = out.Rsh(out, test.shift) + + if !isNormalized(out) { + t.Errorf("#%d: %v is not normalized", i, *out) + } + if in.Cmp(out) != 0 { + t.Errorf("#%d: got %s want %s", i, out, in) + } + } + for i, test := range lshTests { + in, _ := new(Int).SetString(test.in, 10) + out := new(Int).Lsh(in, test.shift) + out.Rsh(out, test.shift) + + if !isNormalized(out) { + t.Errorf("#%d: %v is not normalized", i, *out) + } + if in.Cmp(out) != 0 { + t.Errorf("#%d: got %s want %s", i, out, in) + } + } +} + +var int64Tests = []int64{ + 0, + 1, + -1, + 4294967295, + -4294967295, + 4294967296, + -4294967296, + 9223372036854775807, + -9223372036854775807, + -9223372036854775808, +} + +func TestInt64(t *testing.T) { + for i, testVal := range int64Tests { + in := NewInt(testVal) + out := in.Int64() + + if out != testVal { + t.Errorf("#%d got %d want %d", i, out, testVal) + } + } +} + +var uint64Tests = []uint64{ + 0, + 1, + 4294967295, + 4294967296, + 8589934591, + 8589934592, + 9223372036854775807, + 9223372036854775808, + 18446744073709551615, // 1<<64 - 1 +} + +func TestUint64(t *testing.T) { + in := new(Int) + for i, testVal := range uint64Tests { + in.SetUint64(testVal) + out := in.Uint64() + + if out != testVal { + t.Errorf("#%d got %d want %d", i, out, testVal) + } + + str := fmt.Sprint(testVal) + strOut := in.String() + if strOut != str { + t.Errorf("#%d.String got %s want %s", i, strOut, str) + } + } +} + +var bitwiseTests = []struct { + x, y string + and, or, xor, andNot string +}{ + {"0x00", "0x00", "0x00", "0x00", "0x00", "0x00"}, + {"0x00", "0x01", "0x00", "0x01", "0x01", "0x00"}, + {"0x01", "0x00", "0x00", "0x01", "0x01", "0x01"}, + {"-0x01", "0x00", "0x00", "-0x01", "-0x01", "-0x01"}, + {"-0xaf", "-0x50", "-0xf0", "-0x0f", "0xe1", "0x41"}, + {"0x00", "-0x01", "0x00", "-0x01", "-0x01", "0x00"}, + {"0x01", "0x01", "0x01", "0x01", "0x00", "0x00"}, + {"-0x01", "-0x01", "-0x01", "-0x01", "0x00", "0x00"}, + {"0x07", "0x08", "0x00", "0x0f", "0x0f", "0x07"}, + {"0x05", "0x0f", "0x05", "0x0f", "0x0a", "0x00"}, + {"0xff", "-0x0a", "0xf6", "-0x01", "-0xf7", "0x09"}, + {"0x013ff6", "0x9a4e", "0x1a46", "0x01bffe", "0x01a5b8", "0x0125b0"}, + {"-0x013ff6", "0x9a4e", "0x800a", "-0x0125b2", "-0x01a5bc", "-0x01c000"}, + {"-0x013ff6", "-0x9a4e", "-0x01bffe", "-0x1a46", "0x01a5b8", "0x8008"}, + { + "0x1000009dc6e3d9822cba04129bcbe3401", + "0xb9bd7d543685789d57cb918e833af352559021483cdb05cc21fd", + "0x1000001186210100001000009048c2001", + "0xb9bd7d543685789d57cb918e8bfeff7fddb2ebe87dfbbdfe35fd", + "0xb9bd7d543685789d57ca918e8ae69d6fcdb2eae87df2b97215fc", + "0x8c40c2d8822caa04120b8321400", + }, + { + "0x1000009dc6e3d9822cba04129bcbe3401", + "-0xb9bd7d543685789d57cb918e833af352559021483cdb05cc21fd", + "0x8c40c2d8822caa04120b8321401", + "-0xb9bd7d543685789d57ca918e82229142459020483cd2014001fd", + "-0xb9bd7d543685789d57ca918e8ae69d6fcdb2eae87df2b97215fe", + "0x1000001186210100001000009048c2000", + }, + { + "-0x1000009dc6e3d9822cba04129bcbe3401", + "-0xb9bd7d543685789d57cb918e833af352559021483cdb05cc21fd", + "-0xb9bd7d543685789d57cb918e8bfeff7fddb2ebe87dfbbdfe35fd", + "-0x1000001186210100001000009048c2001", + "0xb9bd7d543685789d57ca918e8ae69d6fcdb2eae87df2b97215fc", + "0xb9bd7d543685789d57ca918e82229142459020483cd2014001fc", + }, +} + +type bitFun func(z, x, y *Int) *Int + +func testBitFun(t *testing.T, msg string, f bitFun, x, y *Int, exp string) { + expected := new(Int) + expected.SetString(exp, 0) + + out := f(new(Int), x, y) + if out.Cmp(expected) != 0 { + t.Errorf("%s: got %s want %s", msg, out, expected) + } +} + +func testBitFunSelf(t *testing.T, msg string, f bitFun, x, y *Int, exp string) { + self := new(Int) + self.Set(x) + expected := new(Int) + expected.SetString(exp, 0) + + self = f(self, self, y) + if self.Cmp(expected) != 0 { + t.Errorf("%s: got %s want %s", msg, self, expected) + } +} + +func altBit(x *Int, i int) uint { + z := new(Int).Rsh(x, uint(i)) + z = z.And(z, NewInt(1)) + if z.Cmp(new(Int)) != 0 { + return 1 + } + return 0 +} + +func altSetBit(z *Int, x *Int, i int, b uint) *Int { + one := NewInt(1) + m := one.Lsh(one, uint(i)) + switch b { + case 1: + return z.Or(x, m) + case 0: + return z.AndNot(x, m) + } + panic("set bit is not 0 or 1") +} + +func testBitset(t *testing.T, x *Int) { + n := x.BitLen() + z := new(Int).Set(x) + z1 := new(Int).Set(x) + for i := 0; i < n+10; i++ { + old := z.Bit(i) + old1 := altBit(z1, i) + if old != old1 { + t.Errorf("bitset: inconsistent value for Bit(%s, %d), got %v want %v", z1, i, old, old1) + } + z := new(Int).SetBit(z, i, 1) + z1 := altSetBit(new(Int), z1, i, 1) + if z.Bit(i) == 0 { + t.Errorf("bitset: bit %d of %s got 0 want 1", i, x) + } + if z.Cmp(z1) != 0 { + t.Errorf("bitset: inconsistent value after SetBit 1, got %s want %s", z, z1) + } + z.SetBit(z, i, 0) + altSetBit(z1, z1, i, 0) + if z.Bit(i) != 0 { + t.Errorf("bitset: bit %d of %s got 1 want 0", i, x) + } + if z.Cmp(z1) != 0 { + t.Errorf("bitset: inconsistent value after SetBit 0, got %s want %s", z, z1) + } + altSetBit(z1, z1, i, old) + z.SetBit(z, i, old) + if z.Cmp(z1) != 0 { + t.Errorf("bitset: inconsistent value after SetBit old, got %s want %s", z, z1) + } + } + if z.Cmp(x) != 0 { + t.Errorf("bitset: got %s want %s", z, x) + } +} + +var bitsetTests = []struct { + x string + i int + b uint +}{ + {"0", 0, 0}, + {"0", 200, 0}, + {"1", 0, 1}, + {"1", 1, 0}, + {"-1", 0, 1}, + {"-1", 200, 1}, + {"0x2000000000000000000000000000", 108, 0}, + {"0x2000000000000000000000000000", 109, 1}, + {"0x2000000000000000000000000000", 110, 0}, + {"-0x2000000000000000000000000001", 108, 1}, + {"-0x2000000000000000000000000001", 109, 0}, + {"-0x2000000000000000000000000001", 110, 1}, +} + +func TestBitSet(t *testing.T) { + for _, test := range bitwiseTests { + x := new(Int) + x.SetString(test.x, 0) + testBitset(t, x) + x = new(Int) + x.SetString(test.y, 0) + testBitset(t, x) + } + for i, test := range bitsetTests { + x := new(Int) + x.SetString(test.x, 0) + b := x.Bit(test.i) + if b != test.b { + t.Errorf("#%d got %v want %v", i, b, test.b) + } + } + z := NewInt(1) + z.SetBit(NewInt(0), 2, 1) + if z.Cmp(NewInt(4)) != 0 { + t.Errorf("destination leaked into result; got %s want 4", z) + } +} + +func BenchmarkBitset(b *testing.B) { + z := new(Int) + z.SetBit(z, 512, 1) + b.ResetTimer() + b.StartTimer() + for i := b.N - 1; i >= 0; i-- { + z.SetBit(z, i&512, 1) + } +} + +func BenchmarkBitsetNeg(b *testing.B) { + z := NewInt(-1) + z.SetBit(z, 512, 0) + b.ResetTimer() + b.StartTimer() + for i := b.N - 1; i >= 0; i-- { + z.SetBit(z, i&512, 0) + } +} + +func BenchmarkBitsetOrig(b *testing.B) { + z := new(Int) + altSetBit(z, z, 512, 1) + b.ResetTimer() + b.StartTimer() + for i := b.N - 1; i >= 0; i-- { + altSetBit(z, z, i&512, 1) + } +} + +func BenchmarkBitsetNegOrig(b *testing.B) { + z := NewInt(-1) + altSetBit(z, z, 512, 0) + b.ResetTimer() + b.StartTimer() + for i := b.N - 1; i >= 0; i-- { + altSetBit(z, z, i&512, 0) + } +} + +func TestBitwise(t *testing.T) { + x := new(Int) + y := new(Int) + for _, test := range bitwiseTests { + x.SetString(test.x, 0) + y.SetString(test.y, 0) + + testBitFun(t, "and", (*Int).And, x, y, test.and) + testBitFunSelf(t, "and", (*Int).And, x, y, test.and) + testBitFun(t, "andNot", (*Int).AndNot, x, y, test.andNot) + testBitFunSelf(t, "andNot", (*Int).AndNot, x, y, test.andNot) + testBitFun(t, "or", (*Int).Or, x, y, test.or) + testBitFunSelf(t, "or", (*Int).Or, x, y, test.or) + testBitFun(t, "xor", (*Int).Xor, x, y, test.xor) + testBitFunSelf(t, "xor", (*Int).Xor, x, y, test.xor) + } +} + +var notTests = []struct { + in string + out string +}{ + {"0", "-1"}, + {"1", "-2"}, + {"7", "-8"}, + {"0", "-1"}, + {"-81910", "81909"}, + { + "298472983472983471903246121093472394872319615612417471234712061", + "-298472983472983471903246121093472394872319615612417471234712062", + }, +} + +func TestNot(t *testing.T) { + in := new(Int) + out := new(Int) + expected := new(Int) + for i, test := range notTests { + in.SetString(test.in, 10) + expected.SetString(test.out, 10) + out = out.Not(in) + if out.Cmp(expected) != 0 { + t.Errorf("#%d: got %s want %s", i, out, expected) + } + out = out.Not(out) + if out.Cmp(in) != 0 { + t.Errorf("#%d: got %s want %s", i, out, in) + } + } +} + +var modInverseTests = []struct { + element string + modulus string +}{ + {"1234567", "458948883992"}, + {"239487239847", "2410312426921032588552076022197566074856950548502459942654116941958108831682612228890093858261341614673227141477904012196503648957050582631942730706805009223062734745341073406696246014589361659774041027169249453200378729434170325843778659198143763193776859869524088940195577346119843545301547043747207749969763750084308926339295559968882457872412993810129130294592999947926365264059284647209730384947211681434464714438488520940127459844288859336526896320919633919"}, +} + +func TestModInverse(t *testing.T) { + var element, modulus, gcd, inverse Int + one := NewInt(1) + for i, test := range modInverseTests { + (&element).SetString(test.element, 10) + (&modulus).SetString(test.modulus, 10) + (&inverse).ModInverse(&element, &modulus) + (&inverse).Mul(&inverse, &element) + (&inverse).Mod(&inverse, &modulus) + if (&inverse).Cmp(one) != 0 { + t.Errorf("#%d: failed (e·e^(-1)=%s)", i, &inverse) + } + } + // exhaustive test for small values + for n := 2; n < 100; n++ { + (&modulus).SetInt64(int64(n)) + for x := 1; x < n; x++ { + (&element).SetInt64(int64(x)) + (&gcd).GCD(nil, nil, &element, &modulus) + if (&gcd).Cmp(one) != 0 { + continue + } + (&inverse).ModInverse(&element, &modulus) + (&inverse).Mul(&inverse, &element) + (&inverse).Mod(&inverse, &modulus) + if (&inverse).Cmp(one) != 0 { + t.Errorf("ModInverse(%d,%d)*%d%%%d=%d, not 1", &element, &modulus, &element, &modulus, &inverse) + } + } + } +} + +var encodingTests = []string{ + "-539345864568634858364538753846587364875430589374589", + "-678645873", + "-100", + "-2", + "-1", + "0", + "1", + "2", + "10", + "42", + "1234567890", + "298472983472983471903246121093472394872319615612417471234712061", +} + +func TestIntGobEncoding(t *testing.T) { + var medium bytes.Buffer + enc := gob.NewEncoder(&medium) + dec := gob.NewDecoder(&medium) + for _, test := range encodingTests { + medium.Reset() // empty buffer for each test case (in case of failures) + var tx Int + tx.SetString(test, 10) + if err := enc.Encode(&tx); err != nil { + t.Errorf("encoding of %s failed: %s", &tx, err) + } + var rx Int + if err := dec.Decode(&rx); err != nil { + t.Errorf("decoding of %s failed: %s", &tx, err) + } + if rx.Cmp(&tx) != 0 { + t.Errorf("transmission of %s failed: got %s want %s", &tx, &rx, &tx) + } + } +} + +// Sending a nil Int pointer (inside a slice) on a round trip through gob should yield a zero. +// TODO: top-level nils. +func TestGobEncodingNilIntInSlice(t *testing.T) { + buf := new(bytes.Buffer) + enc := gob.NewEncoder(buf) + dec := gob.NewDecoder(buf) + + var in = make([]*Int, 1) + err := enc.Encode(&in) + if err != nil { + t.Errorf("gob encode failed: %q", err) + } + var out []*Int + err = dec.Decode(&out) + if err != nil { + t.Fatalf("gob decode failed: %q", err) + } + if len(out) != 1 { + t.Fatalf("wrong len; want 1 got %d", len(out)) + } + var zero Int + if out[0].Cmp(&zero) != 0 { + t.Errorf("transmission of (*Int)(nill) failed: got %s want 0", out) + } +} + +func TestIntJSONEncoding(t *testing.T) { + for _, test := range encodingTests { + var tx Int + tx.SetString(test, 10) + b, err := json.Marshal(&tx) + if err != nil { + t.Errorf("marshaling of %s failed: %s", &tx, err) + } + var rx Int + if err := json.Unmarshal(b, &rx); err != nil { + t.Errorf("unmarshaling of %s failed: %s", &tx, err) + } + if rx.Cmp(&tx) != 0 { + t.Errorf("JSON encoding of %s failed: got %s want %s", &tx, &rx, &tx) + } + } +} + +var intVals = []string{ + "-141592653589793238462643383279502884197169399375105820974944592307816406286", + "-1415926535897932384626433832795028841971", + "-141592653589793", + "-1", + "0", + "1", + "141592653589793", + "1415926535897932384626433832795028841971", + "141592653589793238462643383279502884197169399375105820974944592307816406286", +} + +func TestIntJSONEncodingTextMarshaller(t *testing.T) { + for _, num := range intVals { + var tx Int + tx.SetString(num, 0) + b, err := json.Marshal(&tx) + if err != nil { + t.Errorf("marshaling of %s failed: %s", &tx, err) + continue + } + var rx Int + if err := json.Unmarshal(b, &rx); err != nil { + t.Errorf("unmarshaling of %s failed: %s", &tx, err) + continue + } + if rx.Cmp(&tx) != 0 { + t.Errorf("JSON encoding of %s failed: got %s want %s", &tx, &rx, &tx) + } + } +} + +func TestIntXMLEncodingTextMarshaller(t *testing.T) { + for _, num := range intVals { + var tx Int + tx.SetString(num, 0) + b, err := xml.Marshal(&tx) + if err != nil { + t.Errorf("marshaling of %s failed: %s", &tx, err) + continue + } + var rx Int + if err := xml.Unmarshal(b, &rx); err != nil { + t.Errorf("unmarshaling of %s failed: %s", &tx, err) + continue + } + if rx.Cmp(&tx) != 0 { + t.Errorf("XML encoding of %s failed: got %s want %s", &tx, &rx, &tx) + } + } +} + +func TestIssue2607(t *testing.T) { + // This code sequence used to hang. + n := NewInt(10) + n.Rand(rand.New(rand.NewSource(9)), n) +} diff --git a/src/cmd/compile/internal/big/intconv.go b/src/cmd/compile/internal/big/intconv.go new file mode 100644 index 0000000000..9c68a22bed --- /dev/null +++ b/src/cmd/compile/internal/big/intconv.go @@ -0,0 +1,228 @@ +// 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. + +// This file implements int-to-string conversion functions. + +package big + +import ( + "errors" + "fmt" + "io" +) + +func (x *Int) String() string { + switch { + case x == nil: + return "" + case x.neg: + return "-" + x.abs.decimalString() + } + return x.abs.decimalString() +} + +func charset(ch rune) string { + switch ch { + case 'b': + return lowercaseDigits[0:2] + case 'o': + return lowercaseDigits[0:8] + case 'd', 's', 'v': + return lowercaseDigits[0:10] + case 'x': + return lowercaseDigits[0:16] + case 'X': + return uppercaseDigits[0:16] + } + return "" // unknown format +} + +// write count copies of text to s +func writeMultiple(s fmt.State, text string, count int) { + if len(text) > 0 { + b := []byte(text) + for ; count > 0; count-- { + s.Write(b) + } + } +} + +// Format is a support routine for fmt.Formatter. It accepts +// the formats 'b' (binary), 'o' (octal), 'd' (decimal), 'x' +// (lowercase hexadecimal), and 'X' (uppercase hexadecimal). +// Also supported are the full suite of package fmt's format +// verbs for integral types, including '+', '-', and ' ' +// for sign control, '#' for leading zero in octal and for +// hexadecimal, a leading "0x" or "0X" for "%#x" and "%#X" +// respectively, specification of minimum digits precision, +// output field width, space or zero padding, and left or +// right justification. +// +func (x *Int) Format(s fmt.State, ch rune) { + cs := charset(ch) + + // special cases + switch { + case cs == "": + // unknown format + fmt.Fprintf(s, "%%!%c(big.Int=%s)", ch, x.String()) + return + case x == nil: + fmt.Fprint(s, "") + return + } + + // determine sign character + sign := "" + switch { + case x.neg: + sign = "-" + case s.Flag('+'): // supersedes ' ' when both specified + sign = "+" + case s.Flag(' '): + sign = " " + } + + // determine prefix characters for indicating output base + prefix := "" + if s.Flag('#') { + switch ch { + case 'o': // octal + prefix = "0" + case 'x': // hexadecimal + prefix = "0x" + case 'X': + prefix = "0X" + } + } + + // determine digits with base set by len(cs) and digit characters from cs + digits := x.abs.string(cs) + + // number of characters for the three classes of number padding + var left int // space characters to left of digits for right justification ("%8d") + var zeroes int // zero characters (actually cs[0]) as left-most digits ("%.8d") + var right int // space characters to right of digits for left justification ("%-8d") + + // determine number padding from precision: the least number of digits to output + precision, precisionSet := s.Precision() + if precisionSet { + switch { + case len(digits) < precision: + zeroes = precision - len(digits) // count of zero padding + case digits == "0" && precision == 0: + return // print nothing if zero value (x == 0) and zero precision ("." or ".0") + } + } + + // determine field pad from width: the least number of characters to output + length := len(sign) + len(prefix) + zeroes + len(digits) + if width, widthSet := s.Width(); widthSet && length < width { // pad as specified + switch d := width - length; { + case s.Flag('-'): + // pad on the right with spaces; supersedes '0' when both specified + right = d + case s.Flag('0') && !precisionSet: + // pad with zeroes unless precision also specified + zeroes = d + default: + // pad on the left with spaces + left = d + } + } + + // print number as [left pad][sign][prefix][zero pad][digits][right pad] + writeMultiple(s, " ", left) + writeMultiple(s, sign, 1) + writeMultiple(s, prefix, 1) + writeMultiple(s, "0", zeroes) + writeMultiple(s, digits, 1) + writeMultiple(s, " ", right) +} + +// scan sets z to the integer value corresponding to the longest possible prefix +// read from r representing a signed integer number in a given conversion base. +// It returns z, the actual conversion base used, and an error, if any. In the +// error case, the value of z is undefined but the returned value is nil. The +// syntax follows the syntax of integer literals in Go. +// +// The base argument must be 0 or a value from 2 through MaxBase. If the base +// is 0, the string prefix determines the actual conversion base. A prefix of +// ``0x'' or ``0X'' selects base 16; the ``0'' prefix selects base 8, and a +// ``0b'' or ``0B'' prefix selects base 2. Otherwise the selected base is 10. +// +func (z *Int) scan(r io.ByteScanner, base int) (*Int, int, error) { + // determine sign + neg, err := scanSign(r) + if err != nil { + return nil, 0, err + } + + // determine mantissa + z.abs, base, _, err = z.abs.scan(r, base, false) + if err != nil { + return nil, base, err + } + z.neg = len(z.abs) > 0 && neg // 0 has no sign + + return z, base, nil +} + +func scanSign(r io.ByteScanner) (neg bool, err error) { + var ch byte + if ch, err = r.ReadByte(); err != nil { + return false, err + } + switch ch { + case '-': + neg = true + case '+': + // nothing to do + default: + r.UnreadByte() + } + return +} + +// byteReader is a local wrapper around fmt.ScanState; +// it implements the ByteReader interface. +type byteReader struct { + fmt.ScanState +} + +func (r byteReader) ReadByte() (byte, error) { + ch, size, err := r.ReadRune() + if size != 1 && err == nil { + err = fmt.Errorf("invalid rune %#U", ch) + } + return byte(ch), err +} + +func (r byteReader) UnreadByte() error { + return r.UnreadRune() +} + +// Scan is a support routine for fmt.Scanner; it sets z to the value of +// the scanned number. It accepts the formats 'b' (binary), 'o' (octal), +// 'd' (decimal), 'x' (lowercase hexadecimal), and 'X' (uppercase hexadecimal). +func (z *Int) Scan(s fmt.ScanState, ch rune) error { + s.SkipSpace() // skip leading space characters + base := 0 + switch ch { + case 'b': + base = 2 + case 'o': + base = 8 + case 'd': + base = 10 + case 'x', 'X': + base = 16 + case 's', 'v': + // let scan determine the base + default: + return errors.New("Int.Scan: invalid verb") + } + _, _, err := z.scan(byteReader{s}, base) + return err +} diff --git a/src/cmd/compile/internal/big/intconv_test.go b/src/cmd/compile/internal/big/intconv_test.go new file mode 100644 index 0000000000..2deb84b48f --- /dev/null +++ b/src/cmd/compile/internal/big/intconv_test.go @@ -0,0 +1,342 @@ +// 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 big + +import ( + "bytes" + "fmt" + "testing" +) + +var stringTests = []struct { + in string + out string + base int + val int64 + ok bool +}{ + {in: "", ok: false}, + {in: "a", ok: false}, + {in: "z", ok: false}, + {in: "+", ok: false}, + {in: "-", ok: false}, + {in: "0b", ok: false}, + {in: "0x", ok: false}, + {in: "2", base: 2, ok: false}, + {in: "0b2", base: 0, ok: false}, + {in: "08", ok: false}, + {in: "8", base: 8, ok: false}, + {in: "0xg", base: 0, ok: false}, + {in: "g", base: 16, ok: false}, + {"0", "0", 0, 0, true}, + {"0", "0", 10, 0, true}, + {"0", "0", 16, 0, true}, + {"+0", "0", 0, 0, true}, + {"-0", "0", 0, 0, true}, + {"10", "10", 0, 10, true}, + {"10", "10", 10, 10, true}, + {"10", "10", 16, 16, true}, + {"-10", "-10", 16, -16, true}, + {"+10", "10", 16, 16, true}, + {"0x10", "16", 0, 16, true}, + {in: "0x10", base: 16, ok: false}, + {"-0x10", "-16", 0, -16, true}, + {"+0x10", "16", 0, 16, true}, + {"00", "0", 0, 0, true}, + {"0", "0", 8, 0, true}, + {"07", "7", 0, 7, true}, + {"7", "7", 8, 7, true}, + {"023", "19", 0, 19, true}, + {"23", "23", 8, 19, true}, + {"cafebabe", "cafebabe", 16, 0xcafebabe, true}, + {"0b0", "0", 0, 0, true}, + {"-111", "-111", 2, -7, true}, + {"-0b111", "-7", 0, -7, true}, + {"0b1001010111", "599", 0, 0x257, true}, + {"1001010111", "1001010111", 2, 0x257, true}, +} + +func format(base int) string { + switch base { + case 2: + return "%b" + case 8: + return "%o" + case 16: + return "%x" + } + return "%d" +} + +func TestGetString(t *testing.T) { + z := new(Int) + for i, test := range stringTests { + if !test.ok { + continue + } + z.SetInt64(test.val) + + if test.base == 10 { + s := z.String() + if s != test.out { + t.Errorf("#%da got %s; want %s", i, s, test.out) + } + } + + s := fmt.Sprintf(format(test.base), z) + if s != test.out { + t.Errorf("#%db got %s; want %s", i, s, test.out) + } + } +} + +func TestSetString(t *testing.T) { + tmp := new(Int) + for i, test := range stringTests { + // initialize to a non-zero value so that issues with parsing + // 0 are detected + tmp.SetInt64(1234567890) + n1, ok1 := new(Int).SetString(test.in, test.base) + n2, ok2 := tmp.SetString(test.in, test.base) + expected := NewInt(test.val) + if ok1 != test.ok || ok2 != test.ok { + t.Errorf("#%d (input '%s') ok incorrect (should be %t)", i, test.in, test.ok) + continue + } + if !ok1 { + if n1 != nil { + t.Errorf("#%d (input '%s') n1 != nil", i, test.in) + } + continue + } + if !ok2 { + if n2 != nil { + t.Errorf("#%d (input '%s') n2 != nil", i, test.in) + } + continue + } + + if ok1 && !isNormalized(n1) { + t.Errorf("#%d (input '%s'): %v is not normalized", i, test.in, *n1) + } + if ok2 && !isNormalized(n2) { + t.Errorf("#%d (input '%s'): %v is not normalized", i, test.in, *n2) + } + + if n1.Cmp(expected) != 0 { + t.Errorf("#%d (input '%s') got: %s want: %d", i, test.in, n1, test.val) + } + if n2.Cmp(expected) != 0 { + t.Errorf("#%d (input '%s') got: %s want: %d", i, test.in, n2, test.val) + } + } +} + +var formatTests = []struct { + input string + format string + output string +}{ + {"", "%x", ""}, + {"", "%#x", ""}, + {"", "%#y", "%!y(big.Int=)"}, + + {"10", "%b", "1010"}, + {"10", "%o", "12"}, + {"10", "%d", "10"}, + {"10", "%v", "10"}, + {"10", "%x", "a"}, + {"10", "%X", "A"}, + {"-10", "%X", "-A"}, + {"10", "%y", "%!y(big.Int=10)"}, + {"-10", "%y", "%!y(big.Int=-10)"}, + + {"10", "%#b", "1010"}, + {"10", "%#o", "012"}, + {"10", "%#d", "10"}, + {"10", "%#v", "10"}, + {"10", "%#x", "0xa"}, + {"10", "%#X", "0XA"}, + {"-10", "%#X", "-0XA"}, + {"10", "%#y", "%!y(big.Int=10)"}, + {"-10", "%#y", "%!y(big.Int=-10)"}, + + {"1234", "%d", "1234"}, + {"1234", "%3d", "1234"}, + {"1234", "%4d", "1234"}, + {"-1234", "%d", "-1234"}, + {"1234", "% 5d", " 1234"}, + {"1234", "%+5d", "+1234"}, + {"1234", "%-5d", "1234 "}, + {"1234", "%x", "4d2"}, + {"1234", "%X", "4D2"}, + {"-1234", "%3x", "-4d2"}, + {"-1234", "%4x", "-4d2"}, + {"-1234", "%5x", " -4d2"}, + {"-1234", "%-5x", "-4d2 "}, + {"1234", "%03d", "1234"}, + {"1234", "%04d", "1234"}, + {"1234", "%05d", "01234"}, + {"1234", "%06d", "001234"}, + {"-1234", "%06d", "-01234"}, + {"1234", "%+06d", "+01234"}, + {"1234", "% 06d", " 01234"}, + {"1234", "%-6d", "1234 "}, + {"1234", "%-06d", "1234 "}, + {"-1234", "%-06d", "-1234 "}, + + {"1234", "%.3d", "1234"}, + {"1234", "%.4d", "1234"}, + {"1234", "%.5d", "01234"}, + {"1234", "%.6d", "001234"}, + {"-1234", "%.3d", "-1234"}, + {"-1234", "%.4d", "-1234"}, + {"-1234", "%.5d", "-01234"}, + {"-1234", "%.6d", "-001234"}, + + {"1234", "%8.3d", " 1234"}, + {"1234", "%8.4d", " 1234"}, + {"1234", "%8.5d", " 01234"}, + {"1234", "%8.6d", " 001234"}, + {"-1234", "%8.3d", " -1234"}, + {"-1234", "%8.4d", " -1234"}, + {"-1234", "%8.5d", " -01234"}, + {"-1234", "%8.6d", " -001234"}, + + {"1234", "%+8.3d", " +1234"}, + {"1234", "%+8.4d", " +1234"}, + {"1234", "%+8.5d", " +01234"}, + {"1234", "%+8.6d", " +001234"}, + {"-1234", "%+8.3d", " -1234"}, + {"-1234", "%+8.4d", " -1234"}, + {"-1234", "%+8.5d", " -01234"}, + {"-1234", "%+8.6d", " -001234"}, + + {"1234", "% 8.3d", " 1234"}, + {"1234", "% 8.4d", " 1234"}, + {"1234", "% 8.5d", " 01234"}, + {"1234", "% 8.6d", " 001234"}, + {"-1234", "% 8.3d", " -1234"}, + {"-1234", "% 8.4d", " -1234"}, + {"-1234", "% 8.5d", " -01234"}, + {"-1234", "% 8.6d", " -001234"}, + + {"1234", "%.3x", "4d2"}, + {"1234", "%.4x", "04d2"}, + {"1234", "%.5x", "004d2"}, + {"1234", "%.6x", "0004d2"}, + {"-1234", "%.3x", "-4d2"}, + {"-1234", "%.4x", "-04d2"}, + {"-1234", "%.5x", "-004d2"}, + {"-1234", "%.6x", "-0004d2"}, + + {"1234", "%8.3x", " 4d2"}, + {"1234", "%8.4x", " 04d2"}, + {"1234", "%8.5x", " 004d2"}, + {"1234", "%8.6x", " 0004d2"}, + {"-1234", "%8.3x", " -4d2"}, + {"-1234", "%8.4x", " -04d2"}, + {"-1234", "%8.5x", " -004d2"}, + {"-1234", "%8.6x", " -0004d2"}, + + {"1234", "%+8.3x", " +4d2"}, + {"1234", "%+8.4x", " +04d2"}, + {"1234", "%+8.5x", " +004d2"}, + {"1234", "%+8.6x", " +0004d2"}, + {"-1234", "%+8.3x", " -4d2"}, + {"-1234", "%+8.4x", " -04d2"}, + {"-1234", "%+8.5x", " -004d2"}, + {"-1234", "%+8.6x", " -0004d2"}, + + {"1234", "% 8.3x", " 4d2"}, + {"1234", "% 8.4x", " 04d2"}, + {"1234", "% 8.5x", " 004d2"}, + {"1234", "% 8.6x", " 0004d2"}, + {"1234", "% 8.7x", " 00004d2"}, + {"1234", "% 8.8x", " 000004d2"}, + {"-1234", "% 8.3x", " -4d2"}, + {"-1234", "% 8.4x", " -04d2"}, + {"-1234", "% 8.5x", " -004d2"}, + {"-1234", "% 8.6x", " -0004d2"}, + {"-1234", "% 8.7x", "-00004d2"}, + {"-1234", "% 8.8x", "-000004d2"}, + + {"1234", "%-8.3d", "1234 "}, + {"1234", "%-8.4d", "1234 "}, + {"1234", "%-8.5d", "01234 "}, + {"1234", "%-8.6d", "001234 "}, + {"1234", "%-8.7d", "0001234 "}, + {"1234", "%-8.8d", "00001234"}, + {"-1234", "%-8.3d", "-1234 "}, + {"-1234", "%-8.4d", "-1234 "}, + {"-1234", "%-8.5d", "-01234 "}, + {"-1234", "%-8.6d", "-001234 "}, + {"-1234", "%-8.7d", "-0001234"}, + {"-1234", "%-8.8d", "-00001234"}, + + {"16777215", "%b", "111111111111111111111111"}, // 2**24 - 1 + + {"0", "%.d", ""}, + {"0", "%.0d", ""}, + {"0", "%3.d", ""}, +} + +func TestFormat(t *testing.T) { + for i, test := range formatTests { + var x *Int + if test.input != "" { + var ok bool + x, ok = new(Int).SetString(test.input, 0) + if !ok { + t.Errorf("#%d failed reading input %s", i, test.input) + } + } + output := fmt.Sprintf(test.format, x) + if output != test.output { + t.Errorf("#%d got %q; want %q, {%q, %q, %q}", i, output, test.output, test.input, test.format, test.output) + } + } +} + +var scanTests = []struct { + input string + format string + output string + remaining int +}{ + {"1010", "%b", "10", 0}, + {"0b1010", "%v", "10", 0}, + {"12", "%o", "10", 0}, + {"012", "%v", "10", 0}, + {"10", "%d", "10", 0}, + {"10", "%v", "10", 0}, + {"a", "%x", "10", 0}, + {"0xa", "%v", "10", 0}, + {"A", "%X", "10", 0}, + {"-A", "%X", "-10", 0}, + {"+0b1011001", "%v", "89", 0}, + {"0xA", "%v", "10", 0}, + {"0 ", "%v", "0", 1}, + {"2+3", "%v", "2", 2}, + {"0XABC 12", "%v", "2748", 3}, +} + +func TestScan(t *testing.T) { + var buf bytes.Buffer + for i, test := range scanTests { + x := new(Int) + buf.Reset() + buf.WriteString(test.input) + if _, err := fmt.Fscanf(&buf, test.format, x); err != nil { + t.Errorf("#%d error: %s", i, err) + } + if x.String() != test.output { + t.Errorf("#%d got %s; want %s", i, x.String(), test.output) + } + if buf.Len() != test.remaining { + t.Errorf("#%d got %d bytes remaining; want %d", i, buf.Len(), test.remaining) + } + } +} diff --git a/src/cmd/compile/internal/big/nat.go b/src/cmd/compile/internal/big/nat.go new file mode 100644 index 0000000000..2a279d186c --- /dev/null +++ b/src/cmd/compile/internal/big/nat.go @@ -0,0 +1,1155 @@ +// Copyright 2009 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 big implements multi-precision arithmetic (big numbers). +// The following numeric types are supported: +// +// Int signed integers +// Rat rational numbers +// Float floating-point numbers +// +// Methods are typically of the form: +// +// func (z *T) Unary(x *T) *T // z = op x +// func (z *T) Binary(x, y *T) *T // z = x op y +// func (x *T) M() T1 // v = x.M() +// +// with T one of Int, Rat, or Float. For unary and binary operations, the +// result is the receiver (usually named z in that case); if it is one of +// the operands x or y it may be overwritten (and its memory reused). +// To enable chaining of operations, the result is also returned. Methods +// returning a result other than *Int, *Rat, or *Float take an operand as +// the receiver (usually named x in that case). +// +package big + +// This file contains operations on unsigned multi-precision integers. +// These are the building blocks for the operations on signed integers +// and rationals. + +import "math/rand" + +// An unsigned integer x of the form +// +// x = x[n-1]*_B^(n-1) + x[n-2]*_B^(n-2) + ... + x[1]*_B + x[0] +// +// with 0 <= x[i] < _B and 0 <= i < n is stored in a slice of length n, +// with the digits x[i] as the slice elements. +// +// A number is normalized if the slice contains no leading 0 digits. +// During arithmetic operations, denormalized values may occur but are +// always normalized before returning the final result. The normalized +// representation of 0 is the empty or nil slice (length = 0). +// +type nat []Word + +var ( + natOne = nat{1} + natTwo = nat{2} + natTen = nat{10} +) + +func (z nat) clear() { + for i := range z { + z[i] = 0 + } +} + +func (z nat) norm() nat { + i := len(z) + for i > 0 && z[i-1] == 0 { + i-- + } + return z[0:i] +} + +func (z nat) make(n int) nat { + if n <= cap(z) { + return z[:n] // reuse z + } + // Choosing a good value for e has significant performance impact + // because it increases the chance that a value can be reused. + const e = 4 // extra capacity + return make(nat, n, n+e) +} + +func (z nat) setWord(x Word) nat { + if x == 0 { + return z[:0] + } + z = z.make(1) + z[0] = x + return z +} + +func (z nat) setUint64(x uint64) nat { + // single-digit values + if w := Word(x); uint64(w) == x { + return z.setWord(w) + } + + // compute number of words n required to represent x + n := 0 + for t := x; t > 0; t >>= _W { + n++ + } + + // split x into n words + z = z.make(n) + for i := range z { + z[i] = Word(x & _M) + x >>= _W + } + + return z +} + +func (z nat) set(x nat) nat { + z = z.make(len(x)) + copy(z, x) + return z +} + +func (z nat) add(x, y nat) nat { + m := len(x) + n := len(y) + + switch { + case m < n: + return z.add(y, x) + case m == 0: + // n == 0 because m >= n; result is 0 + return z[:0] + case n == 0: + // result is x + return z.set(x) + } + // m > 0 + + z = z.make(m + 1) + c := addVV(z[0:n], x, y) + if m > n { + c = addVW(z[n:m], x[n:], c) + } + z[m] = c + + return z.norm() +} + +func (z nat) sub(x, y nat) nat { + m := len(x) + n := len(y) + + switch { + case m < n: + panic("underflow") + case m == 0: + // n == 0 because m >= n; result is 0 + return z[:0] + case n == 0: + // result is x + return z.set(x) + } + // m > 0 + + z = z.make(m) + c := subVV(z[0:n], x, y) + if m > n { + c = subVW(z[n:], x[n:], c) + } + if c != 0 { + panic("underflow") + } + + return z.norm() +} + +func (x nat) cmp(y nat) (r int) { + m := len(x) + n := len(y) + if m != n || m == 0 { + switch { + case m < n: + r = -1 + case m > n: + r = 1 + } + return + } + + i := m - 1 + for i > 0 && x[i] == y[i] { + i-- + } + + switch { + case x[i] < y[i]: + r = -1 + case x[i] > y[i]: + r = 1 + } + return +} + +func (z nat) mulAddWW(x nat, y, r Word) nat { + m := len(x) + if m == 0 || y == 0 { + return z.setWord(r) // result is r + } + // m > 0 + + z = z.make(m + 1) + z[m] = mulAddVWW(z[0:m], x, y, r) + + return z.norm() +} + +// basicMul multiplies x and y and leaves the result in z. +// The (non-normalized) result is placed in z[0 : len(x) + len(y)]. +func basicMul(z, x, y nat) { + z[0 : len(x)+len(y)].clear() // initialize z + for i, d := range y { + if d != 0 { + z[len(x)+i] = addMulVVW(z[i:i+len(x)], x, d) + } + } +} + +// 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) { + if c := addVV(z[0:n], z, x); c != 0 { + addVW(z[n:n+n>>1], z[n:], c) + } +} + +// Like karatsubaAdd, but does subtract. +func karatsubaSub(z, x nat, n int) { + if c := subVV(z[0:n], z, x); c != 0 { + subVW(z[n:n+n>>1], z[n:], c) + } +} + +// Operands that are shorter than karatsubaThreshold are multiplied using +// "grade school" multiplication; for longer operands the Karatsuba algorithm +// is used. +var karatsubaThreshold int = 40 // computed by calibrate.go + +// karatsuba multiplies x and y and leaves the result in z. +// Both x and y must have the same length n and n must be a +// power of 2. The result vector z must have len(z) >= 6*n. +// The (non-normalized) result is placed in z[0 : 2*n]. +func karatsuba(z, x, y nat) { + n := len(y) + + // Switch to basic multiplication if numbers are odd or small. + // (n is always even if karatsubaThreshold is even, but be + // conservative) + if n&1 != 0 || n < karatsubaThreshold || n < 2 { + basicMul(z, x, y) + return + } + // n&1 == 0 && n >= karatsubaThreshold && n >= 2 + + // Karatsuba multiplication is based on the observation that + // for two numbers x and y with: + // + // x = x1*b + x0 + // y = y1*b + y0 + // + // the product x*y can be obtained with 3 products z2, z1, z0 + // instead of 4: + // + // x*y = x1*y1*b*b + (x1*y0 + x0*y1)*b + x0*y0 + // = z2*b*b + z1*b + z0 + // + // with: + // + // xd = x1 - x0 + // yd = y0 - y1 + // + // z1 = xd*yd + z2 + z0 + // = (x1-x0)*(y0 - y1) + z2 + z0 + // = x1*y0 - x1*y1 - x0*y0 + x0*y1 + z2 + z0 + // = x1*y0 - z2 - z0 + x0*y1 + z2 + z0 + // = x1*y0 + x0*y1 + + // split x, y into "digits" + n2 := n >> 1 // n2 >= 1 + x1, x0 := x[n2:], x[0:n2] // x = x1*b + y0 + y1, y0 := y[n2:], y[0:n2] // y = y1*b + y0 + + // z is used for the result and temporary storage: + // + // 6*n 5*n 4*n 3*n 2*n 1*n 0*n + // z = [z2 copy|z0 copy| xd*yd | yd:xd | x1*y1 | x0*y0 ] + // + // For each recursive call of karatsuba, an unused slice of + // z is passed in that has (at least) half the length of the + // caller's z. + + // compute z0 and z2 with the result "in place" in z + karatsuba(z, x0, y0) // z0 = x0*y0 + karatsuba(z[n:], x1, y1) // z2 = x1*y1 + + // compute xd (or the negative value if underflow occurs) + s := 1 // sign of product xd*yd + xd := z[2*n : 2*n+n2] + if subVV(xd, x1, x0) != 0 { // x1-x0 + s = -s + subVV(xd, x0, x1) // x0-x1 + } + + // compute yd (or the negative value if underflow occurs) + yd := z[2*n+n2 : 3*n] + if subVV(yd, y0, y1) != 0 { // y0-y1 + s = -s + subVV(yd, y1, y0) // y1-y0 + } + + // p = (x1-x0)*(y0-y1) == x1*y0 - x1*y1 - x0*y0 + x0*y1 for s > 0 + // p = (x0-x1)*(y0-y1) == x0*y0 - x0*y1 - x1*y0 + x1*y1 for s < 0 + p := z[n*3:] + karatsuba(p, xd, yd) + + // save original z2:z0 + // (ok to use upper half of z since we're done recursing) + r := z[n*4:] + copy(r, z[:n*2]) + + // add up all partial products + // + // 2*n n 0 + // z = [ z2 | z0 ] + // + [ z0 ] + // + [ z2 ] + // + [ p ] + // + karatsubaAdd(z[n2:], r, n) + karatsubaAdd(z[n2:], r[n:], n) + if s > 0 { + karatsubaAdd(z[n2:], p, n) + } else { + karatsubaSub(z[n2:], p, n) + } +} + +// alias reports whether x and y share the same base array. +func alias(x, y nat) bool { + return cap(x) > 0 && cap(y) > 0 && &x[0:cap(x)][cap(x)-1] == &y[0:cap(y)][cap(y)-1] +} + +// addAt implements z += x<<(_W*i); z must be long enough. +// (we don't use nat.add because we need z to stay the same +// slice, and we don't need to normalize z after each addition) +func addAt(z, x nat, i int) { + if n := len(x); n > 0 { + if c := addVV(z[i:i+n], z[i:], x); c != 0 { + j := i + n + if j < len(z) { + addVW(z[j:], z[j:], c) + } + } + } +} + +func max(x, y int) int { + if x > y { + return x + } + return y +} + +// karatsubaLen computes an approximation to the maximum k <= n such that +// k = p<= 0. Thus, the +// result is the largest number that can be divided repeatedly by 2 before +// becoming about the value of karatsubaThreshold. +func karatsubaLen(n int) int { + i := uint(0) + for n > karatsubaThreshold { + n >>= 1 + i++ + } + return n << i +} + +func (z nat) mul(x, y nat) nat { + m := len(x) + n := len(y) + + switch { + case m < n: + return z.mul(y, x) + case m == 0 || n == 0: + return z[:0] + case n == 1: + return z.mulAddWW(x, y[0], 0) + } + // m >= n > 1 + + // determine if z can be reused + if alias(z, x) || alias(z, y) { + z = nil // z is an alias for x or y - cannot reuse + } + + // use basic multiplication if the numbers are small + if n < karatsubaThreshold { + z = z.make(m + n) + basicMul(z, x, y) + return z.norm() + } + // m >= n && n >= karatsubaThreshold && n >= 2 + + // determine Karatsuba length k such that + // + // x = xh*b + x0 (0 <= x0 < b) + // y = yh*b + y0 (0 <= y0 < b) + // b = 1<<(_W*k) ("base" of digits xi, yi) + // + k := karatsubaLen(n) + // k <= n + + // multiply x0 and y0 via Karatsuba + x0 := x[0:k] // x0 is not normalized + y0 := y[0:k] // y0 is not normalized + z = z.make(max(6*k, m+n)) // enough space for karatsuba of x0*y0 and full result of x*y + karatsuba(z, x0, y0) + z = z[0 : m+n] // z has final length but may be incomplete + z[2*k:].clear() // upper portion of z is garbage (and 2*k <= m+n since k <= n <= m) + + // If xh != 0 or yh != 0, add the missing terms to z. For + // + // xh = xi*b^i + ... + x2*b^2 + x1*b (0 <= xi < b) + // yh = y1*b (0 <= y1 < b) + // + // the missing terms are + // + // x0*y1*b and xi*y0*b^i, xi*y1*b^(i+1) for i > 0 + // + // since all the yi for i > 1 are 0 by choice of k: If any of them + // were > 0, then yh >= b^2 and thus y >= b^2. Then k' = k*2 would + // be a larger valid threshold contradicting the assumption about k. + // + if k < n || m != n { + var t nat + + // add x0*y1*b + x0 := x0.norm() + y1 := y[k:] // y1 is normalized because y is + t = t.mul(x0, y1) // update t so we don't lose t's underlying array + addAt(z, t, k) + + // add xi*y0< k { + xi = xi[:k] + } + xi = xi.norm() + t = t.mul(xi, y0) + addAt(z, t, i) + t = t.mul(xi, y1) + addAt(z, t, i+k) + } + } + + return z.norm() +} + +// mulRange computes the product of all the unsigned integers in the +// range [a, b] inclusively. If a > b (empty range), the result is 1. +func (z nat) mulRange(a, b uint64) nat { + switch { + case a == 0: + // cut long ranges short (optimization) + return z.setUint64(0) + case a > b: + return z.setUint64(1) + case a == b: + return z.setUint64(a) + case a+1 == b: + return z.mul(nat(nil).setUint64(a), nat(nil).setUint64(b)) + } + m := (a + b) / 2 + return z.mul(nat(nil).mulRange(a, m), nat(nil).mulRange(m+1, b)) +} + +// q = (x-r)/y, with 0 <= r < y +func (z nat) divW(x nat, y Word) (q nat, r Word) { + m := len(x) + switch { + case y == 0: + panic("division by zero") + case y == 1: + q = z.set(x) // result is x + return + case m == 0: + q = z[:0] // result is 0 + return + } + // m > 0 + z = z.make(m) + r = divWVW(z, 0, x, y) + q = z.norm() + return +} + +func (z nat) div(z2, u, v nat) (q, r nat) { + if len(v) == 0 { + panic("division by zero") + } + + if u.cmp(v) < 0 { + q = z[:0] + r = z2.set(u) + return + } + + if len(v) == 1 { + var r2 Word + q, r2 = z.divW(u, v[0]) + r = z2.setWord(r2) + return + } + + q, r = z.divLarge(z2, u, v) + return +} + +// q = (uIn-r)/v, with 0 <= r < y +// Uses z as storage for q, and u as storage for r if possible. +// See Knuth, Volume 2, section 4.3.1, Algorithm D. +// Preconditions: +// len(v) >= 2 +// len(uIn) >= len(v) +func (z nat) divLarge(u, uIn, v nat) (q, r nat) { + n := len(v) + m := len(uIn) - n + + // determine if z can be reused + // TODO(gri) should find a better solution - this if statement + // is very costly (see e.g. time pidigits -s -n 10000) + if alias(z, uIn) || alias(z, v) { + z = nil // z is an alias for uIn or v - cannot reuse + } + q = z.make(m + 1) + + qhatv := make(nat, n+1) + if alias(u, uIn) || alias(u, v) { + u = nil // u is an alias for uIn or v - cannot reuse + } + u = u.make(len(uIn) + 1) + u.clear() // TODO(gri) no need to clear if we allocated a new u + + // D1. + shift := leadingZeros(v[n-1]) + if shift > 0 { + // do not modify v, it may be used by another goroutine simultaneously + v1 := make(nat, n) + shlVU(v1, v, shift) + v = v1 + } + u[len(uIn)] = shlVU(u[0:len(uIn)], uIn, shift) + + // D2. + for j := m; j >= 0; j-- { + // D3. + qhat := Word(_M) + if u[j+n] != v[n-1] { + var rhat Word + qhat, rhat = divWW(u[j+n], u[j+n-1], v[n-1]) + + // x1 | x2 = q̂v_{n-2} + x1, x2 := mulWW(qhat, v[n-2]) + // test if q̂v_{n-2} > br̂ + u_{j+n-2} + for greaterThan(x1, x2, rhat, u[j+n-2]) { + qhat-- + prevRhat := rhat + rhat += v[n-1] + // v[n-1] >= 0, so this tests for overflow. + if rhat < prevRhat { + break + } + x1, x2 = mulWW(qhat, v[n-2]) + } + } + + // D4. + qhatv[n] = mulAddVWW(qhatv[0:n], v, qhat, 0) + + c := subVV(u[j:j+len(qhatv)], u[j:], qhatv) + if c != 0 { + c := addVV(u[j:j+n], u[j:], v) + u[j+n] += c + qhat-- + } + + q[j] = qhat + } + + q = q.norm() + shrVU(u, u, shift) + r = u.norm() + + return q, r +} + +// Length of x in bits. x must be normalized. +func (x nat) bitLen() int { + if i := len(x) - 1; i >= 0 { + return i*_W + bitLen(x[i]) + } + return 0 +} + +const deBruijn32 = 0x077CB531 + +var deBruijn32Lookup = []byte{ + 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, + 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9, +} + +const deBruijn64 = 0x03f79d71b4ca8b09 + +var deBruijn64Lookup = []byte{ + 0, 1, 56, 2, 57, 49, 28, 3, 61, 58, 42, 50, 38, 29, 17, 4, + 62, 47, 59, 36, 45, 43, 51, 22, 53, 39, 33, 30, 24, 18, 12, 5, + 63, 55, 48, 27, 60, 41, 37, 16, 46, 35, 44, 21, 52, 32, 23, 11, + 54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6, +} + +// trailingZeroBits returns the number of consecutive least significant zero +// bits of x. +func trailingZeroBits(x Word) uint { + // x & -x leaves only the right-most bit set in the word. Let k be the + // index of that bit. Since only a single bit is set, the value is two + // to the power of k. Multiplying by a power of two is equivalent to + // left shifting, in this case by k bits. The de Bruijn constant is + // such that all six bit, consecutive substrings are distinct. + // Therefore, if we have a left shifted version of this constant we can + // find by how many bits it was shifted by looking at which six bit + // substring ended up at the top of the word. + // (Knuth, volume 4, section 7.3.1) + switch _W { + case 32: + return uint(deBruijn32Lookup[((x&-x)*deBruijn32)>>27]) + case 64: + return uint(deBruijn64Lookup[((x&-x)*(deBruijn64&_M))>>58]) + default: + panic("unknown word size") + } +} + +// trailingZeroBits returns the number of consecutive least significant zero +// bits of x. +func (x nat) trailingZeroBits() uint { + if len(x) == 0 { + return 0 + } + var i uint + for x[i] == 0 { + i++ + } + // x[i] != 0 + return i*_W + trailingZeroBits(x[i]) +} + +// z = x << s +func (z nat) shl(x nat, s uint) nat { + m := len(x) + if m == 0 { + return z[:0] + } + // m > 0 + + n := m + int(s/_W) + z = z.make(n + 1) + z[n] = shlVU(z[n-m:n], x, s%_W) + z[0 : n-m].clear() + + return z.norm() +} + +// z = x >> s +func (z nat) shr(x nat, s uint) nat { + m := len(x) + n := m - int(s/_W) + if n <= 0 { + return z[:0] + } + // n > 0 + + z = z.make(n) + shrVU(z, x[m-n:], s%_W) + + return z.norm() +} + +func (z nat) setBit(x nat, i uint, b uint) nat { + j := int(i / _W) + m := Word(1) << (i % _W) + n := len(x) + switch b { + case 0: + z = z.make(n) + copy(z, x) + if j >= n { + // no need to grow + return z + } + z[j] &^= m + return z.norm() + case 1: + if j >= n { + z = z.make(j + 1) + z[n:].clear() + } else { + z = z.make(n) + } + copy(z, x) + z[j] |= m + // no need to normalize + return z + } + panic("set bit is not 0 or 1") +} + +// bit returns the value of the i'th bit, with lsb == bit 0. +func (x nat) bit(i uint) uint { + j := i / _W + if j >= uint(len(x)) { + return 0 + } + // 0 <= j < len(x) + return uint(x[j] >> (i % _W) & 1) +} + +// sticky returns 1 if there's a 1 bit within the +// i least significant bits, otherwise it returns 0. +func (x nat) sticky(i uint) uint { + j := i / _W + if j >= uint(len(x)) { + if len(x) == 0 { + return 0 + } + return 1 + } + // 0 <= j < len(x) + for _, x := range x[:j] { + if x != 0 { + return 1 + } + } + if x[j]<<(_W-i%_W) != 0 { + return 1 + } + return 0 +} + +func (z nat) and(x, y nat) nat { + m := len(x) + n := len(y) + if m > n { + m = n + } + // m <= n + + z = z.make(m) + for i := 0; i < m; i++ { + z[i] = x[i] & y[i] + } + + return z.norm() +} + +func (z nat) andNot(x, y nat) nat { + m := len(x) + n := len(y) + if n > m { + n = m + } + // m >= n + + z = z.make(m) + for i := 0; i < n; i++ { + z[i] = x[i] &^ y[i] + } + copy(z[n:m], x[n:m]) + + return z.norm() +} + +func (z nat) or(x, y nat) nat { + m := len(x) + n := len(y) + s := x + if m < n { + n, m = m, n + s = y + } + // m >= n + + z = z.make(m) + for i := 0; i < n; i++ { + z[i] = x[i] | y[i] + } + copy(z[n:m], s[n:m]) + + return z.norm() +} + +func (z nat) xor(x, y nat) nat { + m := len(x) + n := len(y) + s := x + if m < n { + n, m = m, n + s = y + } + // m >= n + + z = z.make(m) + for i := 0; i < n; i++ { + z[i] = x[i] ^ y[i] + } + copy(z[n:m], s[n:m]) + + return z.norm() +} + +// greaterThan reports whether (x1<<_W + x2) > (y1<<_W + y2) +func greaterThan(x1, x2, y1, y2 Word) bool { + return x1 > y1 || x1 == y1 && x2 > y2 +} + +// modW returns x % d. +func (x nat) modW(d Word) (r Word) { + // TODO(agl): we don't actually need to store the q value. + var q nat + q = q.make(len(x)) + return divWVW(q, 0, x, d) +} + +// random creates a random integer in [0..limit), using the space in z if +// possible. n is the bit length of limit. +func (z nat) random(rand *rand.Rand, limit nat, n int) nat { + if alias(z, limit) { + z = nil // z is an alias for limit - cannot reuse + } + z = z.make(len(limit)) + + bitLengthOfMSW := uint(n % _W) + if bitLengthOfMSW == 0 { + bitLengthOfMSW = _W + } + mask := Word((1 << bitLengthOfMSW) - 1) + + for { + switch _W { + case 32: + for i := range z { + z[i] = Word(rand.Uint32()) + } + case 64: + for i := range z { + z[i] = Word(rand.Uint32()) | Word(rand.Uint32())<<32 + } + default: + panic("unknown word size") + } + z[len(limit)-1] &= mask + if z.cmp(limit) < 0 { + break + } + } + + return z.norm() +} + +// If m != 0 (i.e., len(m) != 0), expNN sets z to x**y mod m; +// otherwise it sets z to x**y. The result is the value of z. +func (z nat) expNN(x, y, m nat) nat { + if alias(z, x) || alias(z, y) { + // We cannot allow in-place modification of x or y. + z = nil + } + + // x**y mod 1 == 0 + if len(m) == 1 && m[0] == 1 { + return z.setWord(0) + } + // m == 0 || m > 1 + + // x**0 == 1 + if len(y) == 0 { + return z.setWord(1) + } + // y > 0 + + if len(m) != 0 { + // We likely end up being as long as the modulus. + z = z.make(len(m)) + } + z = z.set(x) + + // If the base is non-trivial and the exponent is large, we use + // 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. + if len(x) > 1 && len(y) > 1 && len(m) > 0 { + return z.expNNWindowed(x, y, m) + } + + v := y[len(y)-1] // v > 0 because y is normalized and y > 0 + shift := leadingZeros(v) + 1 + v <<= shift + var q nat + + const mask = 1 << (_W - 1) + + // We walk through the bits of the exponent one by one. Each time we + // see a bit, we square, thus doubling the power. If the bit is a one, + // we also multiply by x, thus adding one to the power. + + w := _W - int(shift) + // zz and r are used to avoid allocating in mul and div as + // otherwise the arguments would alias. + var zz, r nat + for j := 0; j < w; j++ { + zz = zz.mul(z, z) + zz, z = z, zz + + if v&mask != 0 { + zz = zz.mul(z, x) + zz, z = z, zz + } + + if len(m) != 0 { + zz, r = zz.div(r, z, m) + zz, r, q, z = q, z, zz, r + } + + v <<= 1 + } + + for i := len(y) - 2; i >= 0; i-- { + v = y[i] + + for j := 0; j < _W; j++ { + zz = zz.mul(z, z) + zz, z = z, zz + + if v&mask != 0 { + zz = zz.mul(z, x) + zz, z = z, zz + } + + if len(m) != 0 { + zz, r = zz.div(r, z, m) + zz, r, q, z = q, z, zz, r + } + + v <<= 1 + } + } + + return z.norm() +} + +// expNNWindowed calculates x**y mod m using a fixed, 4-bit window. +func (z nat) expNNWindowed(x, y, m nat) nat { + // zz and r are used to avoid allocating in mul and div as otherwise + // the arguments would alias. + var zz, r nat + + const n = 4 + // powers[i] contains x^i. + var powers [1 << n]nat + powers[0] = natOne + powers[1] = x + for i := 2; i < 1<= 0; i-- { + yi := y[i] + for j := 0; j < _W; j += n { + if i != len(y)-1 || j != 0 { + // Unrolled loop for significant performance + // gain. Use go test -bench=".*" in crypto/rsa + // to check performance before making changes. + zz = zz.mul(z, z) + zz, z = z, zz + zz, r = zz.div(r, z, m) + z, r = r, z + + zz = zz.mul(z, z) + zz, z = z, zz + zz, r = zz.div(r, z, m) + z, r = r, z + + zz = zz.mul(z, z) + zz, z = z, zz + zz, r = zz.div(r, z, m) + z, r = r, z + + zz = zz.mul(z, z) + zz, z = z, zz + zz, r = zz.div(r, z, m) + z, r = r, z + } + + zz = zz.mul(z, powers[yi>>(_W-n)]) + zz, z = z, zz + zz, r = zz.div(r, z, m) + z, r = r, z + + yi <<= n + } + } + + return z.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. +func (n nat) probablyPrime(reps int) bool { + if len(n) == 0 { + return false + } + + if len(n) == 1 { + if n[0] < 2 { + return false + } + + if n[0]%2 == 0 { + return n[0] == 2 + } + + // We have to exclude these cases because we reject all + // multiples of these numbers below. + switch n[0] { + case 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53: + return true + } + } + + if n[0]&1 == 0 { + return false // n is even + } + + const primesProduct32 = 0xC0CFD797 // Π {p ∈ primes, 2 < p <= 29} + const primesProduct64 = 0xE221F97C30E94E1D // Π {p ∈ primes, 2 < p <= 53} + + var r Word + switch _W { + case 32: + r = n.modW(primesProduct32) + case 64: + r = n.modW(primesProduct64 & _M) + default: + panic("Unknown word size") + } + + if r%3 == 0 || r%5 == 0 || r%7 == 0 || r%11 == 0 || + r%13 == 0 || r%17 == 0 || r%19 == 0 || r%23 == 0 || r%29 == 0 { + return false + } + + if _W == 64 && (r%31 == 0 || r%37 == 0 || r%41 == 0 || + r%43 == 0 || r%47 == 0 || r%53 == 0) { + return false + } + + nm1 := nat(nil).sub(n, natOne) + // determine q, k such that nm1 = q << k + k := nm1.trailingZeroBits() + q := nat(nil).shr(nm1, k) + + nm3 := nat(nil).sub(nm1, natTwo) + rand := rand.New(rand.NewSource(int64(n[0]))) + + var x, y, quotient nat + nm3Len := nm3.bitLen() + +NextRandom: + for i := 0; i < reps; i++ { + x = x.random(rand, nm3, nm3Len) + x = x.add(x, natTwo) + y = y.expNN(x, q, n) + if y.cmp(natOne) == 0 || y.cmp(nm1) == 0 { + continue + } + for j := uint(1); j < k; j++ { + y = y.mul(y, y) + quotient, y = quotient.div(y, y, n) + if y.cmp(nm1) == 0 { + continue NextRandom + } + if y.cmp(natOne) == 0 { + return false + } + } + return false + } + + return true +} + +// bytes writes the value of z into buf using big-endian encoding. +// len(buf) must be >= len(z)*_S. The value of z is encoded in the +// slice buf[i:]. The number i of unused bytes at the beginning of +// buf is returned as result. +func (z nat) bytes(buf []byte) (i int) { + i = len(buf) + for _, d := range z { + for j := 0; j < _S; j++ { + i-- + buf[i] = byte(d) + d >>= 8 + } + } + + for i < len(buf) && buf[i] == 0 { + i++ + } + + return +} + +// setBytes interprets buf as the bytes of a big-endian unsigned +// integer, sets z to that value, and returns z. +func (z nat) setBytes(buf []byte) nat { + z = z.make((len(buf) + _S - 1) / _S) + + k := 0 + s := uint(0) + var d Word + for i := len(buf); i > 0; i-- { + d |= Word(buf[i-1]) << s + if s += 8; s == _S*8 { + z[k] = d + k++ + s = 0 + d = 0 + } + } + if k < len(z) { + z[k] = d + } + + return z.norm() +} diff --git a/src/cmd/compile/internal/big/nat_test.go b/src/cmd/compile/internal/big/nat_test.go new file mode 100644 index 0000000000..b25a89f731 --- /dev/null +++ b/src/cmd/compile/internal/big/nat_test.go @@ -0,0 +1,518 @@ +// Copyright 2009 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 big + +import ( + "runtime" + "strings" + "testing" +) + +var cmpTests = []struct { + x, y nat + r int +}{ + {nil, nil, 0}, + {nil, nat(nil), 0}, + {nat(nil), nil, 0}, + {nat(nil), nat(nil), 0}, + {nat{0}, nat{0}, 0}, + {nat{0}, nat{1}, -1}, + {nat{1}, nat{0}, 1}, + {nat{1}, nat{1}, 0}, + {nat{0, _M}, nat{1}, 1}, + {nat{1}, nat{0, _M}, -1}, + {nat{1, _M}, nat{0, _M}, 1}, + {nat{0, _M}, nat{1, _M}, -1}, + {nat{16, 571956, 8794, 68}, nat{837, 9146, 1, 754489}, -1}, + {nat{34986, 41, 105, 1957}, nat{56, 7458, 104, 1957}, 1}, +} + +func TestCmp(t *testing.T) { + for i, a := range cmpTests { + r := a.x.cmp(a.y) + if r != a.r { + t.Errorf("#%d got r = %v; want %v", i, r, a.r) + } + } +} + +type funNN func(z, x, y nat) nat +type argNN struct { + z, x, y nat +} + +var sumNN = []argNN{ + {}, + {nat{1}, nil, nat{1}}, + {nat{1111111110}, nat{123456789}, nat{987654321}}, + {nat{0, 0, 0, 1}, nil, nat{0, 0, 0, 1}}, + {nat{0, 0, 0, 1111111110}, nat{0, 0, 0, 123456789}, nat{0, 0, 0, 987654321}}, + {nat{0, 0, 0, 1}, nat{0, 0, _M}, nat{0, 0, 1}}, +} + +var prodNN = []argNN{ + {}, + {nil, nil, nil}, + {nil, nat{991}, nil}, + {nat{991}, nat{991}, nat{1}}, + {nat{991 * 991}, nat{991}, nat{991}}, + {nat{0, 0, 991 * 991}, nat{0, 991}, nat{0, 991}}, + {nat{1 * 991, 2 * 991, 3 * 991, 4 * 991}, nat{1, 2, 3, 4}, nat{991}}, + {nat{4, 11, 20, 30, 20, 11, 4}, nat{1, 2, 3, 4}, nat{4, 3, 2, 1}}, + // 3^100 * 3^28 = 3^128 + { + natFromString("11790184577738583171520872861412518665678211592275841109096961"), + natFromString("515377520732011331036461129765621272702107522001"), + natFromString("22876792454961"), + }, + // z = 111....1 (70000 digits) + // x = 10^(99*700) + ... + 10^1400 + 10^700 + 1 + // y = 111....1 (700 digits, larger than Karatsuba threshold on 32-bit and 64-bit) + { + natFromString(strings.Repeat("1", 70000)), + natFromString("1" + strings.Repeat(strings.Repeat("0", 699)+"1", 99)), + natFromString(strings.Repeat("1", 700)), + }, + // z = 111....1 (20000 digits) + // x = 10^10000 + 1 + // y = 111....1 (10000 digits) + { + natFromString(strings.Repeat("1", 20000)), + natFromString("1" + strings.Repeat("0", 9999) + "1"), + natFromString(strings.Repeat("1", 10000)), + }, +} + +func natFromString(s string) nat { + x, _, _, err := nat(nil).scan(strings.NewReader(s), 0, false) + if err != nil { + panic(err) + } + return x +} + +func TestSet(t *testing.T) { + for _, a := range sumNN { + z := nat(nil).set(a.z) + if z.cmp(a.z) != 0 { + t.Errorf("got z = %v; want %v", z, a.z) + } + } +} + +func testFunNN(t *testing.T, msg string, f funNN, a argNN) { + z := f(nil, a.x, a.y) + if z.cmp(a.z) != 0 { + t.Errorf("%s%+v\n\tgot z = %v; want %v", msg, a, z, a.z) + } +} + +func TestFunNN(t *testing.T) { + for _, a := range sumNN { + arg := a + testFunNN(t, "add", nat.add, arg) + + arg = argNN{a.z, a.y, a.x} + testFunNN(t, "add symmetric", nat.add, arg) + + arg = argNN{a.x, a.z, a.y} + testFunNN(t, "sub", nat.sub, arg) + + arg = argNN{a.y, a.z, a.x} + testFunNN(t, "sub symmetric", nat.sub, arg) + } + + for _, a := range prodNN { + arg := a + testFunNN(t, "mul", nat.mul, arg) + + arg = argNN{a.z, a.y, a.x} + testFunNN(t, "mul symmetric", nat.mul, arg) + } +} + +var mulRangesN = []struct { + a, b uint64 + prod string +}{ + {0, 0, "0"}, + {1, 1, "1"}, + {1, 2, "2"}, + {1, 3, "6"}, + {10, 10, "10"}, + {0, 100, "0"}, + {0, 1e9, "0"}, + {1, 0, "1"}, // empty range + {100, 1, "1"}, // empty range + {1, 10, "3628800"}, // 10! + {1, 20, "2432902008176640000"}, // 20! + {1, 100, + "933262154439441526816992388562667004907159682643816214685929" + + "638952175999932299156089414639761565182862536979208272237582" + + "51185210916864000000000000000000000000", // 100! + }, +} + +func TestMulRangeN(t *testing.T) { + for i, r := range mulRangesN { + prod := nat(nil).mulRange(r.a, r.b).decimalString() + if prod != r.prod { + t.Errorf("#%d: got %s; want %s", i, prod, r.prod) + } + } +} + +// allocBytes returns the number of bytes allocated by invoking f. +func allocBytes(f func()) uint64 { + var stats runtime.MemStats + runtime.ReadMemStats(&stats) + t := stats.TotalAlloc + f() + runtime.ReadMemStats(&stats) + return stats.TotalAlloc - t +} + +// TestMulUnbalanced tests that multiplying numbers of different lengths +// does not cause deep recursion and in turn allocate too much memory. +// Test case for issue 3807. +func TestMulUnbalanced(t *testing.T) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) + x := rndNat(50000) + y := rndNat(40) + allocSize := allocBytes(func() { + nat(nil).mul(x, y) + }) + inputSize := uint64(len(x)+len(y)) * _S + if ratio := allocSize / uint64(inputSize); ratio > 10 { + t.Errorf("multiplication uses too much memory (%d > %d times the size of inputs)", allocSize, ratio) + } +} + +func rndNat(n int) nat { + return nat(rndV(n)).norm() +} + +func BenchmarkMul(b *testing.B) { + mulx := rndNat(1e4) + muly := rndNat(1e4) + b.ResetTimer() + for i := 0; i < b.N; i++ { + var z nat + z.mul(mulx, muly) + } +} + +func TestLeadingZeros(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) + } + x >>= 1 + } +} + +type shiftTest struct { + in nat + shift uint + out nat +} + +var leftShiftTests = []shiftTest{ + {nil, 0, nil}, + {nil, 1, nil}, + {natOne, 0, natOne}, + {natOne, 1, natTwo}, + {nat{1 << (_W - 1)}, 1, nat{0}}, + {nat{1 << (_W - 1), 0}, 1, nat{0, 1}}, +} + +func TestShiftLeft(t *testing.T) { + for i, test := range leftShiftTests { + var z nat + z = z.shl(test.in, test.shift) + for j, d := range test.out { + if j >= len(z) || z[j] != d { + t.Errorf("#%d: got: %v want: %v", i, z, test.out) + break + } + } + } +} + +var rightShiftTests = []shiftTest{ + {nil, 0, nil}, + {nil, 1, nil}, + {natOne, 0, natOne}, + {natOne, 1, nil}, + {natTwo, 1, natOne}, + {nat{0, 1}, 1, nat{1 << (_W - 1)}}, + {nat{2, 1, 1}, 1, nat{1<<(_W-1) + 1, 1 << (_W - 1)}}, +} + +func TestShiftRight(t *testing.T) { + for i, test := range rightShiftTests { + var z nat + z = z.shr(test.in, test.shift) + for j, d := range test.out { + if j >= len(z) || z[j] != d { + t.Errorf("#%d: got: %v want: %v", i, z, test.out) + break + } + } + } +} + +type modWTest struct { + in string + dividend string + out string +} + +var modWTests32 = []modWTest{ + {"23492635982634928349238759823742", "252341", "220170"}, +} + +var modWTests64 = []modWTest{ + {"6527895462947293856291561095690465243862946", "524326975699234", "375066989628668"}, +} + +func runModWTests(t *testing.T, tests []modWTest) { + for i, test := range tests { + in, _ := new(Int).SetString(test.in, 10) + d, _ := new(Int).SetString(test.dividend, 10) + out, _ := new(Int).SetString(test.out, 10) + + r := in.abs.modW(d.abs[0]) + if r != out.abs[0] { + t.Errorf("#%d failed: got %d want %s", i, r, out) + } + } +} + +func TestModW(t *testing.T) { + if _W >= 32 { + runModWTests(t, modWTests32) + } + if _W >= 64 { + runModWTests(t, modWTests64) + } +} + +func TestTrailingZeroBits(t *testing.T) { + // test 0 case explicitly + if n := trailingZeroBits(0); n != 0 { + t.Errorf("got trailingZeroBits(0) = %d; want 0", n) + } + + x := Word(1) + for i := uint(0); i < _W; i++ { + n := trailingZeroBits(x) + if n != i { + t.Errorf("got trailingZeroBits(%#x) = %d; want %d", x, n, i%_W) + } + x <<= 1 + } + + // test 0 case explicitly + if n := nat(nil).trailingZeroBits(); n != 0 { + t.Errorf("got nat(nil).trailingZeroBits() = %d; want 0", n) + } + + y := nat(nil).set(natOne) + for i := uint(0); i <= 3*_W; i++ { + n := y.trailingZeroBits() + if n != i { + t.Errorf("got 0x%s.trailingZeroBits() = %d; want %d", y.hexString(), n, i) + } + y = y.shl(y, 1) + } +} + +var expNNTests = []struct { + x, y, m string + out string +}{ + {"0", "0", "0", "1"}, + {"0", "0", "1", "0"}, + {"1", "1", "1", "0"}, + {"2", "1", "1", "0"}, + {"2", "2", "1", "0"}, + {"10", "100000000000", "1", "0"}, + {"0x8000000000000000", "2", "", "0x40000000000000000000000000000000"}, + {"0x8000000000000000", "2", "6719", "4944"}, + {"0x8000000000000000", "3", "6719", "5447"}, + {"0x8000000000000000", "1000", "6719", "1603"}, + {"0x8000000000000000", "1000000", "6719", "3199"}, + { + "2938462938472983472983659726349017249287491026512746239764525612965293865296239471239874193284792387498274256129746192347", + "298472983472983471903246121093472394872319615612417471234712061", + "29834729834729834729347290846729561262544958723956495615629569234729836259263598127342374289365912465901365498236492183464", + "23537740700184054162508175125554701713153216681790245129157191391322321508055833908509185839069455749219131480588829346291", + }, +} + +func TestExpNN(t *testing.T) { + for i, test := range expNNTests { + x := natFromString(test.x) + y := natFromString(test.y) + out := natFromString(test.out) + + var m nat + if len(test.m) > 0 { + m = natFromString(test.m) + } + + z := nat(nil).expNN(x, y, m) + if z.cmp(out) != 0 { + t.Errorf("#%d got %s want %s", i, z.decimalString(), out.decimalString()) + } + } +} + +func ExpHelper(b *testing.B, x, y Word) { + var z nat + for i := 0; i < b.N; i++ { + z.expWW(x, y) + } +} + +func BenchmarkExp3Power0x10(b *testing.B) { ExpHelper(b, 3, 0x10) } +func BenchmarkExp3Power0x40(b *testing.B) { ExpHelper(b, 3, 0x40) } +func BenchmarkExp3Power0x100(b *testing.B) { ExpHelper(b, 3, 0x100) } +func BenchmarkExp3Power0x400(b *testing.B) { ExpHelper(b, 3, 0x400) } +func BenchmarkExp3Power0x1000(b *testing.B) { ExpHelper(b, 3, 0x1000) } +func BenchmarkExp3Power0x4000(b *testing.B) { ExpHelper(b, 3, 0x4000) } +func BenchmarkExp3Power0x10000(b *testing.B) { ExpHelper(b, 3, 0x10000) } +func BenchmarkExp3Power0x40000(b *testing.B) { ExpHelper(b, 3, 0x40000) } +func BenchmarkExp3Power0x100000(b *testing.B) { ExpHelper(b, 3, 0x100000) } +func BenchmarkExp3Power0x400000(b *testing.B) { ExpHelper(b, 3, 0x400000) } + +func fibo(n int) nat { + switch n { + case 0: + return nil + case 1: + return nat{1} + } + f0 := fibo(0) + f1 := fibo(1) + var f2 nat + for i := 1; i < n; i++ { + f2 = f2.add(f0, f1) + f0, f1, f2 = f1, f2, f0 + } + return f1 +} + +var fiboNums = []string{ + "0", + "55", + "6765", + "832040", + "102334155", + "12586269025", + "1548008755920", + "190392490709135", + "23416728348467685", + "2880067194370816120", + "354224848179261915075", +} + +func TestFibo(t *testing.T) { + for i, want := range fiboNums { + n := i * 10 + got := fibo(n).decimalString() + if got != want { + t.Errorf("fibo(%d) failed: got %s want %s", n, got, want) + } + } +} + +func BenchmarkFibo(b *testing.B) { + for i := 0; i < b.N; i++ { + fibo(1e0) + fibo(1e1) + fibo(1e2) + fibo(1e3) + fibo(1e4) + fibo(1e5) + } +} + +var bitTests = []struct { + x string + i uint + want uint +}{ + {"0", 0, 0}, + {"0", 1, 0}, + {"0", 1000, 0}, + + {"0x1", 0, 1}, + {"0x10", 0, 0}, + {"0x10", 3, 0}, + {"0x10", 4, 1}, + {"0x10", 5, 0}, + + {"0x8000000000000000", 62, 0}, + {"0x8000000000000000", 63, 1}, + {"0x8000000000000000", 64, 0}, + + {"0x3" + strings.Repeat("0", 32), 127, 0}, + {"0x3" + strings.Repeat("0", 32), 128, 1}, + {"0x3" + strings.Repeat("0", 32), 129, 1}, + {"0x3" + strings.Repeat("0", 32), 130, 0}, +} + +func TestBit(t *testing.T) { + for i, test := range bitTests { + x := natFromString(test.x) + if got := x.bit(test.i); got != test.want { + t.Errorf("#%d: %s.bit(%d) = %v; want %v", i, test.x, test.i, got, test.want) + } + } +} + +var stickyTests = []struct { + x string + i uint + want uint +}{ + {"0", 0, 0}, + {"0", 1, 0}, + {"0", 1000, 0}, + + {"0x1", 0, 0}, + {"0x1", 1, 1}, + + {"0x1350", 0, 0}, + {"0x1350", 4, 0}, + {"0x1350", 5, 1}, + + {"0x8000000000000000", 63, 0}, + {"0x8000000000000000", 64, 1}, + + {"0x1" + strings.Repeat("0", 100), 400, 0}, + {"0x1" + strings.Repeat("0", 100), 401, 1}, +} + +func TestSticky(t *testing.T) { + for i, test := range stickyTests { + x := natFromString(test.x) + if got := x.sticky(test.i); got != test.want { + t.Errorf("#%d: %s.sticky(%d) = %v; want %v", i, test.x, test.i, got, test.want) + } + if test.want == 1 { + // all subsequent i's should also return 1 + for d := uint(1); d <= 3; d++ { + if got := x.sticky(test.i + d); got != 1 { + t.Errorf("#%d: %s.sticky(%d) = %v; want %v", i, test.x, test.i+d, got, 1) + } + } + } + } +} diff --git a/src/cmd/compile/internal/big/natconv.go b/src/cmd/compile/internal/big/natconv.go new file mode 100644 index 0000000000..022dcfe38c --- /dev/null +++ b/src/cmd/compile/internal/big/natconv.go @@ -0,0 +1,495 @@ +// 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. + +// This file implements nat-to-string conversion functions. + +package big + +import ( + "errors" + "fmt" + "io" + "math" + "sync" +) + +// MaxBase is the largest number base accepted for string conversions. +const MaxBase = 'z' - 'a' + 10 + 1 + +// maxPow returns (b**n, n) such that b**n is the largest power b**n <= _M. +// For instance maxPow(10) == (1e19, 19) for 19 decimal digits in a 64bit Word. +// In other words, at most n digits in base b fit into a Word. +// TODO(gri) replace this with a table, generated at build time. +func maxPow(b Word) (p Word, n int) { + p, n = b, 1 // assuming b <= _M + for max := _M / b; p <= max; { + // p == b**n && p <= max + p *= b + n++ + } + // p == b**n && p <= _M + return +} + +// pow returns x**n for n > 0, and 1 otherwise. +func pow(x Word, n int) (p Word) { + // n == sum of bi * 2**i, for 0 <= i < imax, and bi is 0 or 1 + // thus x**n == product of x**(2**i) for all i where bi == 1 + // (Russian Peasant Method for exponentiation) + p = 1 + for n > 0 { + if n&1 != 0 { + p *= x + } + x *= x + n >>= 1 + } + return +} + +// scan scans the number corresponding to the longest possible prefix +// from r representing an unsigned number in a given conversion base. +// It returns the corresponding natural number res, the actual base b, +// a digit count, and a read or syntax error err, if any. +// +// number = [ prefix ] mantissa . +// prefix = "0" [ "x" | "X" | "b" | "B" ] . +// mantissa = digits | digits "." [ digits ] | "." digits . +// digits = digit { digit } . +// digit = "0" ... "9" | "a" ... "z" | "A" ... "Z" . +// +// Unless fracOk is set, the base argument must be 0 or a value between +// 2 and MaxBase. If fracOk is set, the base argument must be one of +// 0, 2, 10, or 16. Providing an invalid base argument leads to a run- +// time panic. +// +// For base 0, the number prefix determines the actual base: A prefix of +// ``0x'' or ``0X'' selects base 16; if fracOk is not set, the ``0'' prefix +// selects base 8, and a ``0b'' or ``0B'' prefix selects base 2. Otherwise +// the selected base is 10 and no prefix is accepted. +// +// If fracOk is set, an octal prefix is ignored (a leading ``0'' simply +// stands for a zero digit), and a period followed by a fractional part +// is permitted. The result value is computed as if there were no period +// present; and the count value is used to determine the fractional part. +// +// A result digit count > 0 corresponds to the number of (non-prefix) digits +// parsed. A digit count <= 0 indicates the presence of a period (if fracOk +// is set, only), and -count is the number of fractional digits found. +// In this case, the actual value of the scanned number is res * b**count. +// +func (z nat) scan(r io.ByteScanner, base int, fracOk bool) (res nat, b, count int, err error) { + // reject illegal bases + baseOk := base == 0 || + !fracOk && 2 <= base && base <= MaxBase || + fracOk && (base == 2 || base == 10 || base == 16) + if !baseOk { + panic(fmt.Sprintf("illegal number base %d", base)) + } + + // one char look-ahead + ch, err := r.ReadByte() + if err != nil { + return + } + + // determine actual base + b = base + if base == 0 { + // actual base is 10 unless there's a base prefix + b = 10 + if ch == '0' { + count = 1 + switch ch, err = r.ReadByte(); err { + case nil: + // possibly one of 0x, 0X, 0b, 0B + if !fracOk { + b = 8 + } + switch ch { + case 'x', 'X': + b = 16 + case 'b', 'B': + b = 2 + } + switch b { + case 16, 2: + count = 0 // prefix is not counted + if ch, err = r.ReadByte(); err != nil { + // io.EOF is also an error in this case + return + } + case 8: + count = 0 // prefix is not counted + } + case io.EOF: + // input is "0" + res = z[:0] + err = nil + return + default: + // read error + return + } + } + } + + // convert string + // Algorithm: Collect digits in groups of at most n digits in di + // and then use mulAddWW for every such group to add them to the + // result. + z = z[:0] + b1 := Word(b) + bn, n := maxPow(b1) // at most n digits in base b1 fit into Word + di := Word(0) // 0 <= di < b1**i < bn + i := 0 // 0 <= i < n + dp := -1 // position of decimal point + for { + if fracOk && ch == '.' { + fracOk = false + dp = count + // advance + if ch, err = r.ReadByte(); err != nil { + if err == io.EOF { + err = nil + break + } + return + } + } + + // convert rune into digit value d1 + var d1 Word + switch { + case '0' <= ch && ch <= '9': + d1 = Word(ch - '0') + case 'a' <= ch && ch <= 'z': + d1 = Word(ch - 'a' + 10) + case 'A' <= ch && ch <= 'Z': + d1 = Word(ch - 'A' + 10) + default: + d1 = MaxBase + 1 + } + if d1 >= b1 { + r.UnreadByte() // ch does not belong to number anymore + break + } + count++ + + // collect d1 in di + di = di*b1 + d1 + i++ + + // if di is "full", add it to the result + if i == n { + z = z.mulAddWW(z, bn, di) + di = 0 + i = 0 + } + + // advance + if ch, err = r.ReadByte(); err != nil { + if err == io.EOF { + err = nil + break + } + return + } + } + + if count == 0 { + // no digits found + switch { + case base == 0 && b == 8: + // there was only the octal prefix 0 (possibly followed by digits > 7); + // count as one digit and return base 10, not 8 + count = 1 + b = 10 + case base != 0 || b != 8: + // there was neither a mantissa digit nor the octal prefix 0 + err = errors.New("syntax error scanning number") + } + return + } + // count > 0 + + // add remaining digits to result + if i > 0 { + z = z.mulAddWW(z, pow(b1, i), di) + } + res = z.norm() + + // adjust for fraction, if any + if dp >= 0 { + // 0 <= dp <= count > 0 + count = dp - count + } + + return +} + +// Character sets for string conversion. +const ( + lowercaseDigits = "0123456789abcdefghijklmnopqrstuvwxyz" + uppercaseDigits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" +) + +// decimalString returns a decimal representation of x. +// It calls x.string with the charset "0123456789". +func (x nat) decimalString() string { + return x.string(lowercaseDigits[:10]) +} + +// hexString returns a hexadecimal representation of x. +// It calls x.string with the charset "0123456789abcdef". +func (x nat) hexString() string { + return x.string(lowercaseDigits[:16]) +} + +// string converts x to a string using digits from a charset; a digit with +// value d is represented by charset[d]. The conversion base is determined +// by len(charset), which must be >= 2 and <= 256. +func (x nat) string(charset string) string { + b := Word(len(charset)) + + // special cases + switch { + case b < 2 || b > 256: + panic("invalid character set length") + case len(x) == 0: + return string(charset[0]) + } + + // allocate buffer for conversion + i := int(float64(x.bitLen())/math.Log2(float64(b))) + 1 // off by one at most + s := make([]byte, i) + + // convert power of two and non power of two bases separately + if b == b&-b { + // shift is base-b digit size in bits + shift := trailingZeroBits(b) // shift > 0 because b >= 2 + mask := Word(1)<= shift { + i-- + s[i] = charset[w&mask] + w >>= shift + nbits -= shift + } + + // convert any partial leading digit and advance to next word + if nbits == 0 { + // no partial digit remaining, just advance + w = x[k] + nbits = _W + } else { + // partial digit in current (k-1) and next (k) word + w |= x[k] << nbits + i-- + s[i] = charset[w&mask] + + // advance + w = x[k] >> (shift - nbits) + nbits = _W - (shift - nbits) + } + } + + // convert digits of most-significant word (omit leading zeros) + for nbits >= 0 && w != 0 { + i-- + s[i] = charset[w&mask] + w >>= shift + nbits -= shift + } + + } else { + bb, ndigits := maxPow(Word(b)) + + // construct table of successive squares of bb*leafSize to use in subdivisions + // result (table != nil) <=> (len(x) > leafSize > 0) + table := divisors(len(x), b, ndigits, bb) + + // preserve x, create local copy for use by convertWords + q := nat(nil).set(x) + + // convert q to string s in base b + q.convertWords(s, charset, b, ndigits, bb, table) + + // strip leading zeros + // (x != 0; thus s must contain at least one non-zero digit + // and the loop will terminate) + i = 0 + for zero := charset[0]; s[i] == zero; { + i++ + } + } + + return string(s[i:]) +} + +// Convert words of q to base b digits in s. If q is large, it is recursively "split in half" +// by nat/nat division using tabulated divisors. Otherwise, it is converted iteratively using +// repeated nat/Word division. +// +// The iterative method processes n Words by n divW() calls, each of which visits every Word in the +// incrementally shortened q for a total of n + (n-1) + (n-2) ... + 2 + 1, or n(n+1)/2 divW()'s. +// Recursive conversion divides q by its approximate square root, yielding two parts, each half +// the size of q. Using the iterative method on both halves means 2 * (n/2)(n/2 + 1)/2 divW()'s +// plus the expensive long div(). Asymptotically, the ratio is favorable at 1/2 the divW()'s, and +// is made better by splitting the subblocks recursively. Best is to split blocks until one more +// split would take longer (because of the nat/nat div()) than the twice as many divW()'s of the +// iterative approach. This threshold is represented by leafSize. Benchmarking of leafSize in the +// range 2..64 shows that values of 8 and 16 work well, with a 4x speedup at medium lengths and +// ~30x for 20000 digits. Use nat_test.go's BenchmarkLeafSize tests to optimize leafSize for +// specific hardware. +// +func (q nat) convertWords(s []byte, charset string, b Word, ndigits int, bb Word, table []divisor) { + // split larger blocks recursively + if table != nil { + // len(q) > leafSize > 0 + var r nat + index := len(table) - 1 + for len(q) > leafSize { + // find divisor close to sqrt(q) if possible, but in any case < q + maxLength := q.bitLen() // ~= log2 q, or at of least largest possible q of this bit length + minLength := maxLength >> 1 // ~= log2 sqrt(q) + for index > 0 && table[index-1].nbits > minLength { + index-- // desired + } + if table[index].nbits >= maxLength && table[index].bbb.cmp(q) >= 0 { + index-- + if index < 0 { + panic("internal inconsistency") + } + } + + // split q into the two digit number (q'*bbb + r) to form independent subblocks + q, r = q.div(r, q, table[index].bbb) + + // convert subblocks and collect results in s[:h] and s[h:] + h := len(s) - table[index].ndigits + r.convertWords(s[h:], charset, b, ndigits, bb, table[0:index]) + s = s[:h] // == q.convertWords(s, charset, b, ndigits, bb, table[0:index+1]) + } + } + + // having split any large blocks now process the remaining (small) block iteratively + i := len(s) + var r Word + if b == 10 { + // hard-coding for 10 here speeds this up by 1.25x (allows for / and % by constants) + for len(q) > 0 { + // extract least significant, base bb "digit" + q, r = q.divW(q, bb) + for j := 0; j < ndigits && i > 0; j++ { + i-- + // avoid % computation since r%10 == r - int(r/10)*10; + // this appears to be faster for BenchmarkString10000Base10 + // and smaller strings (but a bit slower for larger ones) + t := r / 10 + s[i] = charset[r-t<<3-t-t] // TODO(gri) replace w/ t*10 once compiler produces better code + r = t + } + } + } else { + for len(q) > 0 { + // extract least significant, base bb "digit" + q, r = q.divW(q, bb) + for j := 0; j < ndigits && i > 0; j++ { + i-- + s[i] = charset[r%b] + r /= b + } + } + } + + // prepend high-order zeroes + zero := charset[0] + for i > 0 { // while need more leading zeroes + i-- + s[i] = zero + } +} + +// Split blocks greater than leafSize Words (or set to 0 to disable recursive conversion) +// Benchmark and configure leafSize using: go test -bench="Leaf" +// 8 and 16 effective on 3.0 GHz Xeon "Clovertown" CPU (128 byte cache lines) +// 8 and 16 effective on 2.66 GHz Core 2 Duo "Penryn" CPU +var leafSize int = 8 // number of Word-size binary values treat as a monolithic block + +type divisor struct { + bbb nat // divisor + nbits int // bit length of divisor (discounting leading zeroes) ~= log2(bbb) + ndigits int // digit length of divisor in terms of output base digits +} + +var cacheBase10 struct { + sync.Mutex + table [64]divisor // cached divisors for base 10 +} + +// expWW computes x**y +func (z nat) expWW(x, y Word) nat { + return z.expNN(nat(nil).setWord(x), nat(nil).setWord(y), nil) +} + +// construct table of powers of bb*leafSize to use in subdivisions +func divisors(m int, b Word, ndigits int, bb Word) []divisor { + // only compute table when recursive conversion is enabled and x is large + if leafSize == 0 || m <= leafSize { + return nil + } + + // determine k where (bb**leafSize)**(2**k) >= sqrt(x) + k := 1 + for words := leafSize; words < m>>1 && k < len(cacheBase10.table); words <<= 1 { + k++ + } + + // reuse and extend existing table of divisors or create new table as appropriate + var table []divisor // for b == 10, table overlaps with cacheBase10.table + if b == 10 { + cacheBase10.Lock() + table = cacheBase10.table[0:k] // reuse old table for this conversion + } else { + table = make([]divisor, k) // create new table for this conversion + } + + // extend table + if table[k-1].ndigits == 0 { + // add new entries as needed + var larger nat + for i := 0; i < k; i++ { + if table[i].ndigits == 0 { + if i == 0 { + table[0].bbb = nat(nil).expWW(bb, Word(leafSize)) + table[0].ndigits = ndigits * leafSize + } else { + table[i].bbb = nat(nil).mul(table[i-1].bbb, table[i-1].bbb) + table[i].ndigits = 2 * table[i-1].ndigits + } + + // optimization: exploit aggregated extra bits in macro blocks + larger = nat(nil).set(table[i].bbb) + for mulAddVWW(larger, larger, b, 0) == 0 { + table[i].bbb = table[i].bbb.set(larger) + table[i].ndigits++ + } + + table[i].nbits = table[i].bbb.bitLen() + } + } + } + + if b == 10 { + cacheBase10.Unlock() + } + + return table +} diff --git a/src/cmd/compile/internal/big/natconv_test.go b/src/cmd/compile/internal/big/natconv_test.go new file mode 100644 index 0000000000..f321fbc2df --- /dev/null +++ b/src/cmd/compile/internal/big/natconv_test.go @@ -0,0 +1,425 @@ +// 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 big + +import ( + "io" + "strings" + "testing" +) + +func toString(x nat, charset string) string { + base := len(charset) + + // special cases + switch { + case base < 2: + panic("illegal base") + case len(x) == 0: + return string(charset[0]) + } + + // allocate buffer for conversion + i := x.bitLen()/log2(Word(base)) + 1 // +1: round up + s := make([]byte, i) + + // don't destroy x + q := nat(nil).set(x) + + // convert + for len(q) > 0 { + i-- + var r Word + q, r = q.divW(q, Word(base)) + s[i] = charset[r] + } + + return string(s[i:]) +} + +var strTests = []struct { + x nat // nat value to be converted + c string // conversion charset + s string // expected result +}{ + {nil, "01", "0"}, + {nat{1}, "01", "1"}, + {nat{0xc5}, "01", "11000101"}, + {nat{03271}, lowercaseDigits[:8], "3271"}, + {nat{10}, lowercaseDigits[:10], "10"}, + {nat{1234567890}, uppercaseDigits[:10], "1234567890"}, + {nat{0xdeadbeef}, lowercaseDigits[:16], "deadbeef"}, + {nat{0xdeadbeef}, uppercaseDigits[:16], "DEADBEEF"}, + {nat{0x229be7}, lowercaseDigits[:17], "1a2b3c"}, + {nat{0x309663e6}, uppercaseDigits[:32], "O9COV6"}, +} + +func TestString(t *testing.T) { + // test invalid character set explicitly + var panicStr string + func() { + defer func() { + panicStr = recover().(string) + }() + natOne.string("0") + }() + if panicStr != "invalid character set length" { + t.Errorf("expected panic for invalid character set") + } + + for _, a := range strTests { + s := a.x.string(a.c) + if s != a.s { + t.Errorf("string%+v\n\tgot s = %s; want %s", a, s, a.s) + } + + x, b, _, err := nat(nil).scan(strings.NewReader(a.s), len(a.c), false) + if x.cmp(a.x) != 0 { + t.Errorf("scan%+v\n\tgot z = %v; want %v", a, x, a.x) + } + if b != len(a.c) { + t.Errorf("scan%+v\n\tgot b = %d; want %d", a, b, len(a.c)) + } + if err != nil { + t.Errorf("scan%+v\n\tgot error = %s", a, err) + } + } +} + +var natScanTests = []struct { + s string // string to be scanned + base int // input base + frac bool // fraction ok + x nat // expected nat + b int // expected base + count int // expected digit count + ok bool // expected success + next rune // next character (or 0, if at EOF) +}{ + // error: no mantissa + {}, + {s: "?"}, + {base: 10}, + {base: 36}, + {s: "?", base: 10}, + {s: "0x"}, + {s: "345", base: 2}, + + // error: incorrect use of decimal point + {s: ".0"}, + {s: ".0", base: 10}, + {s: ".", base: 0}, + {s: "0x.0"}, + + // no errors + {"0", 0, false, nil, 10, 1, true, 0}, + {"0", 10, false, nil, 10, 1, true, 0}, + {"0", 36, false, nil, 36, 1, true, 0}, + {"1", 0, false, nat{1}, 10, 1, true, 0}, + {"1", 10, false, nat{1}, 10, 1, true, 0}, + {"0 ", 0, false, nil, 10, 1, true, ' '}, + {"08", 0, false, nil, 10, 1, true, '8'}, + {"08", 10, false, nat{8}, 10, 2, true, 0}, + {"018", 0, false, nat{1}, 8, 1, true, '8'}, + {"0b1", 0, false, nat{1}, 2, 1, true, 0}, + {"0b11000101", 0, false, nat{0xc5}, 2, 8, true, 0}, + {"03271", 0, false, nat{03271}, 8, 4, true, 0}, + {"10ab", 0, false, nat{10}, 10, 2, true, 'a'}, + {"1234567890", 0, false, nat{1234567890}, 10, 10, true, 0}, + {"xyz", 36, false, nat{(33*36+34)*36 + 35}, 36, 3, true, 0}, + {"xyz?", 36, false, nat{(33*36+34)*36 + 35}, 36, 3, true, '?'}, + {"0x", 16, false, nil, 16, 1, true, 'x'}, + {"0xdeadbeef", 0, false, nat{0xdeadbeef}, 16, 8, true, 0}, + {"0XDEADBEEF", 0, false, nat{0xdeadbeef}, 16, 8, true, 0}, + + // no errors, decimal point + {"0.", 0, false, nil, 10, 1, true, '.'}, + {"0.", 10, true, nil, 10, 0, true, 0}, + {"0.1.2", 10, true, nat{1}, 10, -1, true, '.'}, + {".000", 10, true, nil, 10, -3, true, 0}, + {"12.3", 10, true, nat{123}, 10, -1, true, 0}, + {"012.345", 10, true, nat{12345}, 10, -3, true, 0}, +} + +func TestScanBase(t *testing.T) { + for _, a := range natScanTests { + r := strings.NewReader(a.s) + x, b, count, err := nat(nil).scan(r, a.base, a.frac) + if err == nil && !a.ok { + t.Errorf("scan%+v\n\texpected error", a) + } + if err != nil { + if a.ok { + t.Errorf("scan%+v\n\tgot error = %s", a, err) + } + continue + } + if x.cmp(a.x) != 0 { + t.Errorf("scan%+v\n\tgot z = %v; want %v", a, x, a.x) + } + if b != a.b { + t.Errorf("scan%+v\n\tgot b = %d; want %d", a, b, a.base) + } + if count != a.count { + t.Errorf("scan%+v\n\tgot count = %d; want %d", a, count, a.count) + } + next, _, err := r.ReadRune() + if err == io.EOF { + next = 0 + err = nil + } + if err == nil && next != a.next { + t.Errorf("scan%+v\n\tgot next = %q; want %q", a, next, a.next) + } + } +} + +var pi = "3" + + "14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651" + + "32823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461" + + "28475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920" + + "96282925409171536436789259036001133053054882046652138414695194151160943305727036575959195309218611738193261179" + + "31051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798" + + "60943702770539217176293176752384674818467669405132000568127145263560827785771342757789609173637178721468440901" + + "22495343014654958537105079227968925892354201995611212902196086403441815981362977477130996051870721134999999837" + + "29780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083" + + "81420617177669147303598253490428755468731159562863882353787593751957781857780532171226806613001927876611195909" + + "21642019893809525720106548586327886593615338182796823030195203530185296899577362259941389124972177528347913151" + + "55748572424541506959508295331168617278558890750983817546374649393192550604009277016711390098488240128583616035" + + "63707660104710181942955596198946767837449448255379774726847104047534646208046684259069491293313677028989152104" + + "75216205696602405803815019351125338243003558764024749647326391419927260426992279678235478163600934172164121992" + + "45863150302861829745557067498385054945885869269956909272107975093029553211653449872027559602364806654991198818" + + "34797753566369807426542527862551818417574672890977772793800081647060016145249192173217214772350141441973568548" + + "16136115735255213347574184946843852332390739414333454776241686251898356948556209921922218427255025425688767179" + + "04946016534668049886272327917860857843838279679766814541009538837863609506800642251252051173929848960841284886" + + "26945604241965285022210661186306744278622039194945047123713786960956364371917287467764657573962413890865832645" + + "99581339047802759009946576407895126946839835259570982582262052248940772671947826848260147699090264013639443745" + + "53050682034962524517493996514314298091906592509372216964615157098583874105978859597729754989301617539284681382" + + "68683868942774155991855925245953959431049972524680845987273644695848653836736222626099124608051243884390451244" + + "13654976278079771569143599770012961608944169486855584840635342207222582848864815845602850601684273945226746767" + + "88952521385225499546667278239864565961163548862305774564980355936345681743241125150760694794510965960940252288" + + "79710893145669136867228748940560101503308617928680920874760917824938589009714909675985261365549781893129784821" + + "68299894872265880485756401427047755513237964145152374623436454285844479526586782105114135473573952311342716610" + + "21359695362314429524849371871101457654035902799344037420073105785390621983874478084784896833214457138687519435" + + "06430218453191048481005370614680674919278191197939952061419663428754440643745123718192179998391015919561814675" + + "14269123974894090718649423196156794520809514655022523160388193014209376213785595663893778708303906979207734672" + + "21825625996615014215030680384477345492026054146659252014974428507325186660021324340881907104863317346496514539" + + "05796268561005508106658796998163574736384052571459102897064140110971206280439039759515677157700420337869936007" + + "23055876317635942187312514712053292819182618612586732157919841484882916447060957527069572209175671167229109816" + + "90915280173506712748583222871835209353965725121083579151369882091444210067510334671103141267111369908658516398" + + "31501970165151168517143765761835155650884909989859982387345528331635507647918535893226185489632132933089857064" + + "20467525907091548141654985946163718027098199430992448895757128289059232332609729971208443357326548938239119325" + + "97463667305836041428138830320382490375898524374417029132765618093773444030707469211201913020330380197621101100" + + "44929321516084244485963766983895228684783123552658213144957685726243344189303968642624341077322697802807318915" + + "44110104468232527162010526522721116603966655730925471105578537634668206531098965269186205647693125705863566201" + + "85581007293606598764861179104533488503461136576867532494416680396265797877185560845529654126654085306143444318" + + "58676975145661406800700237877659134401712749470420562230538994561314071127000407854733269939081454664645880797" + + "27082668306343285878569830523580893306575740679545716377525420211495576158140025012622859413021647155097925923" + + "09907965473761255176567513575178296664547791745011299614890304639947132962107340437518957359614589019389713111" + + "79042978285647503203198691514028708085990480109412147221317947647772622414254854540332157185306142288137585043" + + "06332175182979866223717215916077166925474873898665494945011465406284336639379003976926567214638530673609657120" + + "91807638327166416274888800786925602902284721040317211860820419000422966171196377921337575114959501566049631862" + + "94726547364252308177036751590673502350728354056704038674351362222477158915049530984448933309634087807693259939" + + "78054193414473774418426312986080998886874132604721569516239658645730216315981931951673538129741677294786724229" + + "24654366800980676928238280689964004824354037014163149658979409243237896907069779422362508221688957383798623001" + + "59377647165122893578601588161755782973523344604281512627203734314653197777416031990665541876397929334419521541" + + "34189948544473456738316249934191318148092777710386387734317720754565453220777092120190516609628049092636019759" + + "88281613323166636528619326686336062735676303544776280350450777235547105859548702790814356240145171806246436267" + + "94561275318134078330336254232783944975382437205835311477119926063813346776879695970309833913077109870408591337" + +// Test case for BenchmarkScanPi. +func TestScanPi(t *testing.T) { + var x nat + z, _, _, err := x.scan(strings.NewReader(pi), 10, false) + if err != nil { + t.Errorf("scanning pi: %s", err) + } + if s := z.decimalString(); s != pi { + t.Errorf("scanning pi: got %s", s) + } +} + +func TestScanPiParallel(t *testing.T) { + const n = 2 + c := make(chan int) + for i := 0; i < n; i++ { + go func() { + TestScanPi(t) + c <- 0 + }() + } + for i := 0; i < n; i++ { + <-c + } +} + +func BenchmarkScanPi(b *testing.B) { + for i := 0; i < b.N; i++ { + var x nat + x.scan(strings.NewReader(pi), 10, false) + } +} + +func BenchmarkStringPiParallel(b *testing.B) { + var x nat + x, _, _, _ = x.scan(strings.NewReader(pi), 0, false) + if x.decimalString() != pi { + panic("benchmark incorrect: conversion failed") + } + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + x.decimalString() + } + }) +} + +func BenchmarkScan10Base2(b *testing.B) { ScanHelper(b, 2, 10, 10) } +func BenchmarkScan100Base2(b *testing.B) { ScanHelper(b, 2, 10, 100) } +func BenchmarkScan1000Base2(b *testing.B) { ScanHelper(b, 2, 10, 1000) } +func BenchmarkScan10000Base2(b *testing.B) { ScanHelper(b, 2, 10, 10000) } +func BenchmarkScan100000Base2(b *testing.B) { ScanHelper(b, 2, 10, 100000) } + +func BenchmarkScan10Base8(b *testing.B) { ScanHelper(b, 8, 10, 10) } +func BenchmarkScan100Base8(b *testing.B) { ScanHelper(b, 8, 10, 100) } +func BenchmarkScan1000Base8(b *testing.B) { ScanHelper(b, 8, 10, 1000) } +func BenchmarkScan10000Base8(b *testing.B) { ScanHelper(b, 8, 10, 10000) } +func BenchmarkScan100000Base8(b *testing.B) { ScanHelper(b, 8, 10, 100000) } + +func BenchmarkScan10Base10(b *testing.B) { ScanHelper(b, 10, 10, 10) } +func BenchmarkScan100Base10(b *testing.B) { ScanHelper(b, 10, 10, 100) } +func BenchmarkScan1000Base10(b *testing.B) { ScanHelper(b, 10, 10, 1000) } +func BenchmarkScan10000Base10(b *testing.B) { ScanHelper(b, 10, 10, 10000) } +func BenchmarkScan100000Base10(b *testing.B) { ScanHelper(b, 10, 10, 100000) } + +func BenchmarkScan10Base16(b *testing.B) { ScanHelper(b, 16, 10, 10) } +func BenchmarkScan100Base16(b *testing.B) { ScanHelper(b, 16, 10, 100) } +func BenchmarkScan1000Base16(b *testing.B) { ScanHelper(b, 16, 10, 1000) } +func BenchmarkScan10000Base16(b *testing.B) { ScanHelper(b, 16, 10, 10000) } +func BenchmarkScan100000Base16(b *testing.B) { ScanHelper(b, 16, 10, 100000) } + +func ScanHelper(b *testing.B, base int, x, y Word) { + b.StopTimer() + var z nat + z = z.expWW(x, y) + + var s string + s = z.string(lowercaseDigits[:base]) + if t := toString(z, lowercaseDigits[:base]); t != s { + b.Fatalf("scanning: got %s; want %s", s, t) + } + b.StartTimer() + + for i := 0; i < b.N; i++ { + z.scan(strings.NewReader(s), base, false) + } +} + +func BenchmarkString10Base2(b *testing.B) { StringHelper(b, 2, 10, 10) } +func BenchmarkString100Base2(b *testing.B) { StringHelper(b, 2, 10, 100) } +func BenchmarkString1000Base2(b *testing.B) { StringHelper(b, 2, 10, 1000) } +func BenchmarkString10000Base2(b *testing.B) { StringHelper(b, 2, 10, 10000) } +func BenchmarkString100000Base2(b *testing.B) { StringHelper(b, 2, 10, 100000) } + +func BenchmarkString10Base8(b *testing.B) { StringHelper(b, 8, 10, 10) } +func BenchmarkString100Base8(b *testing.B) { StringHelper(b, 8, 10, 100) } +func BenchmarkString1000Base8(b *testing.B) { StringHelper(b, 8, 10, 1000) } +func BenchmarkString10000Base8(b *testing.B) { StringHelper(b, 8, 10, 10000) } +func BenchmarkString100000Base8(b *testing.B) { StringHelper(b, 8, 10, 100000) } + +func BenchmarkString10Base10(b *testing.B) { StringHelper(b, 10, 10, 10) } +func BenchmarkString100Base10(b *testing.B) { StringHelper(b, 10, 10, 100) } +func BenchmarkString1000Base10(b *testing.B) { StringHelper(b, 10, 10, 1000) } +func BenchmarkString10000Base10(b *testing.B) { StringHelper(b, 10, 10, 10000) } +func BenchmarkString100000Base10(b *testing.B) { StringHelper(b, 10, 10, 100000) } + +func BenchmarkString10Base16(b *testing.B) { StringHelper(b, 16, 10, 10) } +func BenchmarkString100Base16(b *testing.B) { StringHelper(b, 16, 10, 100) } +func BenchmarkString1000Base16(b *testing.B) { StringHelper(b, 16, 10, 1000) } +func BenchmarkString10000Base16(b *testing.B) { StringHelper(b, 16, 10, 10000) } +func BenchmarkString100000Base16(b *testing.B) { StringHelper(b, 16, 10, 100000) } + +func StringHelper(b *testing.B, base int, x, y Word) { + b.StopTimer() + var z nat + z = z.expWW(x, y) + z.string(lowercaseDigits[:base]) // warm divisor cache + b.StartTimer() + + for i := 0; i < b.N; i++ { + _ = z.string(lowercaseDigits[:base]) + } +} + +func BenchmarkLeafSize0(b *testing.B) { LeafSizeHelper(b, 10, 0) } // test without splitting +func BenchmarkLeafSize1(b *testing.B) { LeafSizeHelper(b, 10, 1) } +func BenchmarkLeafSize2(b *testing.B) { LeafSizeHelper(b, 10, 2) } +func BenchmarkLeafSize3(b *testing.B) { LeafSizeHelper(b, 10, 3) } +func BenchmarkLeafSize4(b *testing.B) { LeafSizeHelper(b, 10, 4) } +func BenchmarkLeafSize5(b *testing.B) { LeafSizeHelper(b, 10, 5) } +func BenchmarkLeafSize6(b *testing.B) { LeafSizeHelper(b, 10, 6) } +func BenchmarkLeafSize7(b *testing.B) { LeafSizeHelper(b, 10, 7) } +func BenchmarkLeafSize8(b *testing.B) { LeafSizeHelper(b, 10, 8) } +func BenchmarkLeafSize9(b *testing.B) { LeafSizeHelper(b, 10, 9) } +func BenchmarkLeafSize10(b *testing.B) { LeafSizeHelper(b, 10, 10) } +func BenchmarkLeafSize11(b *testing.B) { LeafSizeHelper(b, 10, 11) } +func BenchmarkLeafSize12(b *testing.B) { LeafSizeHelper(b, 10, 12) } +func BenchmarkLeafSize13(b *testing.B) { LeafSizeHelper(b, 10, 13) } +func BenchmarkLeafSize14(b *testing.B) { LeafSizeHelper(b, 10, 14) } +func BenchmarkLeafSize15(b *testing.B) { LeafSizeHelper(b, 10, 15) } +func BenchmarkLeafSize16(b *testing.B) { LeafSizeHelper(b, 10, 16) } +func BenchmarkLeafSize32(b *testing.B) { LeafSizeHelper(b, 10, 32) } // try some large lengths +func BenchmarkLeafSize64(b *testing.B) { LeafSizeHelper(b, 10, 64) } + +func LeafSizeHelper(b *testing.B, base Word, size int) { + b.StopTimer() + originalLeafSize := leafSize + resetTable(cacheBase10.table[:]) + leafSize = size + b.StartTimer() + + for d := 1; d <= 10000; d *= 10 { + b.StopTimer() + var z nat + z = z.expWW(base, Word(d)) // build target number + _ = z.string(lowercaseDigits[:base]) // warm divisor cache + b.StartTimer() + + for i := 0; i < b.N; i++ { + _ = z.string(lowercaseDigits[:base]) + } + } + + b.StopTimer() + resetTable(cacheBase10.table[:]) + leafSize = originalLeafSize + b.StartTimer() +} + +func resetTable(table []divisor) { + if table != nil && table[0].bbb != nil { + for i := 0; i < len(table); i++ { + table[i].bbb = nil + table[i].nbits = 0 + table[i].ndigits = 0 + } + } +} + +func TestStringPowers(t *testing.T) { + var b, p Word + for b = 2; b <= 16; b++ { + for p = 0; p <= 512; p++ { + x := nat(nil).expWW(b, p) + xs := x.string(lowercaseDigits[:b]) + xs2 := toString(x, lowercaseDigits[:b]) + if xs != xs2 { + t.Errorf("failed at %d ** %d in base %d: %s != %s", b, p, b, xs, xs2) + } + } + if b >= 3 && testing.Short() { + break + } + } +} diff --git a/src/cmd/compile/internal/big/rat.go b/src/cmd/compile/internal/big/rat.go new file mode 100644 index 0000000000..fb16f18a96 --- /dev/null +++ b/src/cmd/compile/internal/big/rat.go @@ -0,0 +1,570 @@ +// 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. + +// This file implements multi-precision rational numbers. + +package big + +import ( + "encoding/binary" + "errors" + "fmt" + "math" +) + +// A Rat represents a quotient a/b of arbitrary precision. +// The zero value for a Rat represents the value 0. +type Rat struct { + // To make zero values for Rat work w/o initialization, + // a zero value of b (len(b) == 0) acts like b == 1. + // a.neg determines the sign of the Rat, b.neg is ignored. + a, b Int +} + +// NewRat creates a new Rat with numerator a and denominator b. +func NewRat(a, b int64) *Rat { + return new(Rat).SetFrac64(a, b) +} + +// SetFloat64 sets z to exactly f and returns z. +// If f is not finite, SetFloat returns nil. +func (z *Rat) SetFloat64(f float64) *Rat { + const expMask = 1<<11 - 1 + bits := math.Float64bits(f) + mantissa := bits & (1<<52 - 1) + exp := int((bits >> 52) & expMask) + switch exp { + case expMask: // non-finite + return nil + case 0: // denormal + exp -= 1022 + default: // normal + mantissa |= 1 << 52 + exp -= 1023 + } + + shift := 52 - exp + + // Optimization (?): partially pre-normalise. + for mantissa&1 == 0 && shift > 0 { + mantissa >>= 1 + shift-- + } + + z.a.SetUint64(mantissa) + z.a.neg = f < 0 + z.b.Set(intOne) + if shift > 0 { + z.b.Lsh(&z.b, uint(shift)) + } else { + z.a.Lsh(&z.a, uint(-shift)) + } + return z.norm() +} + +// quotToFloat32 returns the non-negative float32 value +// nearest to the quotient a/b, using round-to-even in +// halfway cases. It does not mutate its arguments. +// Preconditions: b is non-zero; a and b have no common factors. +func quotToFloat32(a, b nat) (f float32, exact bool) { + const ( + // float size in bits + Fsize = 32 + + // mantissa + Msize = 23 + Msize1 = Msize + 1 // incl. implicit 1 + Msize2 = Msize1 + 1 + + // exponent + Esize = Fsize - Msize1 + Ebias = 1<<(Esize-1) - 1 + Emin = 1 - Ebias + Emax = Ebias + ) + + // TODO(adonovan): specialize common degenerate cases: 1.0, integers. + alen := a.bitLen() + if alen == 0 { + return 0, true + } + blen := b.bitLen() + if blen == 0 { + panic("division by zero") + } + + // 1. Left-shift A or B such that quotient A/B is in [1<= B). + // This is 2 or 3 more than the float32 mantissa field width of Msize: + // - the optional extra bit is shifted away in step 3 below. + // - the high-order 1 is omitted in "normal" representation; + // - the low-order 1 will be used during rounding then discarded. + exp := alen - blen + var a2, b2 nat + a2 = a2.set(a) + b2 = b2.set(b) + if shift := Msize2 - exp; shift > 0 { + a2 = a2.shl(a2, uint(shift)) + } else if shift < 0 { + b2 = b2.shl(b2, uint(-shift)) + } + + // 2. Compute quotient and remainder (q, r). NB: due to the + // extra shift, the low-order bit of q is logically the + // high-order bit of r. + var q nat + q, r := q.div(a2, a2, b2) // (recycle a2) + mantissa := low32(q) + haveRem := len(r) > 0 // mantissa&1 && !haveRem => remainder is exactly half + + // 3. If quotient didn't fit in Msize2 bits, redo division by b2<<1 + // (in effect---we accomplish this incrementally). + if mantissa>>Msize2 == 1 { + if mantissa&1 == 1 { + haveRem = true + } + mantissa >>= 1 + exp++ + } + if mantissa>>Msize1 != 1 { + panic(fmt.Sprintf("expected exactly %d bits of result", Msize2)) + } + + // 4. Rounding. + if Emin-Msize <= exp && exp <= Emin { + // Denormal case; lose 'shift' bits of precision. + shift := uint(Emin - (exp - 1)) // [1..Esize1) + lostbits := mantissa & (1<>= shift + exp = 2 - Ebias // == exp + shift + } + // Round q using round-half-to-even. + exact = !haveRem + if mantissa&1 != 0 { + exact = false + if haveRem || mantissa&2 != 0 { + if mantissa++; mantissa >= 1< 100...0, so shift is safe + mantissa >>= 1 + exp++ + } + } + } + mantissa >>= 1 // discard rounding bit. Mantissa now scaled by 1<= B). + // This is 2 or 3 more than the float64 mantissa field width of Msize: + // - the optional extra bit is shifted away in step 3 below. + // - the high-order 1 is omitted in "normal" representation; + // - the low-order 1 will be used during rounding then discarded. + exp := alen - blen + var a2, b2 nat + a2 = a2.set(a) + b2 = b2.set(b) + if shift := Msize2 - exp; shift > 0 { + a2 = a2.shl(a2, uint(shift)) + } else if shift < 0 { + b2 = b2.shl(b2, uint(-shift)) + } + + // 2. Compute quotient and remainder (q, r). NB: due to the + // extra shift, the low-order bit of q is logically the + // high-order bit of r. + var q nat + q, r := q.div(a2, a2, b2) // (recycle a2) + mantissa := low64(q) + haveRem := len(r) > 0 // mantissa&1 && !haveRem => remainder is exactly half + + // 3. If quotient didn't fit in Msize2 bits, redo division by b2<<1 + // (in effect---we accomplish this incrementally). + if mantissa>>Msize2 == 1 { + if mantissa&1 == 1 { + haveRem = true + } + mantissa >>= 1 + exp++ + } + if mantissa>>Msize1 != 1 { + panic(fmt.Sprintf("expected exactly %d bits of result", Msize2)) + } + + // 4. Rounding. + if Emin-Msize <= exp && exp <= Emin { + // Denormal case; lose 'shift' bits of precision. + shift := uint(Emin - (exp - 1)) // [1..Esize1) + lostbits := mantissa & (1<>= shift + exp = 2 - Ebias // == exp + shift + } + // Round q using round-half-to-even. + exact = !haveRem + if mantissa&1 != 0 { + exact = false + if haveRem || mantissa&2 != 0 { + if mantissa++; mantissa >= 1< 100...0, so shift is safe + mantissa >>= 1 + exp++ + } + } + } + mantissa >>= 1 // discard rounding bit. Mantissa now scaled by 1< 0 && !z.a.neg // 0 has no sign + return z +} + +// Inv sets z to 1/x and returns z. +func (z *Rat) Inv(x *Rat) *Rat { + if len(x.a.abs) == 0 { + panic("division by zero") + } + z.Set(x) + a := z.b.abs + if len(a) == 0 { + a = a.set(natOne) // materialize numerator + } + b := z.a.abs + if b.cmp(natOne) == 0 { + b = b[:0] // normalize denominator + } + z.a.abs, z.b.abs = a, b // sign doesn't change + return z +} + +// Sign returns: +// +// -1 if x < 0 +// 0 if x == 0 +// +1 if x > 0 +// +func (x *Rat) Sign() int { + return x.a.Sign() +} + +// IsInt reports whether the denominator of x is 1. +func (x *Rat) IsInt() bool { + return len(x.b.abs) == 0 || x.b.abs.cmp(natOne) == 0 +} + +// Num returns the numerator of x; it may be <= 0. +// The result is a reference to x's numerator; it +// may change if a new value is assigned to x, and vice versa. +// The sign of the numerator corresponds to the sign of x. +func (x *Rat) Num() *Int { + return &x.a +} + +// Denom returns the denominator of x; it is always > 0. +// The result is a reference to x's denominator; it +// may change if a new value is assigned to x, and vice versa. +func (x *Rat) Denom() *Int { + x.b.neg = false // the result is always >= 0 + if len(x.b.abs) == 0 { + x.b.abs = x.b.abs.set(natOne) // materialize denominator + } + return &x.b +} + +func (z *Rat) norm() *Rat { + switch { + case len(z.a.abs) == 0: + // z == 0 - normalize sign and denominator + z.a.neg = false + z.b.abs = z.b.abs[:0] + case len(z.b.abs) == 0: + // z is normalized int - nothing to do + case z.b.abs.cmp(natOne) == 0: + // z is int - normalize denominator + z.b.abs = z.b.abs[:0] + default: + neg := z.a.neg + z.a.neg = false + z.b.neg = false + if f := NewInt(0).binaryGCD(&z.a, &z.b); f.Cmp(intOne) != 0 { + z.a.abs, _ = z.a.abs.div(nil, z.a.abs, f.abs) + z.b.abs, _ = z.b.abs.div(nil, z.b.abs, f.abs) + if z.b.abs.cmp(natOne) == 0 { + // z is int - normalize denominator + z.b.abs = z.b.abs[:0] + } + } + z.a.neg = neg + } + return z +} + +// mulDenom sets z to the denominator product x*y (by taking into +// account that 0 values for x or y must be interpreted as 1) and +// returns z. +func mulDenom(z, x, y nat) nat { + switch { + case len(x) == 0: + return z.set(y) + case len(y) == 0: + return z.set(x) + } + return z.mul(x, y) +} + +// scaleDenom computes x*f. +// If f == 0 (zero value of denominator), the result is (a copy of) x. +func scaleDenom(x *Int, f nat) *Int { + var z Int + if len(f) == 0 { + return z.Set(x) + } + z.abs = z.abs.mul(x.abs, f) + z.neg = x.neg + return &z +} + +// Cmp compares x and y and returns: +// +// -1 if x < y +// 0 if x == y +// +1 if x > y +// +func (x *Rat) Cmp(y *Rat) int { + return scaleDenom(&x.a, y.b.abs).Cmp(scaleDenom(&y.a, x.b.abs)) +} + +// Add sets z to the sum x+y and returns z. +func (z *Rat) Add(x, y *Rat) *Rat { + a1 := scaleDenom(&x.a, y.b.abs) + a2 := scaleDenom(&y.a, x.b.abs) + z.a.Add(a1, a2) + z.b.abs = mulDenom(z.b.abs, x.b.abs, y.b.abs) + return z.norm() +} + +// Sub sets z to the difference x-y and returns z. +func (z *Rat) Sub(x, y *Rat) *Rat { + a1 := scaleDenom(&x.a, y.b.abs) + a2 := scaleDenom(&y.a, x.b.abs) + z.a.Sub(a1, a2) + z.b.abs = mulDenom(z.b.abs, x.b.abs, y.b.abs) + return z.norm() +} + +// Mul sets z to the product x*y and returns z. +func (z *Rat) Mul(x, y *Rat) *Rat { + z.a.Mul(&x.a, &y.a) + z.b.abs = mulDenom(z.b.abs, x.b.abs, y.b.abs) + return z.norm() +} + +// Quo sets z to the quotient x/y and returns z. +// If y == 0, a division-by-zero run-time panic occurs. +func (z *Rat) Quo(x, y *Rat) *Rat { + if len(y.a.abs) == 0 { + panic("division by zero") + } + a := scaleDenom(&x.a, y.b.abs) + b := scaleDenom(&y.a, x.b.abs) + z.a.abs = a.abs + z.b.abs = b.abs + z.a.neg = a.neg != b.neg + return z.norm() +} + +// Gob codec version. Permits backward-compatible changes to the encoding. +const ratGobVersion byte = 1 + +// GobEncode implements the gob.GobEncoder interface. +func (x *Rat) GobEncode() ([]byte, error) { + if x == nil { + return nil, nil + } + buf := make([]byte, 1+4+(len(x.a.abs)+len(x.b.abs))*_S) // extra bytes for version and sign bit (1), and numerator length (4) + i := x.b.abs.bytes(buf) + j := x.a.abs.bytes(buf[:i]) + n := i - j + if int(uint32(n)) != n { + // this should never happen + return nil, errors.New("Rat.GobEncode: numerator too large") + } + binary.BigEndian.PutUint32(buf[j-4:j], uint32(n)) + j -= 1 + 4 + b := ratGobVersion << 1 // make space for sign bit + if x.a.neg { + b |= 1 + } + buf[j] = b + return buf[j:], nil +} + +// GobDecode implements the gob.GobDecoder interface. +func (z *Rat) GobDecode(buf []byte) error { + if len(buf) == 0 { + // Other side sent a nil or default value. + *z = Rat{} + return nil + } + b := buf[0] + if b>>1 != ratGobVersion { + return fmt.Errorf("Rat.GobDecode: encoding version %d not supported", b>>1) + } + const j = 1 + 4 + i := j + binary.BigEndian.Uint32(buf[j-4:j]) + z.a.neg = b&1 != 0 + z.a.abs = z.a.abs.setBytes(buf[j:i]) + z.b.abs = z.b.abs.setBytes(buf[i:]) + return nil +} + +// MarshalText implements the encoding.TextMarshaler interface. +func (r *Rat) MarshalText() (text []byte, err error) { + return []byte(r.RatString()), nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +func (r *Rat) UnmarshalText(text []byte) error { + if _, ok := r.SetString(string(text)); !ok { + return fmt.Errorf("math/big: cannot unmarshal %q into a *big.Rat", text) + } + return nil +} diff --git a/src/cmd/compile/internal/big/rat_test.go b/src/cmd/compile/internal/big/rat_test.go new file mode 100644 index 0000000000..012d0c47ec --- /dev/null +++ b/src/cmd/compile/internal/big/rat_test.go @@ -0,0 +1,736 @@ +// 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. + +package big + +import ( + "bytes" + "encoding/gob" + "encoding/json" + "encoding/xml" + "math" + "testing" +) + +func TestZeroRat(t *testing.T) { + var x, y, z Rat + y.SetFrac64(0, 42) + + if x.Cmp(&y) != 0 { + t.Errorf("x and y should be both equal and zero") + } + + if s := x.String(); s != "0/1" { + t.Errorf("got x = %s, want 0/1", s) + } + + if s := x.RatString(); s != "0" { + t.Errorf("got x = %s, want 0", s) + } + + z.Add(&x, &y) + if s := z.RatString(); s != "0" { + t.Errorf("got x+y = %s, want 0", s) + } + + z.Sub(&x, &y) + if s := z.RatString(); s != "0" { + t.Errorf("got x-y = %s, want 0", s) + } + + z.Mul(&x, &y) + if s := z.RatString(); s != "0" { + t.Errorf("got x*y = %s, want 0", s) + } + + // check for division by zero + defer func() { + if s := recover(); s == nil || s.(string) != "division by zero" { + panic(s) + } + }() + z.Quo(&x, &y) +} + +func TestRatSign(t *testing.T) { + zero := NewRat(0, 1) + for _, a := range setStringTests { + x, ok := new(Rat).SetString(a.in) + if !ok { + continue + } + s := x.Sign() + e := x.Cmp(zero) + if s != e { + t.Errorf("got %d; want %d for z = %v", s, e, &x) + } + } +} + +var ratCmpTests = []struct { + rat1, rat2 string + out int +}{ + {"0", "0/1", 0}, + {"1/1", "1", 0}, + {"-1", "-2/2", 0}, + {"1", "0", 1}, + {"0/1", "1/1", -1}, + {"-5/1434770811533343057144", "-5/1434770811533343057145", -1}, + {"49832350382626108453/8964749413", "49832350382626108454/8964749413", -1}, + {"-37414950961700930/7204075375675961", "37414950961700930/7204075375675961", -1}, + {"37414950961700930/7204075375675961", "74829901923401860/14408150751351922", 0}, +} + +func TestRatCmp(t *testing.T) { + for i, test := range ratCmpTests { + x, _ := new(Rat).SetString(test.rat1) + y, _ := new(Rat).SetString(test.rat2) + + out := x.Cmp(y) + if out != test.out { + t.Errorf("#%d got out = %v; want %v", i, out, test.out) + } + } +} + +func TestIsInt(t *testing.T) { + one := NewInt(1) + for _, a := range setStringTests { + x, ok := new(Rat).SetString(a.in) + if !ok { + continue + } + i := x.IsInt() + e := x.Denom().Cmp(one) == 0 + if i != e { + t.Errorf("got IsInt(%v) == %v; want %v", x, i, e) + } + } +} + +func TestRatAbs(t *testing.T) { + zero := new(Rat) + for _, a := range setStringTests { + x, ok := new(Rat).SetString(a.in) + if !ok { + continue + } + e := new(Rat).Set(x) + if e.Cmp(zero) < 0 { + e.Sub(zero, e) + } + z := new(Rat).Abs(x) + if z.Cmp(e) != 0 { + t.Errorf("got Abs(%v) = %v; want %v", x, z, e) + } + } +} + +func TestRatNeg(t *testing.T) { + zero := new(Rat) + for _, a := range setStringTests { + x, ok := new(Rat).SetString(a.in) + if !ok { + continue + } + e := new(Rat).Sub(zero, x) + z := new(Rat).Neg(x) + if z.Cmp(e) != 0 { + t.Errorf("got Neg(%v) = %v; want %v", x, z, e) + } + } +} + +func TestRatInv(t *testing.T) { + zero := new(Rat) + for _, a := range setStringTests { + x, ok := new(Rat).SetString(a.in) + if !ok { + continue + } + if x.Cmp(zero) == 0 { + continue // avoid division by zero + } + e := new(Rat).SetFrac(x.Denom(), x.Num()) + z := new(Rat).Inv(x) + if z.Cmp(e) != 0 { + t.Errorf("got Inv(%v) = %v; want %v", x, z, e) + } + } +} + +type ratBinFun func(z, x, y *Rat) *Rat +type ratBinArg struct { + x, y, z string +} + +func testRatBin(t *testing.T, i int, name string, f ratBinFun, a ratBinArg) { + x, _ := new(Rat).SetString(a.x) + y, _ := new(Rat).SetString(a.y) + z, _ := new(Rat).SetString(a.z) + out := f(new(Rat), x, y) + + if out.Cmp(z) != 0 { + t.Errorf("%s #%d got %s want %s", name, i, out, z) + } +} + +var ratBinTests = []struct { + x, y string + sum, prod string +}{ + {"0", "0", "0", "0"}, + {"0", "1", "1", "0"}, + {"-1", "0", "-1", "0"}, + {"-1", "1", "0", "-1"}, + {"1", "1", "2", "1"}, + {"1/2", "1/2", "1", "1/4"}, + {"1/4", "1/3", "7/12", "1/12"}, + {"2/5", "-14/3", "-64/15", "-28/15"}, + {"4707/49292519774798173060", "-3367/70976135186689855734", "84058377121001851123459/1749296273614329067191168098769082663020", "-1760941/388732505247628681598037355282018369560"}, + {"-61204110018146728334/3", "-31052192278051565633/2", "-215564796870448153567/6", "950260896245257153059642991192710872711/3"}, + {"-854857841473707320655/4237645934602118692642972629634714039", "-18/31750379913563777419", "-27/133467566250814981", "15387441146526731771790/134546868362786310073779084329032722548987800600710485341"}, + {"618575745270541348005638912139/19198433543745179392300736", "-19948846211000086/637313996471", "27674141753240653/30123979153216", "-6169936206128396568797607742807090270137721977/6117715203873571641674006593837351328"}, + {"-3/26206484091896184128", "5/2848423294177090248", "15310893822118706237/9330894968229805033368778458685147968", "-5/24882386581946146755650075889827061248"}, + {"26946729/330400702820", "41563965/225583428284", "1238218672302860271/4658307703098666660055", "224002580204097/14906584649915733312176"}, + {"-8259900599013409474/7", "-84829337473700364773/56707961321161574960", "-468402123685491748914621885145127724451/396955729248131024720", "350340947706464153265156004876107029701/198477864624065512360"}, + {"575775209696864/1320203974639986246357", "29/712593081308", "410331716733912717985762465/940768218243776489278275419794956", "808/45524274987585732633"}, + {"1786597389946320496771/2066653520653241", "6269770/1992362624741777", "3559549865190272133656109052308126637/4117523232840525481453983149257", "8967230/3296219033"}, + {"-36459180403360509753/32150500941194292113930", "9381566963714/9633539", "301622077145533298008420642898530153/309723104686531919656937098270", "-3784609207827/3426986245"}, +} + +func TestRatBin(t *testing.T) { + for i, test := range ratBinTests { + arg := ratBinArg{test.x, test.y, test.sum} + testRatBin(t, i, "Add", (*Rat).Add, arg) + + arg = ratBinArg{test.y, test.x, test.sum} + testRatBin(t, i, "Add symmetric", (*Rat).Add, arg) + + arg = ratBinArg{test.sum, test.x, test.y} + testRatBin(t, i, "Sub", (*Rat).Sub, arg) + + arg = ratBinArg{test.sum, test.y, test.x} + testRatBin(t, i, "Sub symmetric", (*Rat).Sub, arg) + + arg = ratBinArg{test.x, test.y, test.prod} + testRatBin(t, i, "Mul", (*Rat).Mul, arg) + + arg = ratBinArg{test.y, test.x, test.prod} + testRatBin(t, i, "Mul symmetric", (*Rat).Mul, arg) + + if test.x != "0" { + arg = ratBinArg{test.prod, test.x, test.y} + testRatBin(t, i, "Quo", (*Rat).Quo, arg) + } + + if test.y != "0" { + arg = ratBinArg{test.prod, test.y, test.x} + testRatBin(t, i, "Quo symmetric", (*Rat).Quo, arg) + } + } +} + +func TestIssue820(t *testing.T) { + x := NewRat(3, 1) + y := NewRat(2, 1) + z := y.Quo(x, y) + q := NewRat(3, 2) + if z.Cmp(q) != 0 { + t.Errorf("got %s want %s", z, q) + } + + y = NewRat(3, 1) + x = NewRat(2, 1) + z = y.Quo(x, y) + q = NewRat(2, 3) + if z.Cmp(q) != 0 { + t.Errorf("got %s want %s", z, q) + } + + x = NewRat(3, 1) + z = x.Quo(x, x) + q = NewRat(3, 3) + if z.Cmp(q) != 0 { + t.Errorf("got %s want %s", z, q) + } +} + +var setFrac64Tests = []struct { + a, b int64 + out string +}{ + {0, 1, "0"}, + {0, -1, "0"}, + {1, 1, "1"}, + {-1, 1, "-1"}, + {1, -1, "-1"}, + {-1, -1, "1"}, + {-9223372036854775808, -9223372036854775808, "1"}, +} + +func TestRatSetFrac64Rat(t *testing.T) { + for i, test := range setFrac64Tests { + x := new(Rat).SetFrac64(test.a, test.b) + if x.RatString() != test.out { + t.Errorf("#%d got %s want %s", i, x.RatString(), test.out) + } + } +} + +func TestRatGobEncoding(t *testing.T) { + var medium bytes.Buffer + enc := gob.NewEncoder(&medium) + dec := gob.NewDecoder(&medium) + for _, test := range encodingTests { + medium.Reset() // empty buffer for each test case (in case of failures) + var tx Rat + tx.SetString(test + ".14159265") + if err := enc.Encode(&tx); err != nil { + t.Errorf("encoding of %s failed: %s", &tx, err) + } + var rx Rat + if err := dec.Decode(&rx); err != nil { + t.Errorf("decoding of %s failed: %s", &tx, err) + } + if rx.Cmp(&tx) != 0 { + t.Errorf("transmission of %s failed: got %s want %s", &tx, &rx, &tx) + } + } +} + +// Sending a nil Rat pointer (inside a slice) on a round trip through gob should yield a zero. +// TODO: top-level nils. +func TestGobEncodingNilRatInSlice(t *testing.T) { + buf := new(bytes.Buffer) + enc := gob.NewEncoder(buf) + dec := gob.NewDecoder(buf) + + var in = make([]*Rat, 1) + err := enc.Encode(&in) + if err != nil { + t.Errorf("gob encode failed: %q", err) + } + var out []*Rat + err = dec.Decode(&out) + if err != nil { + t.Fatalf("gob decode failed: %q", err) + } + if len(out) != 1 { + t.Fatalf("wrong len; want 1 got %d", len(out)) + } + var zero Rat + if out[0].Cmp(&zero) != 0 { + t.Errorf("transmission of (*Int)(nill) failed: got %s want 0", out) + } +} + +var ratNums = []string{ + "-141592653589793238462643383279502884197169399375105820974944592307816406286", + "-1415926535897932384626433832795028841971", + "-141592653589793", + "-1", + "0", + "1", + "141592653589793", + "1415926535897932384626433832795028841971", + "141592653589793238462643383279502884197169399375105820974944592307816406286", +} + +var ratDenoms = []string{ + "1", + "718281828459045", + "7182818284590452353602874713526624977572", + "718281828459045235360287471352662497757247093699959574966967627724076630353", +} + +func TestRatJSONEncoding(t *testing.T) { + for _, num := range ratNums { + for _, denom := range ratDenoms { + var tx Rat + tx.SetString(num + "/" + denom) + b, err := json.Marshal(&tx) + if err != nil { + t.Errorf("marshaling of %s failed: %s", &tx, err) + continue + } + var rx Rat + if err := json.Unmarshal(b, &rx); err != nil { + t.Errorf("unmarshaling of %s failed: %s", &tx, err) + continue + } + if rx.Cmp(&tx) != 0 { + t.Errorf("JSON encoding of %s failed: got %s want %s", &tx, &rx, &tx) + } + } + } +} + +func TestRatXMLEncoding(t *testing.T) { + for _, num := range ratNums { + for _, denom := range ratDenoms { + var tx Rat + tx.SetString(num + "/" + denom) + b, err := xml.Marshal(&tx) + if err != nil { + t.Errorf("marshaling of %s failed: %s", &tx, err) + continue + } + var rx Rat + if err := xml.Unmarshal(b, &rx); err != nil { + t.Errorf("unmarshaling of %s failed: %s", &tx, err) + continue + } + if rx.Cmp(&tx) != 0 { + t.Errorf("XML encoding of %s failed: got %s want %s", &tx, &rx, &tx) + } + } + } +} + +func TestIssue2379(t *testing.T) { + // 1) no aliasing + q := NewRat(3, 2) + x := new(Rat) + x.SetFrac(NewInt(3), NewInt(2)) + if x.Cmp(q) != 0 { + t.Errorf("1) got %s want %s", x, q) + } + + // 2) aliasing of numerator + x = NewRat(2, 3) + x.SetFrac(NewInt(3), x.Num()) + if x.Cmp(q) != 0 { + t.Errorf("2) got %s want %s", x, q) + } + + // 3) aliasing of denominator + x = NewRat(2, 3) + x.SetFrac(x.Denom(), NewInt(2)) + if x.Cmp(q) != 0 { + t.Errorf("3) got %s want %s", x, q) + } + + // 4) aliasing of numerator and denominator + x = NewRat(2, 3) + x.SetFrac(x.Denom(), x.Num()) + if x.Cmp(q) != 0 { + t.Errorf("4) got %s want %s", x, q) + } + + // 5) numerator and denominator are the same + q = NewRat(1, 1) + x = new(Rat) + n := NewInt(7) + x.SetFrac(n, n) + if x.Cmp(q) != 0 { + t.Errorf("5) got %s want %s", x, q) + } +} + +func TestIssue3521(t *testing.T) { + a := new(Int) + b := new(Int) + a.SetString("64375784358435883458348587", 0) + b.SetString("4789759874531", 0) + + // 0) a raw zero value has 1 as denominator + zero := new(Rat) + one := NewInt(1) + if zero.Denom().Cmp(one) != 0 { + t.Errorf("0) got %s want %s", zero.Denom(), one) + } + + // 1a) a zero value remains zero independent of denominator + x := new(Rat) + x.Denom().Set(new(Int).Neg(b)) + if x.Cmp(zero) != 0 { + t.Errorf("1a) got %s want %s", x, zero) + } + + // 1b) a zero value may have a denominator != 0 and != 1 + x.Num().Set(a) + qab := new(Rat).SetFrac(a, b) + if x.Cmp(qab) != 0 { + t.Errorf("1b) got %s want %s", x, qab) + } + + // 2a) an integral value becomes a fraction depending on denominator + x.SetFrac64(10, 2) + x.Denom().SetInt64(3) + q53 := NewRat(5, 3) + if x.Cmp(q53) != 0 { + t.Errorf("2a) got %s want %s", x, q53) + } + + // 2b) an integral value becomes a fraction depending on denominator + x = NewRat(10, 2) + x.Denom().SetInt64(3) + if x.Cmp(q53) != 0 { + t.Errorf("2b) got %s want %s", x, q53) + } + + // 3) changing the numerator/denominator of a Rat changes the Rat + x.SetFrac(a, b) + a = x.Num() + b = x.Denom() + a.SetInt64(5) + b.SetInt64(3) + if x.Cmp(q53) != 0 { + t.Errorf("3) got %s want %s", x, q53) + } +} + +func TestFloat32Distribution(t *testing.T) { + // Generate a distribution of (sign, mantissa, exp) values + // broader than the float32 range, and check Rat.Float32() + // always picks the closest float32 approximation. + var add = []int64{ + 0, + 1, + 3, + 5, + 7, + 9, + 11, + } + var winc, einc = uint64(1), 1 // soak test (~1.5s on x86-64) + if testing.Short() { + winc, einc = 5, 15 // quick test (~60ms on x86-64) + } + + for _, sign := range "+-" { + for _, a := range add { + for wid := uint64(0); wid < 30; wid += winc { + b := 1< 0 { + num.Lsh(num, uint(exp)) + } else { + den.Lsh(den, uint(-exp)) + } + r := new(Rat).SetFrac(num, den) + f, _ := r.Float32() + + if !checkIsBestApprox32(t, f, r) { + // Append context information. + t.Errorf("(input was mantissa %#x, exp %d; f = %g (%b); f ~ %g; r = %v)", + b, exp, f, f, math.Ldexp(float64(b), exp), r) + } + + checkNonLossyRoundtrip32(t, f) + } + } + } + } +} + +func TestFloat64Distribution(t *testing.T) { + // Generate a distribution of (sign, mantissa, exp) values + // broader than the float64 range, and check Rat.Float64() + // always picks the closest float64 approximation. + var add = []int64{ + 0, + 1, + 3, + 5, + 7, + 9, + 11, + } + var winc, einc = uint64(1), 1 // soak test (~75s on x86-64) + if testing.Short() { + winc, einc = 10, 500 // quick test (~12ms on x86-64) + } + + for _, sign := range "+-" { + for _, a := range add { + for wid := uint64(0); wid < 60; wid += winc { + b := 1< 0 { + num.Lsh(num, uint(exp)) + } else { + den.Lsh(den, uint(-exp)) + } + r := new(Rat).SetFrac(num, den) + f, _ := r.Float64() + + if !checkIsBestApprox64(t, f, r) { + // Append context information. + t.Errorf("(input was mantissa %#x, exp %d; f = %g (%b); f ~ %g; r = %v)", + b, exp, f, f, math.Ldexp(float64(b), exp), r) + } + + checkNonLossyRoundtrip64(t, f) + } + } + } + } +} + +// TestSetFloat64NonFinite checks that SetFloat64 of a non-finite value +// returns nil. +func TestSetFloat64NonFinite(t *testing.T) { + for _, f := range []float64{math.NaN(), math.Inf(+1), math.Inf(-1)} { + var r Rat + if r2 := r.SetFloat64(f); r2 != nil { + t.Errorf("SetFloat64(%g) was %v, want nil", f, r2) + } + } +} + +// checkNonLossyRoundtrip32 checks that a float->Rat->float roundtrip is +// non-lossy for finite f. +func checkNonLossyRoundtrip32(t *testing.T, f float32) { + if !isFinite(float64(f)) { + return + } + r := new(Rat).SetFloat64(float64(f)) + if r == nil { + t.Errorf("Rat.SetFloat64(float64(%g) (%b)) == nil", f, f) + return + } + f2, exact := r.Float32() + if f != f2 || !exact { + t.Errorf("Rat.SetFloat64(float64(%g)).Float32() = %g (%b), %v, want %g (%b), %v; delta = %b", + f, f2, f2, exact, f, f, true, f2-f) + } +} + +// checkNonLossyRoundtrip64 checks that a float->Rat->float roundtrip is +// non-lossy for finite f. +func checkNonLossyRoundtrip64(t *testing.T, f float64) { + if !isFinite(f) { + return + } + r := new(Rat).SetFloat64(f) + if r == nil { + t.Errorf("Rat.SetFloat64(%g (%b)) == nil", f, f) + return + } + f2, exact := r.Float64() + if f != f2 || !exact { + t.Errorf("Rat.SetFloat64(%g).Float64() = %g (%b), %v, want %g (%b), %v; delta = %b", + f, f2, f2, exact, f, f, true, f2-f) + } +} + +// delta returns the absolute difference between r and f. +func delta(r *Rat, f float64) *Rat { + d := new(Rat).Sub(r, new(Rat).SetFloat64(f)) + return d.Abs(d) +} + +// checkIsBestApprox32 checks that f is the best possible float32 +// approximation of r. +// Returns true on success. +func checkIsBestApprox32(t *testing.T, f float32, r *Rat) bool { + if math.Abs(float64(f)) >= math.MaxFloat32 { + // Cannot check +Inf, -Inf, nor the float next to them (MaxFloat32). + // But we have tests for these special cases. + return true + } + + // r must be strictly between f0 and f1, the floats bracketing f. + f0 := math.Nextafter32(f, float32(math.Inf(-1))) + f1 := math.Nextafter32(f, float32(math.Inf(+1))) + + // For f to be correct, r must be closer to f than to f0 or f1. + df := delta(r, float64(f)) + df0 := delta(r, float64(f0)) + df1 := delta(r, float64(f1)) + if df.Cmp(df0) > 0 { + t.Errorf("Rat(%v).Float32() = %g (%b), but previous float32 %g (%b) is closer", r, f, f, f0, f0) + return false + } + if df.Cmp(df1) > 0 { + t.Errorf("Rat(%v).Float32() = %g (%b), but next float32 %g (%b) is closer", r, f, f, f1, f1) + return false + } + if df.Cmp(df0) == 0 && !isEven32(f) { + t.Errorf("Rat(%v).Float32() = %g (%b); halfway should have rounded to %g (%b) instead", r, f, f, f0, f0) + return false + } + if df.Cmp(df1) == 0 && !isEven32(f) { + t.Errorf("Rat(%v).Float32() = %g (%b); halfway should have rounded to %g (%b) instead", r, f, f, f1, f1) + return false + } + return true +} + +// checkIsBestApprox64 checks that f is the best possible float64 +// approximation of r. +// Returns true on success. +func checkIsBestApprox64(t *testing.T, f float64, r *Rat) bool { + if math.Abs(f) >= math.MaxFloat64 { + // Cannot check +Inf, -Inf, nor the float next to them (MaxFloat64). + // But we have tests for these special cases. + return true + } + + // r must be strictly between f0 and f1, the floats bracketing f. + f0 := math.Nextafter(f, math.Inf(-1)) + f1 := math.Nextafter(f, math.Inf(+1)) + + // For f to be correct, r must be closer to f than to f0 or f1. + df := delta(r, f) + df0 := delta(r, f0) + df1 := delta(r, f1) + if df.Cmp(df0) > 0 { + t.Errorf("Rat(%v).Float64() = %g (%b), but previous float64 %g (%b) is closer", r, f, f, f0, f0) + return false + } + if df.Cmp(df1) > 0 { + t.Errorf("Rat(%v).Float64() = %g (%b), but next float64 %g (%b) is closer", r, f, f, f1, f1) + return false + } + if df.Cmp(df0) == 0 && !isEven64(f) { + t.Errorf("Rat(%v).Float64() = %g (%b); halfway should have rounded to %g (%b) instead", r, f, f, f0, f0) + return false + } + if df.Cmp(df1) == 0 && !isEven64(f) { + t.Errorf("Rat(%v).Float64() = %g (%b); halfway should have rounded to %g (%b) instead", r, f, f, f1, f1) + return false + } + return true +} + +func isEven32(f float32) bool { return math.Float32bits(f)&1 == 0 } +func isEven64(f float64) bool { return math.Float64bits(f)&1 == 0 } + +func TestIsFinite(t *testing.T) { + finites := []float64{ + 1.0 / 3, + 4891559871276714924261e+222, + math.MaxFloat64, + math.SmallestNonzeroFloat64, + -math.MaxFloat64, + -math.SmallestNonzeroFloat64, + } + for _, f := range finites { + if !isFinite(f) { + t.Errorf("!IsFinite(%g (%b))", f, f) + } + } + nonfinites := []float64{ + math.NaN(), + math.Inf(-1), + math.Inf(+1), + } + for _, f := range nonfinites { + if isFinite(f) { + t.Errorf("IsFinite(%g, (%b))", f, f) + } + } +} diff --git a/src/cmd/compile/internal/big/ratconv.go b/src/cmd/compile/internal/big/ratconv.go new file mode 100644 index 0000000000..778077b96e --- /dev/null +++ b/src/cmd/compile/internal/big/ratconv.go @@ -0,0 +1,251 @@ +// 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. + +// This file implements rat-to-string conversion functions. + +package big + +import ( + "errors" + "fmt" + "io" + "strconv" + "strings" +) + +func ratTok(ch rune) bool { + return strings.IndexRune("+-/0123456789.eE", ch) >= 0 +} + +// Scan is a support routine for fmt.Scanner. It accepts the formats +// 'e', 'E', 'f', 'F', 'g', 'G', and 'v'. All formats are equivalent. +func (z *Rat) Scan(s fmt.ScanState, ch rune) error { + tok, err := s.Token(true, ratTok) + if err != nil { + return err + } + if strings.IndexRune("efgEFGv", ch) < 0 { + return errors.New("Rat.Scan: invalid verb") + } + if _, ok := z.SetString(string(tok)); !ok { + return errors.New("Rat.Scan: invalid syntax") + } + return nil +} + +// SetString sets z to the value of s and returns z and a boolean indicating +// success. s can be given as a fraction "a/b" or as a floating-point number +// optionally followed by an exponent. If the operation failed, the value of +// z is undefined but the returned value is nil. +func (z *Rat) SetString(s string) (*Rat, bool) { + if len(s) == 0 { + return nil, false + } + // len(s) > 0 + + // parse fraction a/b, if any + if sep := strings.Index(s, "/"); sep >= 0 { + if _, ok := z.a.SetString(s[:sep], 0); !ok { + return nil, false + } + s = s[sep+1:] + var err error + if z.b.abs, _, _, err = z.b.abs.scan(strings.NewReader(s), 0, false); err != nil { + return nil, false + } + if len(z.b.abs) == 0 { + return nil, false + } + return z.norm(), true + } + + // parse floating-point number + r := strings.NewReader(s) + + // sign + neg, err := scanSign(r) + if err != nil { + return nil, false + } + + // mantissa + var ecorr int + z.a.abs, _, ecorr, err = z.a.abs.scan(r, 10, true) + if err != nil { + return nil, false + } + + // exponent + var exp int64 + exp, _, err = scanExponent(r, false) + if err != nil { + return nil, false + } + + // there should be no unread characters left + if _, err = r.ReadByte(); err != io.EOF { + return nil, false + } + + // correct exponent + if ecorr < 0 { + exp += int64(ecorr) + } + + // compute exponent power + expabs := exp + if expabs < 0 { + expabs = -expabs + } + powTen := nat(nil).expNN(natTen, nat(nil).setWord(Word(expabs)), nil) + + // complete fraction + if exp < 0 { + z.b.abs = powTen + z.norm() + } else { + z.a.abs = z.a.abs.mul(z.a.abs, powTen) + z.b.abs = z.b.abs[:0] + } + + z.a.neg = neg && len(z.a.abs) > 0 // 0 has no sign + + return z, true +} + +// scanExponent scans the longest possible prefix of r representing a decimal +// ('e', 'E') or binary ('p') exponent, if any. It returns the exponent, the +// exponent base (10 or 2), or a read or syntax error, if any. +// +// exponent = ( "E" | "e" | "p" ) [ sign ] digits . +// sign = "+" | "-" . +// digits = digit { digit } . +// digit = "0" ... "9" . +// +// A binary exponent is only permitted if binExpOk is set. +func scanExponent(r io.ByteScanner, binExpOk bool) (exp int64, base int, err error) { + base = 10 + + var ch byte + if ch, err = r.ReadByte(); err != nil { + if err == io.EOF { + err = nil // no exponent; same as e0 + } + return + } + + switch ch { + case 'e', 'E': + // ok + case 'p': + if binExpOk { + base = 2 + break // ok + } + fallthrough // binary exponent not permitted + default: + r.UnreadByte() + return // no exponent; same as e0 + } + + var neg bool + if neg, err = scanSign(r); err != nil { + return + } + + var digits []byte + if neg { + digits = append(digits, '-') + } + + // no need to use nat.scan for exponent digits + // since we only care about int64 values - the + // from-scratch scan is easy enough and faster + for i := 0; ; i++ { + if ch, err = r.ReadByte(); err != nil { + if err != io.EOF || i == 0 { + return + } + err = nil + break // i > 0 + } + if ch < '0' || '9' < ch { + if i == 0 { + r.UnreadByte() + err = fmt.Errorf("invalid exponent (missing digits)") + return + } + break // i > 0 + } + digits = append(digits, byte(ch)) + } + // i > 0 => we have at least one digit + + exp, err = strconv.ParseInt(string(digits), 10, 64) + return +} + +// String returns a string representation of x in the form "a/b" (even if b == 1). +func (x *Rat) String() string { + s := "/1" + if len(x.b.abs) != 0 { + s = "/" + x.b.abs.decimalString() + } + return x.a.String() + s +} + +// RatString returns a string representation of x in the form "a/b" if b != 1, +// and in the form "a" if b == 1. +func (x *Rat) RatString() string { + if x.IsInt() { + return x.a.String() + } + return x.String() +} + +// FloatString returns a string representation of x in decimal form with prec +// digits of precision after the decimal point and the last digit rounded. +func (x *Rat) FloatString(prec int) string { + if x.IsInt() { + s := x.a.String() + if prec > 0 { + s += "." + strings.Repeat("0", prec) + } + return s + } + // x.b.abs != 0 + + q, r := nat(nil).div(nat(nil), x.a.abs, x.b.abs) + + p := natOne + if prec > 0 { + p = nat(nil).expNN(natTen, nat(nil).setUint64(uint64(prec)), nil) + } + + r = r.mul(r, p) + r, r2 := r.div(nat(nil), r, x.b.abs) + + // see if we need to round up + r2 = r2.add(r2, r2) + if x.b.abs.cmp(r2) <= 0 { + r = r.add(r, natOne) + if r.cmp(p) >= 0 { + q = nat(nil).add(q, natOne) + r = nat(nil).sub(r, p) + } + } + + s := q.decimalString() + if x.a.neg { + s = "-" + s + } + + if prec > 0 { + rs := r.decimalString() + leadingZeros := prec - len(rs) + s += "." + strings.Repeat("0", leadingZeros) + rs + } + + return s +} diff --git a/src/cmd/compile/internal/big/ratconv_test.go b/src/cmd/compile/internal/big/ratconv_test.go new file mode 100644 index 0000000000..16b3a19418 --- /dev/null +++ b/src/cmd/compile/internal/big/ratconv_test.go @@ -0,0 +1,451 @@ +// 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 big + +import ( + "bytes" + "fmt" + "math" + "strconv" + "strings" + "testing" +) + +type StringTest struct { + in, out string + ok bool +} + +var setStringTests = []StringTest{ + {"0", "0", true}, + {"-0", "0", true}, + {"1", "1", true}, + {"-1", "-1", true}, + {"1.", "1", true}, + {"1e0", "1", true}, + {"1.e1", "10", true}, + {in: "1e"}, + {in: "1.e"}, + {in: "1e+14e-5"}, + {in: "1e4.5"}, + {in: "r"}, + {in: "a/b"}, + {in: "a.b"}, + {"-0.1", "-1/10", true}, + {"-.1", "-1/10", true}, + {"2/4", "1/2", true}, + {".25", "1/4", true}, + {"-1/5", "-1/5", true}, + {"8129567.7690E14", "812956776900000000000", true}, + {"78189e+4", "781890000", true}, + {"553019.8935e+8", "55301989350000", true}, + {"98765432109876543210987654321e-10", "98765432109876543210987654321/10000000000", true}, + {"9877861857500000E-7", "3951144743/4", true}, + {"2169378.417e-3", "2169378417/1000000", true}, + {"884243222337379604041632732738665534", "884243222337379604041632732738665534", true}, + {"53/70893980658822810696", "53/70893980658822810696", true}, + {"106/141787961317645621392", "53/70893980658822810696", true}, + {"204211327800791583.81095", "4084226556015831676219/20000", true}, + {in: "1/0"}, +} + +// These are not supported by fmt.Fscanf. +var setStringTests2 = []StringTest{ + {"0x10", "16", true}, + {"-010/1", "-8", true}, // TODO(gri) should we even permit octal here? + {"-010.", "-10", true}, + {"0x10/0x20", "1/2", true}, + {"0b1000/3", "8/3", true}, + // TODO(gri) add more tests +} + +func TestRatSetString(t *testing.T) { + var tests []StringTest + tests = append(tests, setStringTests...) + tests = append(tests, setStringTests2...) + + for i, test := range tests { + x, ok := new(Rat).SetString(test.in) + + if ok { + if !test.ok { + t.Errorf("#%d SetString(%q) expected failure", i, test.in) + } else if x.RatString() != test.out { + t.Errorf("#%d SetString(%q) got %s want %s", i, test.in, x.RatString(), test.out) + } + } else if x != nil { + t.Errorf("#%d SetString(%q) got %p want nil", i, test.in, x) + } + } +} + +func TestRatScan(t *testing.T) { + var buf bytes.Buffer + for i, test := range setStringTests { + x := new(Rat) + buf.Reset() + buf.WriteString(test.in) + + _, err := fmt.Fscanf(&buf, "%v", x) + if err == nil != test.ok { + if test.ok { + t.Errorf("#%d (%s) error: %s", i, test.in, err) + } else { + t.Errorf("#%d (%s) expected error", i, test.in) + } + continue + } + if err == nil && x.RatString() != test.out { + t.Errorf("#%d got %s want %s", i, x.RatString(), test.out) + } + } +} + +var floatStringTests = []struct { + in string + prec int + out string +}{ + {"0", 0, "0"}, + {"0", 4, "0.0000"}, + {"1", 0, "1"}, + {"1", 2, "1.00"}, + {"-1", 0, "-1"}, + {".25", 2, "0.25"}, + {".25", 1, "0.3"}, + {".25", 3, "0.250"}, + {"-1/3", 3, "-0.333"}, + {"-2/3", 4, "-0.6667"}, + {"0.96", 1, "1.0"}, + {"0.999", 2, "1.00"}, + {"0.9", 0, "1"}, + {".25", -1, "0"}, + {".55", -1, "1"}, +} + +func TestFloatString(t *testing.T) { + for i, test := range floatStringTests { + x, _ := new(Rat).SetString(test.in) + + if x.FloatString(test.prec) != test.out { + t.Errorf("#%d got %s want %s", i, x.FloatString(test.prec), test.out) + } + } +} + +// Test inputs to Rat.SetString. The prefix "long:" causes the test +// to be skipped in --test.short mode. (The threshold is about 500us.) +var float64inputs = []string{ + // Constants plundered from strconv/testfp.txt. + + // Table 1: Stress Inputs for Conversion to 53-bit Binary, < 1/2 ULP + "5e+125", + "69e+267", + "999e-026", + "7861e-034", + "75569e-254", + "928609e-261", + "9210917e+080", + "84863171e+114", + "653777767e+273", + "5232604057e-298", + "27235667517e-109", + "653532977297e-123", + "3142213164987e-294", + "46202199371337e-072", + "231010996856685e-073", + "9324754620109615e+212", + "78459735791271921e+049", + "272104041512242479e+200", + "6802601037806061975e+198", + "20505426358836677347e-221", + "836168422905420598437e-234", + "4891559871276714924261e+222", + + // Table 2: Stress Inputs for Conversion to 53-bit Binary, > 1/2 ULP + "9e-265", + "85e-037", + "623e+100", + "3571e+263", + "81661e+153", + "920657e-023", + "4603285e-024", + "87575437e-309", + "245540327e+122", + "6138508175e+120", + "83356057653e+193", + "619534293513e+124", + "2335141086879e+218", + "36167929443327e-159", + "609610927149051e-255", + "3743626360493413e-165", + "94080055902682397e-242", + "899810892172646163e+283", + "7120190517612959703e+120", + "25188282901709339043e-252", + "308984926168550152811e-052", + "6372891218502368041059e+064", + + // Table 14: Stress Inputs for Conversion to 24-bit Binary, <1/2 ULP + "5e-20", + "67e+14", + "985e+15", + "7693e-42", + "55895e-16", + "996622e-44", + "7038531e-32", + "60419369e-46", + "702990899e-20", + "6930161142e-48", + "25933168707e+13", + "596428896559e+20", + + // Table 15: Stress Inputs for Conversion to 24-bit Binary, >1/2 ULP + "3e-23", + "57e+18", + "789e-35", + "2539e-18", + "76173e+28", + "887745e-11", + "5382571e-37", + "82381273e-35", + "750486563e-38", + "3752432815e-39", + "75224575729e-45", + "459926601011e+15", + + // Constants plundered from strconv/atof_test.go. + + "0", + "1", + "+1", + "1e23", + "1E23", + "100000000000000000000000", + "1e-100", + "123456700", + "99999999999999974834176", + "100000000000000000000001", + "100000000000000008388608", + "100000000000000016777215", + "100000000000000016777216", + "-1", + "-0.1", + "-0", // NB: exception made for this input + "1e-20", + "625e-3", + + // largest float64 + "1.7976931348623157e308", + "-1.7976931348623157e308", + // next float64 - too large + "1.7976931348623159e308", + "-1.7976931348623159e308", + // the border is ...158079 + // borderline - okay + "1.7976931348623158e308", + "-1.7976931348623158e308", + // borderline - too large + "1.797693134862315808e308", + "-1.797693134862315808e308", + + // a little too large + "1e308", + "2e308", + "1e309", + + // way too large + "1e310", + "-1e310", + "1e400", + "-1e400", + "long:1e400000", + "long:-1e400000", + + // denormalized + "1e-305", + "1e-306", + "1e-307", + "1e-308", + "1e-309", + "1e-310", + "1e-322", + // smallest denormal + "5e-324", + "4e-324", + "3e-324", + // too small + "2e-324", + // way too small + "1e-350", + "long:1e-400000", + // way too small, negative + "-1e-350", + "long:-1e-400000", + + // try to overflow exponent + // [Disabled: too slow and memory-hungry with rationals.] + // "1e-4294967296", + // "1e+4294967296", + // "1e-18446744073709551616", + // "1e+18446744073709551616", + + // http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/ + "2.2250738585072012e-308", + // http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/ + "2.2250738585072011e-308", + + // A very large number (initially wrongly parsed by the fast algorithm). + "4.630813248087435e+307", + + // A different kind of very large number. + "22.222222222222222", + "long:2." + strings.Repeat("2", 4000) + "e+1", + + // Exactly halfway between 1 and math.Nextafter(1, 2). + // Round to even (down). + "1.00000000000000011102230246251565404236316680908203125", + // Slightly lower; still round down. + "1.00000000000000011102230246251565404236316680908203124", + // Slightly higher; round up. + "1.00000000000000011102230246251565404236316680908203126", + // Slightly higher, but you have to read all the way to the end. + "long:1.00000000000000011102230246251565404236316680908203125" + strings.Repeat("0", 10000) + "1", + + // Smallest denormal, 2^(-1022-52) + "4.940656458412465441765687928682213723651e-324", + // Half of smallest denormal, 2^(-1022-53) + "2.470328229206232720882843964341106861825e-324", + // A little more than the exact half of smallest denormal + // 2^-1075 + 2^-1100. (Rounds to 1p-1074.) + "2.470328302827751011111470718709768633275e-324", + // The exact halfway between smallest normal and largest denormal: + // 2^-1022 - 2^-1075. (Rounds to 2^-1022.) + "2.225073858507201136057409796709131975935e-308", + + "1152921504606846975", // 1<<60 - 1 + "-1152921504606846975", // -(1<<60 - 1) + "1152921504606846977", // 1<<60 + 1 + "-1152921504606846977", // -(1<<60 + 1) + + "1/3", +} + +// isFinite reports whether f represents a finite rational value. +// It is equivalent to !math.IsNan(f) && !math.IsInf(f, 0). +func isFinite(f float64) bool { + return math.Abs(f) <= math.MaxFloat64 +} + +func TestFloat32SpecialCases(t *testing.T) { + for _, input := range float64inputs { + if strings.HasPrefix(input, "long:") { + if testing.Short() { + continue + } + input = input[len("long:"):] + } + + r, ok := new(Rat).SetString(input) + if !ok { + t.Errorf("Rat.SetString(%q) failed", input) + continue + } + f, exact := r.Float32() + + // 1. Check string -> Rat -> float32 conversions are + // consistent with strconv.ParseFloat. + // Skip this check if the input uses "a/b" rational syntax. + if !strings.Contains(input, "/") { + e64, _ := strconv.ParseFloat(input, 32) + e := float32(e64) + + // Careful: negative Rats too small for + // float64 become -0, but Rat obviously cannot + // preserve the sign from SetString("-0"). + switch { + case math.Float32bits(e) == math.Float32bits(f): + // Ok: bitwise equal. + case f == 0 && r.Num().BitLen() == 0: + // Ok: Rat(0) is equivalent to both +/- float64(0). + default: + t.Errorf("strconv.ParseFloat(%q) = %g (%b), want %g (%b); delta = %g", input, e, e, f, f, f-e) + } + } + + if !isFinite(float64(f)) { + continue + } + + // 2. Check f is best approximation to r. + if !checkIsBestApprox32(t, f, r) { + // Append context information. + t.Errorf("(input was %q)", input) + } + + // 3. Check f->R->f roundtrip is non-lossy. + checkNonLossyRoundtrip32(t, f) + + // 4. Check exactness using slow algorithm. + if wasExact := new(Rat).SetFloat64(float64(f)).Cmp(r) == 0; wasExact != exact { + t.Errorf("Rat.SetString(%q).Float32().exact = %t, want %t", input, exact, wasExact) + } + } +} + +func TestFloat64SpecialCases(t *testing.T) { + for _, input := range float64inputs { + if strings.HasPrefix(input, "long:") { + if testing.Short() { + continue + } + input = input[len("long:"):] + } + + r, ok := new(Rat).SetString(input) + if !ok { + t.Errorf("Rat.SetString(%q) failed", input) + continue + } + f, exact := r.Float64() + + // 1. Check string -> Rat -> float64 conversions are + // consistent with strconv.ParseFloat. + // Skip this check if the input uses "a/b" rational syntax. + if !strings.Contains(input, "/") { + e, _ := strconv.ParseFloat(input, 64) + + // Careful: negative Rats too small for + // float64 become -0, but Rat obviously cannot + // preserve the sign from SetString("-0"). + switch { + case math.Float64bits(e) == math.Float64bits(f): + // Ok: bitwise equal. + case f == 0 && r.Num().BitLen() == 0: + // Ok: Rat(0) is equivalent to both +/- float64(0). + default: + t.Errorf("strconv.ParseFloat(%q) = %g (%b), want %g (%b); delta = %g", input, e, e, f, f, f-e) + } + } + + if !isFinite(f) { + continue + } + + // 2. Check f is best approximation to r. + if !checkIsBestApprox64(t, f, r) { + // Append context information. + t.Errorf("(input was %q)", input) + } + + // 3. Check f->R->f roundtrip is non-lossy. + checkNonLossyRoundtrip64(t, f) + + // 4. Check exactness using slow algorithm. + if wasExact := new(Rat).SetFloat64(f).Cmp(r) == 0; wasExact != exact { + t.Errorf("Rat.SetString(%q).Float64().exact = %t, want %t", input, exact, wasExact) + } + } +} diff --git a/src/cmd/compile/internal/big/roundingmode_string.go b/src/cmd/compile/internal/big/roundingmode_string.go new file mode 100644 index 0000000000..05024b8065 --- /dev/null +++ b/src/cmd/compile/internal/big/roundingmode_string.go @@ -0,0 +1,16 @@ +// generated by stringer -type=RoundingMode; DO NOT EDIT + +package big + +import "fmt" + +const _RoundingMode_name = "ToNearestEvenToNearestAwayToZeroAwayFromZeroToNegativeInfToPositiveInf" + +var _RoundingMode_index = [...]uint8{0, 13, 26, 32, 44, 57, 70} + +func (i RoundingMode) String() string { + if i+1 >= RoundingMode(len(_RoundingMode_index)) { + return fmt.Sprintf("RoundingMode(%d)", i) + } + return _RoundingMode_name[_RoundingMode_index[i]:_RoundingMode_index[i+1]] +} diff --git a/src/cmd/compile/internal/big/vendor.bash b/src/cmd/compile/internal/big/vendor.bash new file mode 100755 index 0000000000..84aa750462 --- /dev/null +++ b/src/cmd/compile/internal/big/vendor.bash @@ -0,0 +1,25 @@ +#!/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. + +# Run this script to obtain an up-to-date vendored version of math/big. + +BIGDIR=../../../../math/big + +# Start from scratch. +rm *.go + +# We don't want any assembly files. +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 +rm arith_decl_pure.go + +# gofmt to clean up after sed +gofmt -w . + +# Test that it works +go test -short diff --git a/src/cmd/compile/internal/gc/align.go b/src/cmd/compile/internal/gc/align.go new file mode 100644 index 0000000000..789e59bfd0 --- /dev/null +++ b/src/cmd/compile/internal/gc/align.go @@ -0,0 +1,706 @@ +// Copyright 2009 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 gc + +import "cmd/internal/obj" + +/* + * machine size and rounding + * alignment is dictated around + * the size of a pointer, set in betypeinit + * (see ../6g/galign.c). + */ +var defercalc int + +func Rnd(o int64, r int64) int64 { + if r < 1 || r > 8 || r&(r-1) != 0 { + Fatal("rnd %d", r) + } + return (o + r - 1) &^ (r - 1) +} + +func offmod(t *Type) { + o := int32(0) + for f := t.Type; f != nil; f = f.Down { + if f.Etype != TFIELD { + Fatal("offmod: not TFIELD: %v", Tconv(f, obj.FmtLong)) + } + f.Width = int64(o) + o += int32(Widthptr) + if int64(o) >= Thearch.MAXWIDTH { + Yyerror("interface too large") + o = int32(Widthptr) + } + } +} + +func widstruct(errtype *Type, t *Type, o int64, flag int) int64 { + starto := o + maxalign := int32(flag) + if maxalign < 1 { + maxalign = 1 + } + lastzero := int64(0) + var w int64 + for f := t.Type; f != nil; f = f.Down { + if f.Etype != TFIELD { + Fatal("widstruct: not TFIELD: %v", Tconv(f, obj.FmtLong)) + } + if f.Type == nil { + // broken field, just skip it so that other valid fields + // get a width. + continue + } + + dowidth(f.Type) + if int32(f.Type.Align) > maxalign { + maxalign = int32(f.Type.Align) + } + if f.Type.Width < 0 { + Fatal("invalid width %d", f.Type.Width) + } + w = f.Type.Width + if f.Type.Align > 0 { + o = Rnd(o, int64(f.Type.Align)) + } + f.Width = o // really offset for TFIELD + if f.Nname != nil { + // this same stackparam logic is in addrescapes + // 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 + f.Nname.Xoffset = 0 + } else { + f.Nname.Xoffset = o + } + } + + if w == 0 { + lastzero = o + } + o += w + if o >= Thearch.MAXWIDTH { + Yyerror("type %v too large", Tconv(errtype, obj.FmtLong)) + o = 8 // small but nonzero + } + } + + // For nonzero-sized structs which end in a zero-sized thing, we add + // an extra byte of padding to the type. This padding ensures that + // taking the address of the zero-sized thing can't manufacture a + // pointer to the next object in the heap. See issue 9401. + if flag == 1 && o > starto && o == lastzero { + o++ + } + + // final width is rounded + if flag != 0 { + o = Rnd(o, int64(maxalign)) + } + t.Align = uint8(maxalign) + + // type width only includes back to first field's offset + t.Width = o - starto + + return o +} + +func dowidth(t *Type) { + if Widthptr == 0 { + Fatal("dowidth without betypeinit") + } + + if t == nil { + return + } + + if t.Width > 0 { + return + } + + if t.Width == -2 { + lno := int(lineno) + lineno = int32(t.Lineno) + if t.Broke == 0 { + t.Broke = 1 + Yyerror("invalid recursive type %v", t) + } + + t.Width = 0 + lineno = int32(lno) + return + } + + // break infinite recursion if the broken recursive type + // is referenced again + if t.Broke != 0 && t.Width == 0 { + return + } + + // defer checkwidth calls until after we're done + defercalc++ + + lno := int(lineno) + lineno = int32(t.Lineno) + t.Width = -2 + t.Align = 0 + + et := int32(t.Etype) + switch et { + case TFUNC, TCHAN, TMAP, TSTRING: + break + + /* simtype == 0 during bootstrap */ + default: + if Simtype[t.Etype] != 0 { + et = int32(Simtype[t.Etype]) + } + } + + w := int64(0) + switch et { + default: + Fatal("dowidth: unknown type: %v", t) + + /* compiler-specific stuff */ + case TINT8, TUINT8, TBOOL: + // bool is int8 + w = 1 + + case TINT16, TUINT16: + w = 2 + + case TINT32, TUINT32, TFLOAT32: + w = 4 + + case TINT64, TUINT64, TFLOAT64, TCOMPLEX64: + w = 8 + t.Align = uint8(Widthreg) + + case TCOMPLEX128: + w = 16 + t.Align = uint8(Widthreg) + + case TPTR32: + w = 4 + checkwidth(t.Type) + + case TPTR64: + w = 8 + checkwidth(t.Type) + + case TUNSAFEPTR: + w = int64(Widthptr) + + case TINTER: // implemented as 2 pointers + w = 2 * int64(Widthptr) + + t.Align = uint8(Widthptr) + offmod(t) + + case TCHAN: // implemented as pointer + w = int64(Widthptr) + + checkwidth(t.Type) + + // make fake type to check later to + // trigger channel argument check. + t1 := typ(TCHANARGS) + + t1.Type = t + checkwidth(t1) + + case TCHANARGS: + t1 := t.Type + dowidth(t.Type) // just in case + if t1.Type.Width >= 1<<16 { + Yyerror("channel element type too large (>64kB)") + } + t.Width = 1 + + case TMAP: // implemented as pointer + w = int64(Widthptr) + + checkwidth(t.Type) + checkwidth(t.Down) + + case TFORW: // should have been filled in + if t.Broke == 0 { + Yyerror("invalid recursive type %v", t) + } + w = 1 // anything will do + + // dummy type; should be replaced before use. + case TANY: + if Debug['A'] == 0 { + Fatal("dowidth any") + } + w = 1 // anything will do + + case TSTRING: + if sizeof_String == 0 { + Fatal("early dowidth string") + } + w = int64(sizeof_String) + t.Align = uint8(Widthptr) + + case TARRAY: + if t.Type == nil { + break + } + if t.Bound >= 0 { + dowidth(t.Type) + if t.Type.Width != 0 { + cap := (uint64(Thearch.MAXWIDTH) - 1) / uint64(t.Type.Width) + if uint64(t.Bound) > cap { + Yyerror("type %v larger than address space", Tconv(t, obj.FmtLong)) + } + } + + w = t.Bound * t.Type.Width + t.Align = t.Type.Align + } else if t.Bound == -1 { + w = int64(sizeof_Array) + checkwidth(t.Type) + t.Align = uint8(Widthptr) + } else if t.Bound == -100 { + if t.Broke == 0 { + Yyerror("use of [...] array outside of array literal") + t.Broke = 1 + } + } else { + Fatal("dowidth %v", t) // probably [...]T + } + + case TSTRUCT: + if t.Funarg != 0 { + Fatal("dowidth fn struct %v", t) + } + w = widstruct(t, t, 0, 1) + + // make fake type to check later to + // trigger function argument computation. + case TFUNC: + t1 := typ(TFUNCARGS) + + t1.Type = t + checkwidth(t1) + + // width of func type is pointer + w = int64(Widthptr) + + // function is 3 cated structures; + // compute their widths as side-effect. + case TFUNCARGS: + t1 := t.Type + + w = widstruct(t.Type, *getthis(t1), 0, 0) + w = widstruct(t.Type, *getinarg(t1), w, Widthreg) + w = widstruct(t.Type, *Getoutarg(t1), w, Widthreg) + t1.Argwid = w + if w%int64(Widthreg) != 0 { + Warn("bad type %v %d\n", t1, w) + } + t.Align = 1 + } + + if Widthptr == 4 && w != int64(int32(w)) { + Yyerror("type %v too large", t) + } + + t.Width = w + if t.Align == 0 { + if w > 8 || w&(w-1) != 0 { + Fatal("invalid alignment for %v", t) + } + t.Align = uint8(w) + } + + lineno = int32(lno) + + if defercalc == 1 { + resumecheckwidth() + } else { + defercalc-- + } +} + +/* + * when a type's width should be known, we call checkwidth + * to compute it. during a declaration like + * + * type T *struct { next T } + * + * it is necessary to defer the calculation of the struct width + * until after T has been initialized to be a pointer to that struct. + * similarly, during import processing structs may be used + * before their definition. in those situations, calling + * defercheckwidth() stops width calculations until + * resumecheckwidth() is called, at which point all the + * checkwidths that were deferred are executed. + * dowidth should only be called when the type's size + * is needed immediately. checkwidth makes sure the + * size is evaluated eventually. + */ +type TypeList struct { + t *Type + next *TypeList +} + +var tlfree *TypeList + +var tlq *TypeList + +func checkwidth(t *Type) { + if t == nil { + return + } + + // function arg structs should not be checked + // outside of the enclosing function. + if t.Funarg != 0 { + Fatal("checkwidth %v", t) + } + + if defercalc == 0 { + dowidth(t) + return + } + + if t.Deferwidth != 0 { + return + } + t.Deferwidth = 1 + + l := tlfree + if l != nil { + tlfree = l.next + } else { + l = new(TypeList) + } + + l.t = t + l.next = tlq + tlq = l +} + +func defercheckwidth() { + // we get out of sync on syntax errors, so don't be pedantic. + if defercalc != 0 && nerrors == 0 { + Fatal("defercheckwidth") + } + defercalc = 1 +} + +func resumecheckwidth() { + if defercalc == 0 { + Fatal("resumecheckwidth") + } + for l := tlq; l != nil; l = tlq { + l.t.Deferwidth = 0 + tlq = l.next + dowidth(l.t) + l.next = tlfree + tlfree = l + } + + defercalc = 0 +} + +var itable *Type // distinguished *byte + +func typeinit() { + if Widthptr == 0 { + Fatal("typeinit before betypeinit") + } + + for i := 0; i < NTYPE; i++ { + Simtype[i] = uint8(i) + } + + Types[TPTR32] = typ(TPTR32) + dowidth(Types[TPTR32]) + + Types[TPTR64] = typ(TPTR64) + dowidth(Types[TPTR64]) + + t := typ(TUNSAFEPTR) + Types[TUNSAFEPTR] = t + t.Sym = Pkglookup("Pointer", unsafepkg) + t.Sym.Def = typenod(t) + + dowidth(Types[TUNSAFEPTR]) + + Tptr = TPTR32 + if Widthptr == 8 { + Tptr = TPTR64 + } + + for i := TINT8; i <= TUINT64; i++ { + Isint[i] = true + } + Isint[TINT] = true + Isint[TUINT] = true + Isint[TUINTPTR] = true + + Isfloat[TFLOAT32] = true + Isfloat[TFLOAT64] = true + + Iscomplex[TCOMPLEX64] = true + Iscomplex[TCOMPLEX128] = true + + Isptr[TPTR32] = true + Isptr[TPTR64] = true + + isforw[TFORW] = true + + Issigned[TINT] = true + Issigned[TINT8] = true + Issigned[TINT16] = true + Issigned[TINT32] = true + Issigned[TINT64] = true + + /* + * initialize okfor + */ + for i := 0; i < NTYPE; i++ { + if Isint[i] || i == TIDEAL { + okforeq[i] = true + okforcmp[i] = true + okforarith[i] = true + okforadd[i] = true + okforand[i] = true + okforconst[i] = true + issimple[i] = true + Minintval[i] = new(Mpint) + Maxintval[i] = new(Mpint) + } + + if Isfloat[i] { + okforeq[i] = true + okforcmp[i] = true + okforadd[i] = true + okforarith[i] = true + okforconst[i] = true + issimple[i] = true + minfltval[i] = newMpflt() + maxfltval[i] = newMpflt() + } + + if Iscomplex[i] { + okforeq[i] = true + okforadd[i] = true + okforarith[i] = true + okforconst[i] = true + issimple[i] = true + } + } + + issimple[TBOOL] = true + + okforadd[TSTRING] = true + + okforbool[TBOOL] = true + + okforcap[TARRAY] = true + okforcap[TCHAN] = true + + okforconst[TBOOL] = true + okforconst[TSTRING] = true + + okforlen[TARRAY] = true + okforlen[TCHAN] = true + okforlen[TMAP] = true + okforlen[TSTRING] = true + + okforeq[TPTR32] = true + okforeq[TPTR64] = true + okforeq[TUNSAFEPTR] = true + okforeq[TINTER] = true + okforeq[TCHAN] = true + okforeq[TSTRING] = true + okforeq[TBOOL] = true + okforeq[TMAP] = true // nil only; refined in typecheck + okforeq[TFUNC] = true // nil only; refined in typecheck + okforeq[TARRAY] = true // nil slice only; refined in typecheck + okforeq[TSTRUCT] = true // it's complicated; refined in typecheck + + okforcmp[TSTRING] = true + + var i int + for i = 0; i < len(okfor); i++ { + okfor[i] = okfornone[:] + } + + // binary + okfor[OADD] = okforadd[:] + + okfor[OAND] = okforand[:] + okfor[OANDAND] = okforbool[:] + okfor[OANDNOT] = okforand[:] + okfor[ODIV] = okforarith[:] + okfor[OEQ] = okforeq[:] + okfor[OGE] = okforcmp[:] + okfor[OGT] = okforcmp[:] + okfor[OLE] = okforcmp[:] + okfor[OLT] = okforcmp[:] + okfor[OMOD] = okforand[:] + okfor[OMUL] = okforarith[:] + okfor[ONE] = okforeq[:] + okfor[OOR] = okforand[:] + okfor[OOROR] = okforbool[:] + okfor[OSUB] = okforarith[:] + okfor[OXOR] = okforand[:] + okfor[OLSH] = okforand[:] + okfor[ORSH] = okforand[:] + + // unary + okfor[OCOM] = okforand[:] + + okfor[OMINUS] = okforarith[:] + okfor[ONOT] = okforbool[:] + okfor[OPLUS] = okforarith[:] + + // special + okfor[OCAP] = okforcap[:] + + okfor[OLEN] = okforlen[:] + + // comparison + iscmp[OLT] = true + + iscmp[OGT] = true + iscmp[OGE] = true + iscmp[OLE] = true + iscmp[OEQ] = true + iscmp[ONE] = true + + mpatofix(Maxintval[TINT8], "0x7f") + mpatofix(Minintval[TINT8], "-0x80") + mpatofix(Maxintval[TINT16], "0x7fff") + mpatofix(Minintval[TINT16], "-0x8000") + mpatofix(Maxintval[TINT32], "0x7fffffff") + mpatofix(Minintval[TINT32], "-0x80000000") + mpatofix(Maxintval[TINT64], "0x7fffffffffffffff") + mpatofix(Minintval[TINT64], "-0x8000000000000000") + + mpatofix(Maxintval[TUINT8], "0xff") + mpatofix(Maxintval[TUINT16], "0xffff") + mpatofix(Maxintval[TUINT32], "0xffffffff") + mpatofix(Maxintval[TUINT64], "0xffffffffffffffff") + + /* f is valid float if min < f < max. (min and max are not themselves valid.) */ + mpatoflt(maxfltval[TFLOAT32], "33554431p103") /* 2^24-1 p (127-23) + 1/2 ulp*/ + mpatoflt(minfltval[TFLOAT32], "-33554431p103") + mpatoflt(maxfltval[TFLOAT64], "18014398509481983p970") /* 2^53-1 p (1023-52) + 1/2 ulp */ + mpatoflt(minfltval[TFLOAT64], "-18014398509481983p970") + + maxfltval[TCOMPLEX64] = maxfltval[TFLOAT32] + minfltval[TCOMPLEX64] = minfltval[TFLOAT32] + maxfltval[TCOMPLEX128] = maxfltval[TFLOAT64] + minfltval[TCOMPLEX128] = minfltval[TFLOAT64] + + /* for walk to use in error messages */ + Types[TFUNC] = functype(nil, nil, nil) + + /* types used in front end */ + // types[TNIL] got set early in lexinit + Types[TIDEAL] = typ(TIDEAL) + + Types[TINTER] = typ(TINTER) + + /* simple aliases */ + Simtype[TMAP] = uint8(Tptr) + + Simtype[TCHAN] = uint8(Tptr) + Simtype[TFUNC] = uint8(Tptr) + Simtype[TUNSAFEPTR] = uint8(Tptr) + + /* pick up the backend thearch.typedefs */ + var s1 *Sym + var etype int + var sameas int + var s *Sym + for i = range Thearch.Typedefs { + s = Lookup(Thearch.Typedefs[i].Name) + s1 = Pkglookup(Thearch.Typedefs[i].Name, builtinpkg) + + etype = Thearch.Typedefs[i].Etype + if etype < 0 || etype >= len(Types) { + Fatal("typeinit: %s bad etype", s.Name) + } + sameas = Thearch.Typedefs[i].Sameas + if sameas < 0 || sameas >= len(Types) { + Fatal("typeinit: %s bad sameas", s.Name) + } + Simtype[etype] = uint8(sameas) + minfltval[etype] = minfltval[sameas] + maxfltval[etype] = maxfltval[sameas] + Minintval[etype] = Minintval[sameas] + Maxintval[etype] = Maxintval[sameas] + + t = Types[etype] + if t != nil { + Fatal("typeinit: %s already defined", s.Name) + } + + t = typ(etype) + t.Sym = s1 + + dowidth(t) + Types[etype] = t + s1.Def = typenod(t) + } + + Array_array = int(Rnd(0, int64(Widthptr))) + Array_nel = int(Rnd(int64(Array_array)+int64(Widthptr), int64(Widthint))) + Array_cap = int(Rnd(int64(Array_nel)+int64(Widthint), int64(Widthint))) + sizeof_Array = int(Rnd(int64(Array_cap)+int64(Widthint), int64(Widthptr))) + + // string is same as slice wo the cap + sizeof_String = int(Rnd(int64(Array_nel)+int64(Widthint), int64(Widthptr))) + + dowidth(Types[TSTRING]) + dowidth(idealstring) + + itable = typ(Tptr) + itable.Type = Types[TUINT8] +} + +/* + * compute total size of f's in/out arguments. + */ +func Argsize(t *Type) int { + var save Iter + var x int64 + + w := int64(0) + + fp := Structfirst(&save, Getoutarg(t)) + for fp != nil { + x = fp.Width + fp.Type.Width + if x > w { + w = x + } + fp = structnext(&save) + } + + fp = funcfirst(&save, t) + for fp != nil { + x = fp.Width + fp.Type.Width + if x > w { + w = x + } + fp = funcnext(&save) + } + + w = (w + int64(Widthptr) - 1) &^ (int64(Widthptr) - 1) + if int64(int(w)) != w { + Fatal("argsize too big") + } + return int(w) +} diff --git a/src/cmd/compile/internal/gc/builtin.go b/src/cmd/compile/internal/gc/builtin.go new file mode 100644 index 0000000000..6bdf78c8ab --- /dev/null +++ b/src/cmd/compile/internal/gc/builtin.go @@ -0,0 +1,168 @@ +// AUTO-GENERATED by mkbuiltin.go; DO NOT EDIT + +package gc + +const runtimeimport = "" + + "package runtime\n" + + "import runtime \"runtime\"\n" + + "func @\"\".newobject (@\"\".typ·2 *byte) (? *any)\n" + + "func @\"\".panicindex ()\n" + + "func @\"\".panicslice ()\n" + + "func @\"\".panicdivide ()\n" + + "func @\"\".throwreturn ()\n" + + "func @\"\".throwinit ()\n" + + "func @\"\".panicwrap (? string, ? string, ? string)\n" + + "func @\"\".gopanic (? interface {})\n" + + "func @\"\".gorecover (? *int32) (? interface {})\n" + + "func @\"\".printbool (? bool)\n" + + "func @\"\".printfloat (? float64)\n" + + "func @\"\".printint (? int64)\n" + + "func @\"\".printhex (? uint64)\n" + + "func @\"\".printuint (? uint64)\n" + + "func @\"\".printcomplex (? complex128)\n" + + "func @\"\".printstring (? string)\n" + + "func @\"\".printpointer (? any)\n" + + "func @\"\".printiface (? any)\n" + + "func @\"\".printeface (? any)\n" + + "func @\"\".printslice (? any)\n" + + "func @\"\".printnl ()\n" + + "func @\"\".printsp ()\n" + + "func @\"\".printlock ()\n" + + "func @\"\".printunlock ()\n" + + "func @\"\".concatstring2 (? *[32]byte, ? string, ? string) (? string)\n" + + "func @\"\".concatstring3 (? *[32]byte, ? string, ? string, ? string) (? string)\n" + + "func @\"\".concatstring4 (? *[32]byte, ? string, ? string, ? string, ? string) (? string)\n" + + "func @\"\".concatstring5 (? *[32]byte, ? string, ? string, ? string, ? string, ? string) (? string)\n" + + "func @\"\".concatstrings (? *[32]byte, ? []string) (? string)\n" + + "func @\"\".cmpstring (? string, ? string) (? int)\n" + + "func @\"\".eqstring (? string, ? string) (? bool)\n" + + "func @\"\".intstring (? *[4]byte, ? int64) (? string)\n" + + "func @\"\".slicebytetostring (? *[32]byte, ? []byte) (? string)\n" + + "func @\"\".slicebytetostringtmp (? []byte) (? string)\n" + + "func @\"\".slicerunetostring (? *[32]byte, ? []rune) (? string)\n" + + "func @\"\".stringtoslicebyte (? *[32]byte, ? string) (? []byte)\n" + + "func @\"\".stringtoslicebytetmp (? string) (? []byte)\n" + + "func @\"\".stringtoslicerune (? *[32]rune, ? string) (? []rune)\n" + + "func @\"\".stringiter (? string, ? int) (? int)\n" + + "func @\"\".stringiter2 (? string, ? int) (@\"\".retk·1 int, @\"\".retv·2 rune)\n" + + "func @\"\".slicecopy (@\"\".to·2 any, @\"\".fr·3 any, @\"\".wid·4 uintptr) (? int)\n" + + "func @\"\".slicestringcopy (@\"\".to·2 any, @\"\".fr·3 any) (? int)\n" + + "func @\"\".typ2Itab (@\"\".typ·2 *byte, @\"\".typ2·3 *byte, @\"\".cache·4 **byte) (@\"\".ret·1 *byte)\n" + + "func @\"\".convI2E (@\"\".elem·2 any) (@\"\".ret·1 any)\n" + + "func @\"\".convI2I (@\"\".typ·2 *byte, @\"\".elem·3 any) (@\"\".ret·1 any)\n" + + "func @\"\".convT2E (@\"\".typ·2 *byte, @\"\".elem·3 *any, @\"\".buf·4 *any) (@\"\".ret·1 any)\n" + + "func @\"\".convT2I (@\"\".typ·2 *byte, @\"\".typ2·3 *byte, @\"\".cache·4 **byte, @\"\".elem·5 *any, @\"\".buf·6 *any) (@\"\".ret·1 any)\n" + + "func @\"\".assertE2E (@\"\".typ·1 *byte, @\"\".iface·2 any, @\"\".ret·3 *any)\n" + + "func @\"\".assertE2E2 (@\"\".typ·2 *byte, @\"\".iface·3 any, @\"\".ret·4 *any) (? bool)\n" + + "func @\"\".assertE2I (@\"\".typ·1 *byte, @\"\".iface·2 any, @\"\".ret·3 *any)\n" + + "func @\"\".assertE2I2 (@\"\".typ·2 *byte, @\"\".iface·3 any, @\"\".ret·4 *any) (? bool)\n" + + "func @\"\".assertE2T (@\"\".typ·1 *byte, @\"\".iface·2 any, @\"\".ret·3 *any)\n" + + "func @\"\".assertE2T2 (@\"\".typ·2 *byte, @\"\".iface·3 any, @\"\".ret·4 *any) (? bool)\n" + + "func @\"\".assertI2E (@\"\".typ·1 *byte, @\"\".iface·2 any, @\"\".ret·3 *any)\n" + + "func @\"\".assertI2E2 (@\"\".typ·2 *byte, @\"\".iface·3 any, @\"\".ret·4 *any) (? bool)\n" + + "func @\"\".assertI2I (@\"\".typ·1 *byte, @\"\".iface·2 any, @\"\".ret·3 *any)\n" + + "func @\"\".assertI2I2 (@\"\".typ·2 *byte, @\"\".iface·3 any, @\"\".ret·4 *any) (? bool)\n" + + "func @\"\".assertI2T (@\"\".typ·1 *byte, @\"\".iface·2 any, @\"\".ret·3 *any)\n" + + "func @\"\".assertI2T2 (@\"\".typ·2 *byte, @\"\".iface·3 any, @\"\".ret·4 *any) (? bool)\n" + + "func @\"\".panicdottype (@\"\".have·1 *byte, @\"\".want·2 *byte, @\"\".iface·3 *byte)\n" + + "func @\"\".ifaceeq (@\"\".i1·2 any, @\"\".i2·3 any) (@\"\".ret·1 bool)\n" + + "func @\"\".efaceeq (@\"\".i1·2 any, @\"\".i2·3 any) (@\"\".ret·1 bool)\n" + + "func @\"\".ifacethash (@\"\".i1·2 any) (@\"\".ret·1 uint32)\n" + + "func @\"\".efacethash (@\"\".i1·2 any) (@\"\".ret·1 uint32)\n" + + "func @\"\".makemap (@\"\".mapType·2 *byte, @\"\".hint·3 int64, @\"\".mapbuf·4 *any, @\"\".bucketbuf·5 *any) (@\"\".hmap·1 map[any]any)\n" + + "func @\"\".mapaccess1 (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 *any) (@\"\".val·1 *any)\n" + + "func @\"\".mapaccess1_fast32 (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 any) (@\"\".val·1 *any)\n" + + "func @\"\".mapaccess1_fast64 (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 any) (@\"\".val·1 *any)\n" + + "func @\"\".mapaccess1_faststr (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 any) (@\"\".val·1 *any)\n" + + "func @\"\".mapaccess2 (@\"\".mapType·3 *byte, @\"\".hmap·4 map[any]any, @\"\".key·5 *any) (@\"\".val·1 *any, @\"\".pres·2 bool)\n" + + "func @\"\".mapaccess2_fast32 (@\"\".mapType·3 *byte, @\"\".hmap·4 map[any]any, @\"\".key·5 any) (@\"\".val·1 *any, @\"\".pres·2 bool)\n" + + "func @\"\".mapaccess2_fast64 (@\"\".mapType·3 *byte, @\"\".hmap·4 map[any]any, @\"\".key·5 any) (@\"\".val·1 *any, @\"\".pres·2 bool)\n" + + "func @\"\".mapaccess2_faststr (@\"\".mapType·3 *byte, @\"\".hmap·4 map[any]any, @\"\".key·5 any) (@\"\".val·1 *any, @\"\".pres·2 bool)\n" + + "func @\"\".mapassign1 (@\"\".mapType·1 *byte, @\"\".hmap·2 map[any]any, @\"\".key·3 *any, @\"\".val·4 *any)\n" + + "func @\"\".mapiterinit (@\"\".mapType·1 *byte, @\"\".hmap·2 map[any]any, @\"\".hiter·3 *any)\n" + + "func @\"\".mapdelete (@\"\".mapType·1 *byte, @\"\".hmap·2 map[any]any, @\"\".key·3 *any)\n" + + "func @\"\".mapiternext (@\"\".hiter·1 *any)\n" + + "func @\"\".makechan (@\"\".chanType·2 *byte, @\"\".hint·3 int64) (@\"\".hchan·1 chan any)\n" + + "func @\"\".chanrecv1 (@\"\".chanType·1 *byte, @\"\".hchan·2 <-chan any, @\"\".elem·3 *any)\n" + + "func @\"\".chanrecv2 (@\"\".chanType·2 *byte, @\"\".hchan·3 <-chan any, @\"\".elem·4 *any) (? bool)\n" + + "func @\"\".chansend1 (@\"\".chanType·1 *byte, @\"\".hchan·2 chan<- any, @\"\".elem·3 *any)\n" + + "func @\"\".closechan (@\"\".hchan·1 any)\n" + + "var @\"\".writeBarrierEnabled bool\n" + + "func @\"\".writebarrierptr (@\"\".dst·1 *any, @\"\".src·2 any)\n" + + "func @\"\".writebarrierstring (@\"\".dst·1 *any, @\"\".src·2 any)\n" + + "func @\"\".writebarrierslice (@\"\".dst·1 *any, @\"\".src·2 any)\n" + + "func @\"\".writebarrieriface (@\"\".dst·1 *any, @\"\".src·2 any)\n" + + "func @\"\".writebarrierfat01 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat10 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat11 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat001 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat010 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat011 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat100 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat101 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat110 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat111 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat0001 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat0010 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat0011 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat0100 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat0101 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat0110 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat0111 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat1000 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat1001 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat1010 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat1011 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat1100 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat1101 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat1110 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + + "func @\"\".writebarrierfat1111 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + + "func @\"\".typedmemmove (@\"\".typ·1 *byte, @\"\".dst·2 *any, @\"\".src·3 *any)\n" + + "func @\"\".typedslicecopy (@\"\".typ·2 *byte, @\"\".dst·3 any, @\"\".src·4 any) (? int)\n" + + "func @\"\".selectnbsend (@\"\".chanType·2 *byte, @\"\".hchan·3 chan<- any, @\"\".elem·4 *any) (? bool)\n" + + "func @\"\".selectnbrecv (@\"\".chanType·2 *byte, @\"\".elem·3 *any, @\"\".hchan·4 <-chan any) (? bool)\n" + + "func @\"\".selectnbrecv2 (@\"\".chanType·2 *byte, @\"\".elem·3 *any, @\"\".received·4 *bool, @\"\".hchan·5 <-chan any) (? bool)\n" + + "func @\"\".newselect (@\"\".sel·1 *byte, @\"\".selsize·2 int64, @\"\".size·3 int32)\n" + + "func @\"\".selectsend (@\"\".sel·2 *byte, @\"\".hchan·3 chan<- any, @\"\".elem·4 *any) (@\"\".selected·1 bool)\n" + + "func @\"\".selectrecv (@\"\".sel·2 *byte, @\"\".hchan·3 <-chan any, @\"\".elem·4 *any) (@\"\".selected·1 bool)\n" + + "func @\"\".selectrecv2 (@\"\".sel·2 *byte, @\"\".hchan·3 <-chan any, @\"\".elem·4 *any, @\"\".received·5 *bool) (@\"\".selected·1 bool)\n" + + "func @\"\".selectdefault (@\"\".sel·2 *byte) (@\"\".selected·1 bool)\n" + + "func @\"\".selectgo (@\"\".sel·1 *byte)\n" + + "func @\"\".block ()\n" + + "func @\"\".makeslice (@\"\".typ·2 *byte, @\"\".nel·3 int64, @\"\".cap·4 int64) (@\"\".ary·1 []any)\n" + + "func @\"\".growslice (@\"\".typ·2 *byte, @\"\".old·3 []any, @\"\".n·4 int) (@\"\".ary·1 []any)\n" + + "func @\"\".memmove (@\"\".to·1 *any, @\"\".frm·2 *any, @\"\".length·3 uintptr)\n" + + "func @\"\".memclr (@\"\".ptr·1 *byte, @\"\".length·2 uintptr)\n" + + "func @\"\".memequal (@\"\".x·2 *any, @\"\".y·3 *any, @\"\".size·4 uintptr) (? bool)\n" + + "func @\"\".memequal8 (@\"\".x·2 *any, @\"\".y·3 *any) (? bool)\n" + + "func @\"\".memequal16 (@\"\".x·2 *any, @\"\".y·3 *any) (? bool)\n" + + "func @\"\".memequal32 (@\"\".x·2 *any, @\"\".y·3 *any) (? bool)\n" + + "func @\"\".memequal64 (@\"\".x·2 *any, @\"\".y·3 *any) (? bool)\n" + + "func @\"\".memequal128 (@\"\".x·2 *any, @\"\".y·3 *any) (? bool)\n" + + "func @\"\".int64div (? int64, ? int64) (? int64)\n" + + "func @\"\".uint64div (? uint64, ? uint64) (? uint64)\n" + + "func @\"\".int64mod (? int64, ? int64) (? int64)\n" + + "func @\"\".uint64mod (? uint64, ? uint64) (? uint64)\n" + + "func @\"\".float64toint64 (? float64) (? int64)\n" + + "func @\"\".float64touint64 (? float64) (? uint64)\n" + + "func @\"\".int64tofloat64 (? int64) (? float64)\n" + + "func @\"\".uint64tofloat64 (? uint64) (? float64)\n" + + "func @\"\".complex128div (@\"\".num·2 complex128, @\"\".den·3 complex128) (@\"\".quo·1 complex128)\n" + + "func @\"\".racefuncenter (? uintptr)\n" + + "func @\"\".racefuncexit ()\n" + + "func @\"\".raceread (? uintptr)\n" + + "func @\"\".racewrite (? uintptr)\n" + + "func @\"\".racereadrange (@\"\".addr·1 uintptr, @\"\".size·2 uintptr)\n" + + "func @\"\".racewriterange (@\"\".addr·1 uintptr, @\"\".size·2 uintptr)\n" + + "\n" + + "$$\n" + +const unsafeimport = "" + + "package unsafe\n" + + "import runtime \"runtime\"\n" + + "type @\"\".Pointer uintptr\n" + + "func @\"\".Offsetof (? any) (? uintptr)\n" + + "func @\"\".Sizeof (? any) (? uintptr)\n" + + "func @\"\".Alignof (? any) (? uintptr)\n" + + "\n" + + "$$\n" diff --git a/src/cmd/compile/internal/gc/builtin/runtime.go b/src/cmd/compile/internal/gc/builtin/runtime.go new file mode 100644 index 0000000000..179a4ddd9a --- /dev/null +++ b/src/cmd/compile/internal/gc/builtin/runtime.go @@ -0,0 +1,194 @@ +// Copyright 2009 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. + +// NOTE: If you change this file you must run "go generate" +// to update builtin.go. This is not done automatically +// to avoid depending on having a working compiler binary. + +// +build ignore + +package PACKAGE + +// emitted by compiler, not referred to by go programs + +func newobject(typ *byte) *any +func panicindex() +func panicslice() +func panicdivide() +func throwreturn() +func throwinit() +func panicwrap(string, string, string) + +func gopanic(interface{}) +func gorecover(*int32) interface{} + +func printbool(bool) +func printfloat(float64) +func printint(int64) +func printhex(uint64) +func printuint(uint64) +func printcomplex(complex128) +func printstring(string) +func printpointer(any) +func printiface(any) +func printeface(any) +func printslice(any) +func printnl() +func printsp() +func printlock() +func printunlock() + +func concatstring2(*[32]byte, string, string) string +func concatstring3(*[32]byte, string, string, string) string +func concatstring4(*[32]byte, string, string, string, string) string +func concatstring5(*[32]byte, string, string, string, string, string) string +func concatstrings(*[32]byte, []string) string + +func cmpstring(string, string) int +func eqstring(string, string) bool +func intstring(*[4]byte, int64) string +func slicebytetostring(*[32]byte, []byte) string +func slicebytetostringtmp([]byte) string +func slicerunetostring(*[32]byte, []rune) string +func stringtoslicebyte(*[32]byte, string) []byte +func stringtoslicebytetmp(string) []byte +func stringtoslicerune(*[32]rune, string) []rune +func stringiter(string, int) int +func stringiter2(string, int) (retk int, retv rune) +func slicecopy(to any, fr any, wid uintptr) int +func slicestringcopy(to any, fr any) int + +// interface conversions +func typ2Itab(typ *byte, typ2 *byte, cache **byte) (ret *byte) +func convI2E(elem any) (ret any) +func convI2I(typ *byte, elem any) (ret any) +func convT2E(typ *byte, elem, buf *any) (ret any) +func convT2I(typ *byte, typ2 *byte, cache **byte, elem, buf *any) (ret any) + +// interface type assertions x.(T) +func assertE2E(typ *byte, iface any, ret *any) +func assertE2E2(typ *byte, iface any, ret *any) bool +func assertE2I(typ *byte, iface any, ret *any) +func assertE2I2(typ *byte, iface any, ret *any) bool +func assertE2T(typ *byte, iface any, ret *any) +func assertE2T2(typ *byte, iface any, ret *any) bool +func assertI2E(typ *byte, iface any, ret *any) +func assertI2E2(typ *byte, iface any, ret *any) bool +func assertI2I(typ *byte, iface any, ret *any) +func assertI2I2(typ *byte, iface any, ret *any) bool +func assertI2T(typ *byte, iface any, ret *any) +func assertI2T2(typ *byte, iface any, ret *any) bool +func panicdottype(have, want, iface *byte) + +func ifaceeq(i1 any, i2 any) (ret bool) +func efaceeq(i1 any, i2 any) (ret bool) +func ifacethash(i1 any) (ret uint32) +func efacethash(i1 any) (ret uint32) + +// *byte is really *runtime.Type +func makemap(mapType *byte, hint int64, mapbuf *any, bucketbuf *any) (hmap map[any]any) +func mapaccess1(mapType *byte, hmap map[any]any, key *any) (val *any) +func mapaccess1_fast32(mapType *byte, hmap map[any]any, key any) (val *any) +func mapaccess1_fast64(mapType *byte, hmap map[any]any, key any) (val *any) +func mapaccess1_faststr(mapType *byte, hmap map[any]any, key any) (val *any) +func mapaccess2(mapType *byte, hmap map[any]any, key *any) (val *any, pres bool) +func mapaccess2_fast32(mapType *byte, hmap map[any]any, key any) (val *any, pres bool) +func mapaccess2_fast64(mapType *byte, hmap map[any]any, key any) (val *any, pres bool) +func mapaccess2_faststr(mapType *byte, hmap map[any]any, key any) (val *any, pres bool) +func mapassign1(mapType *byte, hmap map[any]any, key *any, val *any) +func mapiterinit(mapType *byte, hmap map[any]any, hiter *any) +func mapdelete(mapType *byte, hmap map[any]any, key *any) +func mapiternext(hiter *any) + +// *byte is really *runtime.Type +func makechan(chanType *byte, hint int64) (hchan chan any) +func chanrecv1(chanType *byte, hchan <-chan any, elem *any) +func chanrecv2(chanType *byte, hchan <-chan any, elem *any) bool +func chansend1(chanType *byte, hchan chan<- any, elem *any) +func closechan(hchan any) + +var writeBarrierEnabled bool + +func writebarrierptr(dst *any, src any) +func writebarrierstring(dst *any, src any) +func writebarrierslice(dst *any, src any) +func writebarrieriface(dst *any, src any) + +// The unused *byte argument makes sure that src is 2-pointer-aligned, +// which is the maximum alignment on NaCl amd64p32 +// (and possibly on 32-bit systems if we start 64-bit aligning uint64s). +// The bitmap in the name tells which words being copied are pointers. +func writebarrierfat01(dst *any, _ uintptr, src any) +func writebarrierfat10(dst *any, _ uintptr, src any) +func writebarrierfat11(dst *any, _ uintptr, src any) +func writebarrierfat001(dst *any, _ uintptr, src any) +func writebarrierfat010(dst *any, _ uintptr, src any) +func writebarrierfat011(dst *any, _ uintptr, src any) +func writebarrierfat100(dst *any, _ uintptr, src any) +func writebarrierfat101(dst *any, _ uintptr, src any) +func writebarrierfat110(dst *any, _ uintptr, src any) +func writebarrierfat111(dst *any, _ uintptr, src any) +func writebarrierfat0001(dst *any, _ uintptr, src any) +func writebarrierfat0010(dst *any, _ uintptr, src any) +func writebarrierfat0011(dst *any, _ uintptr, src any) +func writebarrierfat0100(dst *any, _ uintptr, src any) +func writebarrierfat0101(dst *any, _ uintptr, src any) +func writebarrierfat0110(dst *any, _ uintptr, src any) +func writebarrierfat0111(dst *any, _ uintptr, src any) +func writebarrierfat1000(dst *any, _ uintptr, src any) +func writebarrierfat1001(dst *any, _ uintptr, src any) +func writebarrierfat1010(dst *any, _ uintptr, src any) +func writebarrierfat1011(dst *any, _ uintptr, src any) +func writebarrierfat1100(dst *any, _ uintptr, src any) +func writebarrierfat1101(dst *any, _ uintptr, src any) +func writebarrierfat1110(dst *any, _ uintptr, src any) +func writebarrierfat1111(dst *any, _ uintptr, src any) + +// *byte is really *runtime.Type +func typedmemmove(typ *byte, dst *any, src *any) +func typedslicecopy(typ *byte, dst any, src any) int + +func selectnbsend(chanType *byte, hchan chan<- any, elem *any) bool +func selectnbrecv(chanType *byte, elem *any, hchan <-chan any) bool +func selectnbrecv2(chanType *byte, elem *any, received *bool, hchan <-chan any) bool + +func newselect(sel *byte, selsize int64, size int32) +func selectsend(sel *byte, hchan chan<- any, elem *any) (selected bool) +func selectrecv(sel *byte, hchan <-chan any, elem *any) (selected bool) +func selectrecv2(sel *byte, hchan <-chan any, elem *any, received *bool) (selected bool) +func selectdefault(sel *byte) (selected bool) +func selectgo(sel *byte) +func block() + +func makeslice(typ *byte, nel int64, cap int64) (ary []any) +func growslice(typ *byte, old []any, n int) (ary []any) +func memmove(to *any, frm *any, length uintptr) +func memclr(ptr *byte, length uintptr) + +func memequal(x, y *any, size uintptr) bool +func memequal8(x, y *any) bool +func memequal16(x, y *any) bool +func memequal32(x, y *any) bool +func memequal64(x, y *any) bool +func memequal128(x, y *any) bool + +// only used on 32-bit +func int64div(int64, int64) int64 +func uint64div(uint64, uint64) uint64 +func int64mod(int64, int64) int64 +func uint64mod(uint64, uint64) uint64 +func float64toint64(float64) int64 +func float64touint64(float64) uint64 +func int64tofloat64(int64) float64 +func uint64tofloat64(uint64) float64 + +func complex128div(num complex128, den complex128) (quo complex128) + +// race detection +func racefuncenter(uintptr) +func racefuncexit() +func raceread(uintptr) +func racewrite(uintptr) +func racereadrange(addr, size uintptr) +func racewriterange(addr, size uintptr) diff --git a/src/cmd/compile/internal/gc/builtin/unsafe.go b/src/cmd/compile/internal/gc/builtin/unsafe.go new file mode 100644 index 0000000000..ce508692eb --- /dev/null +++ b/src/cmd/compile/internal/gc/builtin/unsafe.go @@ -0,0 +1,18 @@ +// Copyright 2009 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. + +// NOTE: If you change this file you must run "go generate" +// to update builtin.go. This is not done automatically +// to avoid depending on having a working compiler binary. + +// +build ignore + +package PACKAGE + +type Pointer uintptr // not really; filled in by compiler + +// return types here are ignored; see unsafe.go +func Offsetof(any) uintptr +func Sizeof(any) uintptr +func Alignof(any) uintptr diff --git a/src/cmd/compile/internal/gc/bv.go b/src/cmd/compile/internal/gc/bv.go new file mode 100644 index 0000000000..2b988e6547 --- /dev/null +++ b/src/cmd/compile/internal/gc/bv.go @@ -0,0 +1,195 @@ +// Copyright 2013 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 gc + +import "fmt" + +const ( + WORDSIZE = 4 + WORDBITS = 32 + WORDMASK = WORDBITS - 1 + WORDSHIFT = 5 +) + +// A Bvec is a bit vector. +type Bvec struct { + n int32 // number of bits in vector + b []uint32 // words holding bits +} + +func bvsize(n uint32) uint32 { + return ((n + WORDBITS - 1) / WORDBITS) * WORDSIZE +} + +func bvbits(bv Bvec) int32 { + return bv.n +} + +func bvwords(bv Bvec) int32 { + return (bv.n + WORDBITS - 1) / WORDBITS +} + +func bvalloc(n int32) Bvec { + return Bvec{n, make([]uint32, bvsize(uint32(n))/4)} +} + +type bulkBvec struct { + words []uint32 + nbit int32 + nword int32 +} + +func bvbulkalloc(nbit int32, count int32) bulkBvec { + nword := (nbit + WORDBITS - 1) / WORDBITS + return bulkBvec{ + words: make([]uint32, nword*count), + nbit: nbit, + nword: nword, + } +} + +func (b *bulkBvec) next() Bvec { + out := Bvec{b.nbit, b.words[:b.nword]} + b.words = b.words[b.nword:] + return out +} + +/* difference */ +func bvandnot(dst Bvec, src1 Bvec, src2 Bvec) { + for i, x := range src1.b { + dst.b[i] = x &^ src2.b[i] + } +} + +func bvcmp(bv1 Bvec, bv2 Bvec) int { + if bv1.n != bv2.n { + Fatal("bvequal: lengths %d and %d are not equal", bv1.n, bv2.n) + } + for i, x := range bv1.b { + if x != bv2.b[i] { + return 1 + } + } + return 0 +} + +func bvcopy(dst Bvec, src Bvec) { + for i, x := range src.b { + dst.b[i] = x + } +} + +func bvconcat(src1 Bvec, src2 Bvec) Bvec { + dst := bvalloc(src1.n + src2.n) + for i := int32(0); i < src1.n; i++ { + if bvget(src1, i) != 0 { + bvset(dst, i) + } + } + for i := int32(0); i < src2.n; i++ { + if bvget(src2, i) != 0 { + bvset(dst, i+src1.n) + } + } + return dst +} + +func bvget(bv Bvec, i int32) int { + if i < 0 || i >= bv.n { + Fatal("bvget: index %d is out of bounds with length %d\n", i, bv.n) + } + return int((bv.b[i>>WORDSHIFT] >> uint(i&WORDMASK)) & 1) +} + +// bvnext returns the smallest index >= i for which bvget(bv, i) == 1. +// If there is no such index, bvnext returns -1. +func bvnext(bv Bvec, i int32) int { + if i >= bv.n { + return -1 + } + + // Jump i ahead to next word with bits. + if bv.b[i>>WORDSHIFT]>>uint(i&WORDMASK) == 0 { + i &^= WORDMASK + i += WORDBITS + for i < bv.n && bv.b[i>>WORDSHIFT] == 0 { + i += WORDBITS + } + } + + if i >= bv.n { + return -1 + } + + // Find 1 bit. + w := bv.b[i>>WORDSHIFT] >> uint(i&WORDMASK) + + for w&1 == 0 { + w >>= 1 + i++ + } + + return int(i) +} + +func bvisempty(bv Bvec) bool { + for i := int32(0); i < bv.n; i += WORDBITS { + if bv.b[i>>WORDSHIFT] != 0 { + return false + } + } + return true +} + +func bvnot(bv Bvec) { + i := int32(0) + w := int32(0) + for ; i < bv.n; i, w = i+WORDBITS, w+1 { + bv.b[w] = ^bv.b[w] + } +} + +/* union */ +func bvor(dst Bvec, src1 Bvec, src2 Bvec) { + for i, x := range src1.b { + dst.b[i] = x | src2.b[i] + } +} + +/* intersection */ +func bvand(dst Bvec, src1 Bvec, src2 Bvec) { + for i, x := range src1.b { + dst.b[i] = x & src2.b[i] + } +} + +func bvprint(bv Bvec) { + fmt.Printf("#*") + for i := int32(0); i < bv.n; i++ { + fmt.Printf("%d", bvget(bv, i)) + } +} + +func bvreset(bv Bvec, i int32) { + if i < 0 || i >= bv.n { + Fatal("bvreset: index %d is out of bounds with length %d\n", i, bv.n) + } + mask := uint32(^(1 << uint(i%WORDBITS))) + bv.b[i/WORDBITS] &= mask +} + +func bvresetall(bv Bvec) { + for i := range bv.b { + bv.b[i] = 0 + } +} + +func bvset(bv Bvec, i int32) { + if i < 0 || i >= bv.n { + Fatal("bvset: index %d is out of bounds with length %d\n", i, bv.n) + } + mask := uint32(1 << uint(i%WORDBITS)) + bv.b[i/WORDBITS] |= mask +} diff --git a/src/cmd/compile/internal/gc/cgen.go b/src/cmd/compile/internal/gc/cgen.go new file mode 100644 index 0000000000..ca58b1c6a3 --- /dev/null +++ b/src/cmd/compile/internal/gc/cgen.go @@ -0,0 +1,3564 @@ +// Copyright 2009 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 gc + +import ( + "cmd/internal/obj" + "fmt" +) + +/* + * generate: + * res = n; + * simplifies and calls Thearch.Gmove. + * if wb is true, need to emit write barriers. + */ +func Cgen(n, res *Node) { + cgen_wb(n, res, false) +} + +func cgen_wb(n, res *Node, wb bool) { + if Debug['g'] != 0 { + op := "cgen" + if wb { + op = "cgen_wb" + } + Dump("\n"+op+"-n", n) + Dump(op+"-res", res) + } + + if n == nil || n.Type == nil { + return + } + + if res == nil || res.Type == nil { + Fatal("cgen: res nil") + } + + for n.Op == OCONVNOP { + n = n.Left + } + + switch n.Op { + case OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR: + cgen_slice(n, res, wb) + return + + case OEFACE: + if res.Op != ONAME || !res.Addable || wb { + var n1 Node + Tempname(&n1, n.Type) + Cgen_eface(n, &n1) + cgen_wb(&n1, res, wb) + } else { + Cgen_eface(n, res) + } + return + + case ODOTTYPE: + cgen_dottype(n, res, nil, wb) + return + + case OAPPEND: + cgen_append(n, res) + return + } + + if n.Ullman >= UINF { + if n.Op == OINDREG { + Fatal("cgen: this is going to miscompile") + } + if res.Ullman >= UINF { + var n1 Node + Tempname(&n1, n.Type) + Cgen(n, &n1) + cgen_wb(&n1, res, wb) + return + } + } + + if Isfat(n.Type) { + if n.Type.Width < 0 { + Fatal("forgot to compute width for %v", n.Type) + } + sgen_wb(n, res, n.Type.Width, wb) + return + } + + if !res.Addable { + if n.Ullman > res.Ullman { + if Ctxt.Arch.Regsize == 4 && Is64(n.Type) { + var n1 Node + Tempname(&n1, n.Type) + Cgen(n, &n1) + cgen_wb(&n1, res, wb) + return + } + + var n1 Node + Regalloc(&n1, n.Type, res) + Cgen(n, &n1) + if n1.Ullman > res.Ullman { + Dump("n1", &n1) + Dump("res", res) + Fatal("loop in cgen") + } + + cgen_wb(&n1, res, wb) + Regfree(&n1) + return + } + + var f int + if res.Ullman < UINF { + if Complexop(n, res) { + Complexgen(n, res) + return + } + + f = 1 // gen thru register + switch n.Op { + case OLITERAL: + if Smallintconst(n) { + f = 0 + } + + case OREGISTER: + f = 0 + } + + if !Iscomplex[n.Type.Etype] && Ctxt.Arch.Regsize == 8 && !wb { + a := Thearch.Optoas(OAS, res.Type) + var addr obj.Addr + if Thearch.Sudoaddable(a, res, &addr) { + var p1 *obj.Prog + if f != 0 { + var n2 Node + Regalloc(&n2, res.Type, nil) + Cgen(n, &n2) + p1 = Thearch.Gins(a, &n2, nil) + Regfree(&n2) + } else { + p1 = Thearch.Gins(a, n, nil) + } + p1.To = addr + if Debug['g'] != 0 { + fmt.Printf("%v [ignore previous line]\n", p1) + } + Thearch.Sudoclean() + return + } + } + } + + if Ctxt.Arch.Thechar == '8' { + // no registers to speak of + var n1, n2 Node + Tempname(&n1, n.Type) + Cgen(n, &n1) + Igen(res, &n2, nil) + cgen_wb(&n1, &n2, wb) + Regfree(&n2) + return + } + + var n1 Node + Igen(res, &n1, nil) + cgen_wb(n, &n1, wb) + Regfree(&n1) + return + } + + // update addressability for string, slice + // can't do in walk because n->left->addable + // changes if n->left is an escaping local variable. + switch n.Op { + case OSPTR, OLEN: + if Isslice(n.Left.Type) || Istype(n.Left.Type, TSTRING) { + n.Addable = n.Left.Addable + } + + case OCAP: + if Isslice(n.Left.Type) { + n.Addable = n.Left.Addable + } + + case OITAB: + n.Addable = n.Left.Addable + } + + if wb { + if int(Simtype[res.Type.Etype]) != Tptr { + Fatal("cgen_wb of type %v", res.Type) + } + if n.Ullman >= UINF { + var n1 Node + Tempname(&n1, n.Type) + Cgen(n, &n1) + n = &n1 + } + cgen_wbptr(n, res) + return + } + + // Write barrier now handled. Code below this line can ignore wb. + + if Ctxt.Arch.Thechar == '5' { // TODO(rsc): Maybe more often? + // if both are addressable, move + if n.Addable && res.Addable { + if Is64(n.Type) || Is64(res.Type) || n.Op == OREGISTER || res.Op == OREGISTER || Iscomplex[n.Type.Etype] || Iscomplex[res.Type.Etype] { + Thearch.Gmove(n, res) + } else { + var n1 Node + Regalloc(&n1, n.Type, nil) + Thearch.Gmove(n, &n1) + Cgen(&n1, res) + Regfree(&n1) + } + + return + } + + // if both are not addressable, use a temporary. + if !n.Addable && !res.Addable { + // could use regalloc here sometimes, + // but have to check for ullman >= UINF. + var n1 Node + Tempname(&n1, n.Type) + Cgen(n, &n1) + Cgen(&n1, res) + return + } + + // if result is not addressable directly but n is, + // compute its address and then store via the address. + if !res.Addable { + var n1 Node + Igen(res, &n1, nil) + Cgen(n, &n1) + Regfree(&n1) + return + } + } + + if Complexop(n, res) { + Complexgen(n, res) + return + } + + if (Ctxt.Arch.Thechar == '6' || Ctxt.Arch.Thechar == '8') && n.Addable { + Thearch.Gmove(n, res) + return + } + + if Ctxt.Arch.Thechar == '7' || Ctxt.Arch.Thechar == '9' { + // if both are addressable, move + if n.Addable { + if n.Op == OREGISTER || res.Op == OREGISTER { + Thearch.Gmove(n, res) + } else { + var n1 Node + Regalloc(&n1, n.Type, nil) + Thearch.Gmove(n, &n1) + Cgen(&n1, res) + Regfree(&n1) + } + return + } + } + + // if n is sudoaddable generate addr and move + if Ctxt.Arch.Thechar == '5' && !Is64(n.Type) && !Is64(res.Type) && !Iscomplex[n.Type.Etype] && !Iscomplex[res.Type.Etype] { + a := Thearch.Optoas(OAS, n.Type) + var addr obj.Addr + if Thearch.Sudoaddable(a, n, &addr) { + if res.Op != OREGISTER { + var n2 Node + Regalloc(&n2, res.Type, nil) + p1 := Thearch.Gins(a, nil, &n2) + p1.From = addr + if Debug['g'] != 0 { + fmt.Printf("%v [ignore previous line]\n", p1) + } + Thearch.Gmove(&n2, res) + Regfree(&n2) + } else { + p1 := Thearch.Gins(a, nil, res) + p1.From = addr + if Debug['g'] != 0 { + fmt.Printf("%v [ignore previous line]\n", p1) + } + } + Thearch.Sudoclean() + return + } + } + + nl := n.Left + nr := n.Right + + if nl != nil && nl.Ullman >= UINF { + if nr != nil && nr.Ullman >= UINF { + var n1 Node + Tempname(&n1, nl.Type) + Cgen(nl, &n1) + n2 := *n + n2.Left = &n1 + Cgen(&n2, res) + return + } + } + + // 64-bit ops are hard on 32-bit machine. + if Ctxt.Arch.Regsize == 4 && (Is64(n.Type) || Is64(res.Type) || n.Left != nil && Is64(n.Left.Type)) { + switch n.Op { + // math goes to cgen64. + case OMINUS, + OCOM, + OADD, + OSUB, + OMUL, + OLROT, + OLSH, + ORSH, + OAND, + OOR, + OXOR: + Thearch.Cgen64(n, res) + return + } + } + + if Thearch.Cgen_float != nil && nl != nil && Isfloat[n.Type.Etype] && Isfloat[nl.Type.Etype] { + Thearch.Cgen_float(n, res) + return + } + + if !Iscomplex[n.Type.Etype] && Ctxt.Arch.Regsize == 8 { + a := Thearch.Optoas(OAS, n.Type) + var addr obj.Addr + if Thearch.Sudoaddable(a, n, &addr) { + if res.Op == OREGISTER { + p1 := Thearch.Gins(a, nil, res) + p1.From = addr + } else { + var n2 Node + Regalloc(&n2, n.Type, nil) + p1 := Thearch.Gins(a, nil, &n2) + p1.From = addr + Thearch.Gins(a, &n2, res) + Regfree(&n2) + } + + Thearch.Sudoclean() + return + } + } + + var a int + switch n.Op { + default: + Dump("cgen", n) + Dump("cgen-res", res) + Fatal("cgen: unknown op %v", Nconv(n, obj.FmtShort|obj.FmtSign)) + + case OOROR, OANDAND, + OEQ, ONE, + OLT, OLE, + OGE, OGT, + ONOT: + Bvgen(n, res, true) + return + + case OPLUS: + Cgen(nl, res) + return + + // unary + case OCOM: + a := Thearch.Optoas(OXOR, nl.Type) + + var n1 Node + Regalloc(&n1, nl.Type, nil) + Cgen(nl, &n1) + var n2 Node + Nodconst(&n2, nl.Type, -1) + Thearch.Gins(a, &n2, &n1) + cgen_norm(n, &n1, res) + return + + case OMINUS: + if Isfloat[nl.Type.Etype] { + nr = Nodintconst(-1) + Convlit(&nr, n.Type) + a = Thearch.Optoas(OMUL, nl.Type) + goto sbop + } + + a := Thearch.Optoas(int(n.Op), nl.Type) + // unary + var n1 Node + Regalloc(&n1, nl.Type, res) + + Cgen(nl, &n1) + if Ctxt.Arch.Thechar == '5' { + var n2 Node + Nodconst(&n2, nl.Type, 0) + Thearch.Gins(a, &n2, &n1) + } else if Ctxt.Arch.Thechar == '7' { + Thearch.Gins(a, &n1, &n1) + } else { + Thearch.Gins(a, nil, &n1) + } + cgen_norm(n, &n1, res) + return + + case OSQRT: + var n1 Node + Regalloc(&n1, nl.Type, res) + Cgen(n.Left, &n1) + Thearch.Gins(Thearch.Optoas(OSQRT, nl.Type), &n1, &n1) + Thearch.Gmove(&n1, res) + Regfree(&n1) + return + + case OGETG: + Thearch.Getg(res) + return + + // symmetric binary + case OAND, + OOR, + OXOR, + OADD, + OMUL: + if n.Op == OMUL && Thearch.Cgen_bmul != nil && Thearch.Cgen_bmul(int(n.Op), nl, nr, res) { + break + } + a = Thearch.Optoas(int(n.Op), nl.Type) + goto sbop + + // asymmetric binary + case OSUB: + a = Thearch.Optoas(int(n.Op), nl.Type) + goto abop + + case OHMUL: + Thearch.Cgen_hmul(nl, nr, res) + + case OCONV: + if Eqtype(n.Type, nl.Type) || Noconv(n.Type, nl.Type) { + Cgen(nl, res) + return + } + + if Ctxt.Arch.Thechar == '8' { + var n1 Node + var n2 Node + Tempname(&n2, n.Type) + Mgen(nl, &n1, res) + Thearch.Gmove(&n1, &n2) + Thearch.Gmove(&n2, res) + Mfree(&n1) + break + } + + var n1 Node + var n2 Node + if Ctxt.Arch.Thechar == '5' { + if nl.Addable && !Is64(nl.Type) { + Regalloc(&n1, nl.Type, res) + Thearch.Gmove(nl, &n1) + } else { + if n.Type.Width > int64(Widthptr) || Is64(nl.Type) || Isfloat[nl.Type.Etype] { + Tempname(&n1, nl.Type) + } else { + Regalloc(&n1, nl.Type, res) + } + Cgen(nl, &n1) + } + if n.Type.Width > int64(Widthptr) || Is64(n.Type) || Isfloat[n.Type.Etype] { + Tempname(&n2, n.Type) + } else { + Regalloc(&n2, n.Type, nil) + } + } else { + if n.Type.Width > nl.Type.Width { + // If loading from memory, do conversion during load, + // so as to avoid use of 8-bit register in, say, int(*byteptr). + switch nl.Op { + case ODOT, ODOTPTR, OINDEX, OIND, ONAME: + Igen(nl, &n1, res) + Regalloc(&n2, n.Type, res) + Thearch.Gmove(&n1, &n2) + Thearch.Gmove(&n2, res) + Regfree(&n2) + Regfree(&n1) + return + } + } + Regalloc(&n1, nl.Type, res) + Regalloc(&n2, n.Type, &n1) + Cgen(nl, &n1) + } + + // if we do the conversion n1 -> n2 here + // reusing the register, then gmove won't + // have to allocate its own register. + Thearch.Gmove(&n1, &n2) + Thearch.Gmove(&n2, res) + if n2.Op == OREGISTER { + Regfree(&n2) + } + if n1.Op == OREGISTER { + Regfree(&n1) + } + + case ODOT, + ODOTPTR, + OINDEX, + OIND, + ONAME: // PHEAP or PPARAMREF var + var n1 Node + Igen(n, &n1, res) + + Thearch.Gmove(&n1, res) + Regfree(&n1) + + // interface table is first word of interface value + case OITAB: + var n1 Node + Igen(nl, &n1, res) + + n1.Type = n.Type + Thearch.Gmove(&n1, res) + Regfree(&n1) + + case OSPTR: + // pointer is the first word of string or slice. + if Isconst(nl, CTSTR) { + var n1 Node + Regalloc(&n1, Types[Tptr], res) + p1 := Thearch.Gins(Thearch.Optoas(OAS, n1.Type), nil, &n1) + Datastring(nl.Val.U.(string), &p1.From) + p1.From.Type = obj.TYPE_ADDR + Thearch.Gmove(&n1, res) + Regfree(&n1) + break + } + + var n1 Node + Igen(nl, &n1, res) + n1.Type = n.Type + Thearch.Gmove(&n1, res) + Regfree(&n1) + + case OLEN: + if Istype(nl.Type, TMAP) || Istype(nl.Type, TCHAN) { + // map and chan have len in the first int-sized word. + // a zero pointer means zero length + var n1 Node + Regalloc(&n1, Types[Tptr], res) + + Cgen(nl, &n1) + + var n2 Node + Nodconst(&n2, Types[Tptr], 0) + p1 := Thearch.Ginscmp(OEQ, Types[Tptr], &n1, &n2, 0) + + n2 = n1 + n2.Op = OINDREG + n2.Type = Types[Simtype[TINT]] + Thearch.Gmove(&n2, &n1) + + Patch(p1, Pc) + + Thearch.Gmove(&n1, res) + Regfree(&n1) + break + } + + if Istype(nl.Type, TSTRING) || Isslice(nl.Type) { + // both slice and string have len one pointer into the struct. + // a zero pointer means zero length + var n1 Node + Igen(nl, &n1, res) + + n1.Type = Types[Simtype[TUINT]] + n1.Xoffset += int64(Array_nel) + Thearch.Gmove(&n1, res) + Regfree(&n1) + break + } + + Fatal("cgen: OLEN: unknown type %v", Tconv(nl.Type, obj.FmtLong)) + + case OCAP: + if Istype(nl.Type, TCHAN) { + // chan has cap in the second int-sized word. + // a zero pointer means zero length + var n1 Node + Regalloc(&n1, Types[Tptr], res) + + Cgen(nl, &n1) + + var n2 Node + Nodconst(&n2, Types[Tptr], 0) + p1 := Thearch.Ginscmp(OEQ, Types[Tptr], &n1, &n2, 0) + + n2 = n1 + n2.Op = OINDREG + n2.Xoffset = int64(Widthint) + n2.Type = Types[Simtype[TINT]] + Thearch.Gmove(&n2, &n1) + + Patch(p1, Pc) + + Thearch.Gmove(&n1, res) + Regfree(&n1) + break + } + + if Isslice(nl.Type) { + var n1 Node + Igen(nl, &n1, res) + n1.Type = Types[Simtype[TUINT]] + n1.Xoffset += int64(Array_cap) + Thearch.Gmove(&n1, res) + Regfree(&n1) + break + } + + Fatal("cgen: OCAP: unknown type %v", Tconv(nl.Type, obj.FmtLong)) + + case OADDR: + if n.Bounded { // let race detector avoid nil checks + Disable_checknil++ + } + Agen(nl, res) + if n.Bounded { + Disable_checknil-- + } + + case OCALLMETH: + cgen_callmeth(n, 0) + cgen_callret(n, res) + + case OCALLINTER: + cgen_callinter(n, res, 0) + cgen_callret(n, res) + + case OCALLFUNC: + cgen_call(n, 0) + cgen_callret(n, res) + + case OMOD, ODIV: + if Isfloat[n.Type.Etype] || Thearch.Dodiv == nil { + a = Thearch.Optoas(int(n.Op), nl.Type) + goto abop + } + + if nl.Ullman >= nr.Ullman { + var n1 Node + Regalloc(&n1, nl.Type, res) + Cgen(nl, &n1) + cgen_div(int(n.Op), &n1, nr, res) + Regfree(&n1) + } else { + var n2 Node + if !Smallintconst(nr) { + Regalloc(&n2, nr.Type, res) + Cgen(nr, &n2) + } else { + n2 = *nr + } + + cgen_div(int(n.Op), nl, &n2, res) + if n2.Op != OLITERAL { + Regfree(&n2) + } + } + + case OLSH, ORSH, OLROT: + Thearch.Cgen_shift(int(n.Op), n.Bounded, nl, nr, res) + } + + return + + /* + * put simplest on right - we'll generate into left + * and then adjust it using the computation of right. + * constants and variables have the same ullman + * count, so look for constants specially. + * + * an integer constant we can use as an immediate + * is simpler than a variable - we can use the immediate + * in the adjustment instruction directly - so it goes + * on the right. + * + * other constants, like big integers or floating point + * constants, require a mov into a register, so those + * might as well go on the left, so we can reuse that + * register for the computation. + */ +sbop: // symmetric binary + if nl.Ullman < nr.Ullman || (nl.Ullman == nr.Ullman && (Smallintconst(nl) || (nr.Op == OLITERAL && !Smallintconst(nr)))) { + r := nl + nl = nr + nr = r + } + +abop: // asymmetric binary + var n1 Node + var n2 Node + if Ctxt.Arch.Thechar == '8' { + // no registers, sigh + if Smallintconst(nr) { + var n1 Node + Mgen(nl, &n1, res) + var n2 Node + Regalloc(&n2, nl.Type, &n1) + Thearch.Gmove(&n1, &n2) + Thearch.Gins(a, nr, &n2) + Thearch.Gmove(&n2, res) + Regfree(&n2) + Mfree(&n1) + } else if nl.Ullman >= nr.Ullman { + var nt Node + Tempname(&nt, nl.Type) + Cgen(nl, &nt) + var n2 Node + Mgen(nr, &n2, nil) + var n1 Node + Regalloc(&n1, nl.Type, res) + Thearch.Gmove(&nt, &n1) + Thearch.Gins(a, &n2, &n1) + Thearch.Gmove(&n1, res) + Regfree(&n1) + Mfree(&n2) + } else { + var n2 Node + Regalloc(&n2, nr.Type, res) + Cgen(nr, &n2) + var n1 Node + Regalloc(&n1, nl.Type, nil) + Cgen(nl, &n1) + Thearch.Gins(a, &n2, &n1) + Regfree(&n2) + Thearch.Gmove(&n1, res) + Regfree(&n1) + } + return + } + + if nl.Ullman >= nr.Ullman { + Regalloc(&n1, nl.Type, res) + Cgen(nl, &n1) + + if Smallintconst(nr) && Ctxt.Arch.Thechar != '5' && Ctxt.Arch.Thechar != '7' && Ctxt.Arch.Thechar != '9' { // TODO(rsc): Check opcode for arm + n2 = *nr + } else { + Regalloc(&n2, nr.Type, nil) + Cgen(nr, &n2) + } + } else { + if Smallintconst(nr) && Ctxt.Arch.Thechar != '5' && Ctxt.Arch.Thechar != '7' && Ctxt.Arch.Thechar != '9' { // TODO(rsc): Check opcode for arm + n2 = *nr + } else { + Regalloc(&n2, nr.Type, res) + Cgen(nr, &n2) + } + + Regalloc(&n1, nl.Type, nil) + Cgen(nl, &n1) + } + + Thearch.Gins(a, &n2, &n1) + if n2.Op != OLITERAL { + Regfree(&n2) + } + cgen_norm(n, &n1, res) +} + +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") + } + + var dst, src Node + Igen(res, &dst, nil) + if n.Op == OREGISTER { + src = *n + Regrealloc(&src) + } else { + Cgenr(n, &src, nil) + } + + wbEnabled := syslook("writeBarrierEnabled", 0) + 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) + var adst Node + Agenr(&dst, &adst, &dst) + p := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &adst, nil) + a := &p.To + a.Type = obj.TYPE_MEM + a.Reg = int16(Thearch.REGSP) + a.Offset = 0 + if HasLinkRegister() { + a.Offset += int64(Widthptr) + } + p2 := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &src, nil) + p2.To = p.To + p2.To.Offset += int64(Widthptr) + Regfree(&adst) + if sys_wbptr == nil { + sys_wbptr = writebarrierfn("writebarrierptr", Types[Tptr], Types[Tptr]) + } + Ginscall(sys_wbptr, 0) + Patch(pjmp, Pc) + + Regfree(&dst) + Regfree(&src) +} + +func cgen_wbfat(n, res *Node) { + if Curfn != nil && Curfn.Func.Nowritebarrier { + Yyerror("write barrier prohibited") + } + if Debug_wb > 0 { + Warn("write barrier") + } + needType := true + funcName := "typedmemmove" + var dst, src Node + if n.Ullman >= res.Ullman { + Agenr(n, &src, nil) + Agenr(res, &dst, nil) + } else { + Agenr(res, &dst, nil) + Agenr(n, &src, nil) + } + p := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &dst, nil) + a := &p.To + a.Type = obj.TYPE_MEM + a.Reg = int16(Thearch.REGSP) + a.Offset = 0 + if HasLinkRegister() { + a.Offset += int64(Widthptr) + } + if needType { + a.Offset += int64(Widthptr) + } + p2 := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &src, nil) + p2.To = p.To + p2.To.Offset += int64(Widthptr) + Regfree(&dst) + if needType { + src.Type = Types[Tptr] + Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), typename(n.Type), &src) + p3 := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &src, nil) + p3.To = p2.To + p3.To.Offset -= 2 * int64(Widthptr) + } + Regfree(&src) + Ginscall(writebarrierfn(funcName, Types[Tptr], Types[Tptr]), 0) +} + +// cgen_norm moves n1 to res, truncating to expected type if necessary. +// n1 is a register, and cgen_norm frees it. +func cgen_norm(n, n1, res *Node) { + switch Ctxt.Arch.Thechar { + case '6', '8': + // We use sized math, so the result is already truncated. + default: + switch n.Op { + case OADD, OSUB, OMUL, ODIV, OCOM, OMINUS: + // TODO(rsc): What about left shift? + Thearch.Gins(Thearch.Optoas(OAS, n.Type), n1, n1) + } + } + + Thearch.Gmove(n1, res) + Regfree(n1) +} + +func Mgen(n *Node, n1 *Node, rg *Node) { + n1.Op = OEMPTY + + if n.Addable { + *n1 = *n + if n1.Op == OREGISTER || n1.Op == OINDREG { + reg[n.Reg-int16(Thearch.REGMIN)]++ + } + return + } + + Tempname(n1, n.Type) + Cgen(n, n1) + if n.Type.Width <= int64(Widthptr) || Isfloat[n.Type.Etype] { + n2 := *n1 + Regalloc(n1, n.Type, rg) + Thearch.Gmove(&n2, n1) + } +} + +func Mfree(n *Node) { + if n.Op == OREGISTER { + Regfree(n) + } +} + +/* + * allocate a register (reusing res if possible) and generate + * a = n + * The caller must call Regfree(a). + */ +func Cgenr(n *Node, a *Node, res *Node) { + if Debug['g'] != 0 { + Dump("cgenr-n", n) + } + + if Isfat(n.Type) { + Fatal("cgenr on fat node") + } + + if n.Addable { + Regalloc(a, n.Type, res) + Thearch.Gmove(n, a) + return + } + + switch n.Op { + case ONAME, + ODOT, + ODOTPTR, + OINDEX, + OCALLFUNC, + OCALLMETH, + OCALLINTER: + var n1 Node + Igen(n, &n1, res) + Regalloc(a, Types[Tptr], &n1) + Thearch.Gmove(&n1, a) + Regfree(&n1) + + default: + Regalloc(a, n.Type, res) + Cgen(n, a) + } +} + +/* + * allocate a register (reusing res if possible) and generate + * a = &n + * The caller must call Regfree(a). + * The generated code checks that the result is not nil. + */ +func Agenr(n *Node, a *Node, res *Node) { + if Debug['g'] != 0 { + Dump("\nagenr-n", n) + } + + nl := n.Left + nr := n.Right + + switch n.Op { + case ODOT, ODOTPTR, OCALLFUNC, OCALLMETH, OCALLINTER: + var n1 Node + Igen(n, &n1, res) + Regalloc(a, Types[Tptr], &n1) + Agen(&n1, a) + Regfree(&n1) + + case OIND: + Cgenr(n.Left, a, res) + Cgen_checknil(a) + + case OINDEX: + if Ctxt.Arch.Thechar == '5' { + var p2 *obj.Prog // to be patched to panicindex. + w := uint32(n.Type.Width) + bounded := Debug['B'] != 0 || n.Bounded + var n1 Node + var n3 Node + if nr.Addable { + var tmp Node + if !Isconst(nr, CTINT) { + Tempname(&tmp, Types[TINT32]) + } + if !Isconst(nl, CTSTR) { + Agenr(nl, &n3, res) + } + if !Isconst(nr, CTINT) { + p2 = Thearch.Cgenindex(nr, &tmp, bounded) + Regalloc(&n1, tmp.Type, nil) + Thearch.Gmove(&tmp, &n1) + } + } else if nl.Addable { + if !Isconst(nr, CTINT) { + var tmp Node + Tempname(&tmp, Types[TINT32]) + p2 = Thearch.Cgenindex(nr, &tmp, bounded) + Regalloc(&n1, tmp.Type, nil) + Thearch.Gmove(&tmp, &n1) + } + + if !Isconst(nl, CTSTR) { + Agenr(nl, &n3, res) + } + } else { + var tmp Node + Tempname(&tmp, Types[TINT32]) + p2 = Thearch.Cgenindex(nr, &tmp, bounded) + nr = &tmp + if !Isconst(nl, CTSTR) { + Agenr(nl, &n3, res) + } + Regalloc(&n1, tmp.Type, nil) + Thearch.Gins(Thearch.Optoas(OAS, tmp.Type), &tmp, &n1) + } + + // &a is in &n3 (allocated in res) + // i is in &n1 (if not constant) + // w is width + + // constant index + if Isconst(nr, CTINT) { + if Isconst(nl, CTSTR) { + Fatal("constant string constant index") + } + v := uint64(Mpgetfix(nr.Val.U.(*Mpint))) + var n2 Node + if Isslice(nl.Type) || nl.Type.Etype == TSTRING { + if Debug['B'] == 0 && !n.Bounded { + n1 = n3 + 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) + } + + n1 = n3 + n1.Op = OINDREG + n1.Type = Types[Tptr] + n1.Xoffset = int64(Array_array) + Thearch.Gmove(&n1, &n3) + } + + Nodconst(&n2, Types[Tptr], int64(v*uint64(w))) + Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n3) + *a = n3 + break + } + + var n2 Node + Regalloc(&n2, Types[TINT32], &n1) // i + Thearch.Gmove(&n1, &n2) + Regfree(&n1) + + var n4 Node + if Debug['B'] == 0 && !n.Bounded { + // check bounds + if Isconst(nl, CTSTR) { + Nodconst(&n4, Types[TUINT32], int64(len(nl.Val.U.(string)))) + } else if Isslice(nl.Type) || nl.Type.Etype == TSTRING { + n1 = n3 + n1.Op = OINDREG + n1.Type = Types[Tptr] + n1.Xoffset = int64(Array_nel) + Regalloc(&n4, Types[TUINT32], nil) + Thearch.Gmove(&n1, &n4) + } else { + Nodconst(&n4, Types[TUINT32], nl.Type.Bound) + } + p1 := Thearch.Ginscmp(OLT, Types[TUINT32], &n2, &n4, +1) + if n4.Op == OREGISTER { + Regfree(&n4) + } + if p2 != nil { + Patch(p2, Pc) + } + Ginscall(Panicindex, -1) + Patch(p1, Pc) + } + + if Isconst(nl, CTSTR) { + Regalloc(&n3, Types[Tptr], res) + p1 := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), nil, &n3) + Datastring(nl.Val.U.(string), &p1.From) + p1.From.Type = obj.TYPE_ADDR + } else if Isslice(nl.Type) || nl.Type.Etype == TSTRING { + n1 = n3 + n1.Op = OINDREG + n1.Type = Types[Tptr] + n1.Xoffset = int64(Array_array) + Thearch.Gmove(&n1, &n3) + } + + if w == 0 { + // nothing to do + } else if Thearch.AddIndex != nil && Thearch.AddIndex(&n2, int64(w), &n3) { + // done by back end + } else if w == 1 { + Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n3) + } else { + if w&(w-1) == 0 { + // Power of 2. Use shift. + Thearch.Ginscon(Thearch.Optoas(OLSH, Types[TUINT32]), int64(log2(uint64(w))), &n2) + } else { + // Not a power of 2. Use multiply. + Regalloc(&n4, Types[TUINT32], nil) + Nodconst(&n1, Types[TUINT32], int64(w)) + Thearch.Gmove(&n1, &n4) + Thearch.Gins(Thearch.Optoas(OMUL, Types[TUINT32]), &n4, &n2) + Regfree(&n4) + } + Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n3) + } + *a = n3 + Regfree(&n2) + break + } + if Ctxt.Arch.Thechar == '8' { + var p2 *obj.Prog // to be patched to panicindex. + w := uint32(n.Type.Width) + bounded := Debug['B'] != 0 || n.Bounded + var n3 Node + var tmp Node + var n1 Node + if nr.Addable { + // Generate &nl first, and move nr into register. + if !Isconst(nl, CTSTR) { + Igen(nl, &n3, res) + } + if !Isconst(nr, CTINT) { + p2 = Thearch.Igenindex(nr, &tmp, bounded) + Regalloc(&n1, tmp.Type, nil) + Thearch.Gmove(&tmp, &n1) + } + } else if nl.Addable { + // Generate nr first, and move &nl into register. + if !Isconst(nr, CTINT) { + p2 = Thearch.Igenindex(nr, &tmp, bounded) + Regalloc(&n1, tmp.Type, nil) + Thearch.Gmove(&tmp, &n1) + } + + if !Isconst(nl, CTSTR) { + Igen(nl, &n3, res) + } + } else { + p2 = Thearch.Igenindex(nr, &tmp, bounded) + nr = &tmp + if !Isconst(nl, CTSTR) { + Igen(nl, &n3, res) + } + Regalloc(&n1, tmp.Type, nil) + Thearch.Gins(Thearch.Optoas(OAS, tmp.Type), &tmp, &n1) + } + + // For fixed array we really want the pointer in n3. + var n2 Node + if Isfixedarray(nl.Type) { + Regalloc(&n2, Types[Tptr], &n3) + Agen(&n3, &n2) + Regfree(&n3) + n3 = n2 + } + + // &a[0] is in n3 (allocated in res) + // i is in n1 (if not constant) + // len(a) is in nlen (if needed) + // w is width + + // constant index + if Isconst(nr, CTINT) { + if Isconst(nl, CTSTR) { + Fatal("constant string constant index") // front end should handle + } + v := uint64(Mpgetfix(nr.Val.U.(*Mpint))) + if Isslice(nl.Type) || nl.Type.Etype == TSTRING { + if Debug['B'] == 0 && !n.Bounded { + nlen := n3 + nlen.Type = Types[TUINT32] + nlen.Xoffset += int64(Array_nel) + Nodconst(&n2, Types[TUINT32], int64(v)) + p1 := Thearch.Ginscmp(OGT, Types[TUINT32], &nlen, &n2, +1) + Ginscall(Panicindex, -1) + Patch(p1, Pc) + } + } + + // Load base pointer in n2 = n3. + Regalloc(&n2, Types[Tptr], &n3) + + n3.Type = Types[Tptr] + n3.Xoffset += int64(Array_array) + Thearch.Gmove(&n3, &n2) + Regfree(&n3) + if v*uint64(w) != 0 { + Nodconst(&n1, Types[Tptr], int64(v*uint64(w))) + Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n1, &n2) + } + *a = n2 + break + } + + // i is in register n1, extend to 32 bits. + t := Types[TUINT32] + + if Issigned[n1.Type.Etype] { + t = Types[TINT32] + } + + Regalloc(&n2, t, &n1) // i + Thearch.Gmove(&n1, &n2) + Regfree(&n1) + + if Debug['B'] == 0 && !n.Bounded { + // check bounds + t := Types[TUINT32] + + var nlen Node + if Isconst(nl, CTSTR) { + Nodconst(&nlen, t, int64(len(nl.Val.U.(string)))) + } else if Isslice(nl.Type) || nl.Type.Etype == TSTRING { + nlen = n3 + nlen.Type = t + nlen.Xoffset += int64(Array_nel) + } else { + Nodconst(&nlen, t, nl.Type.Bound) + } + + p1 := Thearch.Ginscmp(OLT, t, &n2, &nlen, +1) + if p2 != nil { + Patch(p2, Pc) + } + Ginscall(Panicindex, -1) + Patch(p1, Pc) + } + + if Isconst(nl, CTSTR) { + Regalloc(&n3, Types[Tptr], res) + p1 := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), nil, &n3) + Datastring(nl.Val.U.(string), &p1.From) + p1.From.Type = obj.TYPE_ADDR + Thearch.Gins(Thearch.Optoas(OADD, n3.Type), &n2, &n3) + goto indexdone1 + } + + // Load base pointer in n3. + Regalloc(&tmp, Types[Tptr], &n3) + + if Isslice(nl.Type) || nl.Type.Etype == TSTRING { + n3.Type = Types[Tptr] + n3.Xoffset += int64(Array_array) + Thearch.Gmove(&n3, &tmp) + } + + Regfree(&n3) + n3 = tmp + + if w == 0 { + // nothing to do + } else if Thearch.AddIndex != nil && Thearch.AddIndex(&n2, int64(w), &n3) { + // done by back end + } else if w == 1 { + Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n3) + } else { + if w&(w-1) == 0 { + // Power of 2. Use shift. + Thearch.Ginscon(Thearch.Optoas(OLSH, Types[TUINT32]), int64(log2(uint64(w))), &n2) + } else { + // Not a power of 2. Use multiply. + Thearch.Ginscon(Thearch.Optoas(OMUL, Types[TUINT32]), int64(w), &n2) + } + Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n3) + } + + indexdone1: + *a = n3 + Regfree(&n2) + break + } + + freelen := 0 + w := uint64(n.Type.Width) + + // Generate the non-addressable child first. + var n3 Node + var nlen Node + var tmp Node + var n1 Node + if nr.Addable { + goto irad + } + if nl.Addable { + Cgenr(nr, &n1, nil) + if !Isconst(nl, CTSTR) { + if Isfixedarray(nl.Type) { + Agenr(nl, &n3, res) + } else { + Igen(nl, &nlen, res) + freelen = 1 + nlen.Type = Types[Tptr] + nlen.Xoffset += int64(Array_array) + Regalloc(&n3, Types[Tptr], res) + Thearch.Gmove(&nlen, &n3) + nlen.Type = Types[Simtype[TUINT]] + nlen.Xoffset += int64(Array_nel) - int64(Array_array) + } + } + + goto index + } + + Tempname(&tmp, nr.Type) + Cgen(nr, &tmp) + nr = &tmp + + irad: + if !Isconst(nl, CTSTR) { + if Isfixedarray(nl.Type) { + Agenr(nl, &n3, res) + } else { + if !nl.Addable { + if res != nil && res.Op == OREGISTER { // give up res, which we don't need yet. + Regfree(res) + } + + // igen will need an addressable node. + var tmp2 Node + Tempname(&tmp2, nl.Type) + Cgen(nl, &tmp2) + nl = &tmp2 + + if res != nil && res.Op == OREGISTER { // reacquire res + Regrealloc(res) + } + } + + Igen(nl, &nlen, res) + freelen = 1 + nlen.Type = Types[Tptr] + nlen.Xoffset += int64(Array_array) + Regalloc(&n3, Types[Tptr], res) + Thearch.Gmove(&nlen, &n3) + nlen.Type = Types[Simtype[TUINT]] + nlen.Xoffset += int64(Array_nel) - int64(Array_array) + } + } + + if !Isconst(nr, CTINT) { + Cgenr(nr, &n1, nil) + } + + goto index + + // &a is in &n3 (allocated in res) + // i is in &n1 (if not constant) + // len(a) is in nlen (if needed) + // w is width + + // constant index + index: + if Isconst(nr, CTINT) { + if Isconst(nl, CTSTR) { + Fatal("constant string constant index") // front end should handle + } + 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) + Ginscall(Panicindex, -1) + Patch(p1, Pc) + } + + Regfree(&nlen) + } + + if v*w != 0 { + Thearch.Ginscon(Thearch.Optoas(OADD, Types[Tptr]), int64(v*w), &n3) + } + *a = n3 + break + } + + // type of the index + t := Types[TUINT64] + + if Issigned[n1.Type.Etype] { + t = Types[TINT64] + } + + var n2 Node + Regalloc(&n2, t, &n1) // i + Thearch.Gmove(&n1, &n2) + Regfree(&n1) + + if Debug['B'] == 0 && !n.Bounded { + // check bounds + t = Types[Simtype[TUINT]] + + if Is64(nr.Type) { + t = Types[TUINT64] + } + if Isconst(nl, CTSTR) { + Nodconst(&nlen, t, int64(len(nl.Val.U.(string)))) + } else if Isslice(nl.Type) || nl.Type.Etype == TSTRING { + // nlen already initialized + } else { + Nodconst(&nlen, t, nl.Type.Bound) + } + + p1 := Thearch.Ginscmp(OLT, t, &n2, &nlen, +1) + Ginscall(Panicindex, -1) + Patch(p1, Pc) + } + + 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.(string), &p1.From) + p1.From.Type = obj.TYPE_ADDR + Thearch.Gins(Thearch.Optoas(OADD, n3.Type), &n2, &n3) + goto indexdone + } + + if w == 0 { + // nothing to do + } else if Thearch.AddIndex != nil && Thearch.AddIndex(&n2, int64(w), &n3) { + // done by back end + } else if w == 1 { + Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n3) + } else { + if w&(w-1) == 0 { + // Power of 2. Use shift. + Thearch.Ginscon(Thearch.Optoas(OLSH, t), int64(log2(w)), &n2) + } else { + // Not a power of 2. Use multiply. + Thearch.Ginscon(Thearch.Optoas(OMUL, t), int64(w), &n2) + } + Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n3) + } + + indexdone: + *a = n3 + Regfree(&n2) + if freelen != 0 { + Regfree(&nlen) + } + + default: + Regalloc(a, Types[Tptr], res) + Agen(n, a) + } +} + +// log2 returns the logarithm base 2 of n. n must be a power of 2. +func log2(n uint64) int { + x := 0 + for n>>uint(x) != 1 { + x++ + } + return x +} + +/* + * generate: + * res = &n; + * The generated code checks that the result is not nil. + */ +func Agen(n *Node, res *Node) { + if Debug['g'] != 0 { + Dump("\nagen-res", res) + Dump("agen-r", n) + } + + if n == nil || n.Type == nil { + return + } + + for n.Op == OCONVNOP { + n = n.Left + } + + if Isconst(n, CTNIL) && n.Type.Width > int64(Widthptr) { + // Use of a nil interface or nil slice. + // Create a temporary we can take the address of and read. + // The generated code is just going to panic, so it need not + // be terribly efficient. See issue 3670. + var n1 Node + Tempname(&n1, n.Type) + + Gvardef(&n1) + Thearch.Clearfat(&n1) + var n2 Node + Regalloc(&n2, Types[Tptr], res) + var n3 Node + n3.Op = OADDR + n3.Left = &n1 + Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &n3, &n2) + Thearch.Gmove(&n2, res) + Regfree(&n2) + return + } + + if n.Op == OINDREG && n.Xoffset == 0 { + // Generate MOVW R0, R1 instead of MOVW $0(R0), R1. + // This allows better move propagation in the back ends + // (and maybe it helps the processor). + n1 := *n + n1.Op = OREGISTER + n1.Type = res.Type + Thearch.Gmove(&n1, res) + return + } + + if n.Addable { + if n.Op == OREGISTER { + Fatal("agen OREGISTER") + } + var n1 Node + n1.Op = OADDR + n1.Left = n + var n2 Node + Regalloc(&n2, Types[Tptr], res) + Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &n1, &n2) + Thearch.Gmove(&n2, res) + Regfree(&n2) + return + } + + nl := n.Left + + switch n.Op { + default: + Fatal("agen: unknown op %v", Nconv(n, obj.FmtShort|obj.FmtSign)) + + case OCALLMETH: + cgen_callmeth(n, 0) + cgen_aret(n, res) + + case OCALLINTER: + cgen_callinter(n, res, 0) + cgen_aret(n, res) + + case OCALLFUNC: + cgen_call(n, 0) + cgen_aret(n, res) + + case OEFACE, ODOTTYPE, OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR: + var n1 Node + Tempname(&n1, n.Type) + Cgen(n, &n1) + Agen(&n1, res) + + case OINDEX: + var n1 Node + Agenr(n, &n1, res) + Thearch.Gmove(&n1, res) + Regfree(&n1) + + case ONAME: + // should only get here with names in this func. + if n.Funcdepth > 0 && n.Funcdepth != Funcdepth { + Dump("bad agen", n) + Fatal("agen: bad ONAME funcdepth %d != %d", n.Funcdepth, Funcdepth) + } + + // should only get here for heap vars or paramref + if n.Class&PHEAP == 0 && n.Class != PPARAMREF { + Dump("bad agen", n) + Fatal("agen: bad ONAME class %#x", n.Class) + } + + Cgen(n.Name.Heapaddr, res) + if n.Xoffset != 0 { + addOffset(res, n.Xoffset) + } + + case OIND: + Cgen(nl, res) + Cgen_checknil(res) + + case ODOT: + Agen(nl, res) + if n.Xoffset != 0 { + addOffset(res, n.Xoffset) + } + + case ODOTPTR: + Cgen(nl, res) + Cgen_checknil(res) + if n.Xoffset != 0 { + addOffset(res, n.Xoffset) + } + } +} + +func addOffset(res *Node, offset int64) { + if Ctxt.Arch.Thechar == '6' || Ctxt.Arch.Thechar == '8' { + Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), Nodintconst(offset), res) + return + } + + var n1, n2 Node + Regalloc(&n1, Types[Tptr], nil) + Thearch.Gmove(res, &n1) + Regalloc(&n2, Types[Tptr], nil) + Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), Nodintconst(offset), &n2) + Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n1) + Thearch.Gmove(&n1, res) + Regfree(&n1) + Regfree(&n2) +} + +// Igen computes the address &n, stores it in a register r, +// and rewrites a to refer to *r. The chosen r may be the +// stack pointer, it may be borrowed from res, or it may +// be a newly allocated register. The caller must call Regfree(a) +// to free r when the address is no longer needed. +// The generated code ensures that &n is not nil. +func Igen(n *Node, a *Node, res *Node) { + if Debug['g'] != 0 { + Dump("\nigen-n", n) + } + + switch n.Op { + case ONAME: + if (n.Class&PHEAP != 0) || n.Class == PPARAMREF { + break + } + *a = *n + return + + case OINDREG: + // Increase the refcount of the register so that igen's caller + // has to call Regfree. + if n.Reg != int16(Thearch.REGSP) { + reg[n.Reg-int16(Thearch.REGMIN)]++ + } + *a = *n + return + + case ODOT: + Igen(n.Left, a, res) + a.Xoffset += n.Xoffset + a.Type = n.Type + Fixlargeoffset(a) + return + + case ODOTPTR: + Cgenr(n.Left, a, res) + Cgen_checknil(a) + a.Op = OINDREG + a.Xoffset += n.Xoffset + a.Type = n.Type + Fixlargeoffset(a) + return + + case OCALLFUNC, OCALLMETH, OCALLINTER: + switch n.Op { + case OCALLFUNC: + cgen_call(n, 0) + + case OCALLMETH: + cgen_callmeth(n, 0) + + case OCALLINTER: + cgen_callinter(n, nil, 0) + } + + var flist Iter + fp := Structfirst(&flist, Getoutarg(n.Left.Type)) + *a = Node{} + a.Op = OINDREG + a.Reg = int16(Thearch.REGSP) + a.Addable = true + a.Xoffset = fp.Width + if HasLinkRegister() { + a.Xoffset += int64(Ctxt.Arch.Ptrsize) + } + a.Type = n.Type + return + + // Index of fixed-size array by constant can + // put the offset in the addressing. + // Could do the same for slice except that we need + // to use the real index for the bounds checking. + case OINDEX: + if Isfixedarray(n.Left.Type) || (Isptr[n.Left.Type.Etype] && Isfixedarray(n.Left.Left.Type)) { + if Isconst(n.Right, CTINT) { + // Compute &a. + if !Isptr[n.Left.Type.Etype] { + Igen(n.Left, a, res) + } else { + var n1 Node + Igen(n.Left, &n1, res) + Cgen_checknil(&n1) + Regalloc(a, Types[Tptr], res) + Thearch.Gmove(&n1, a) + Regfree(&n1) + a.Op = OINDREG + } + + // Compute &a[i] as &a + i*width. + a.Type = n.Type + + a.Xoffset += Mpgetfix(n.Right.Val.U.(*Mpint)) * n.Type.Width + Fixlargeoffset(a) + return + } + } + } + + Agenr(n, a, res) + a.Op = OINDREG + a.Type = n.Type +} + +// Bgen generates code for branches: +// +// if n == wantTrue { +// goto to +// } +func Bgen(n *Node, wantTrue bool, likely int, to *obj.Prog) { + bgenx(n, nil, wantTrue, likely, to) +} + +// Bvgen generates code for calculating boolean values: +// res = n == wantTrue +func Bvgen(n, res *Node, wantTrue bool) { + if Thearch.Ginsboolval == nil { + // Direct value generation not implemented for this architecture. + // Implement using jumps. + bvgenjump(n, res, wantTrue, true) + return + } + bgenx(n, res, wantTrue, 0, nil) +} + +// bvgenjump implements boolean value generation using jumps: +// if n == wantTrue { +// res = 1 +// } else { +// res = 0 +// } +// geninit controls whether n's Ninit is generated. +func bvgenjump(n, res *Node, wantTrue, geninit bool) { + init := n.Ninit + if !geninit { + n.Ninit = nil + } + p1 := Gbranch(obj.AJMP, nil, 0) + p2 := Pc + Thearch.Gmove(Nodbool(true), res) + p3 := Gbranch(obj.AJMP, nil, 0) + Patch(p1, Pc) + Bgen(n, wantTrue, 0, p2) + Thearch.Gmove(Nodbool(false), res) + Patch(p3, Pc) + n.Ninit = init +} + +// bgenx is the backend for Bgen and Bvgen. +// If res is nil, it generates a branch. +// Otherwise, it generates a boolean value. +func bgenx(n, res *Node, wantTrue bool, likely int, to *obj.Prog) { + if Debug['g'] != 0 { + fmt.Printf("\nbgenx wantTrue=%t likely=%d to=%v\n", wantTrue, likely, to) + Dump("n", n) + Dump("res", res) + } + + genval := res != nil + + if n == nil { + n = Nodbool(true) + } + + Genlist(n.Ninit) + + if n.Type == nil { + Convlit(&n, Types[TBOOL]) + if n.Type == nil { + return + } + } + + if n.Type.Etype != TBOOL { + Fatal("bgen: bad type %v for %v", n.Type, Oconv(int(n.Op), 0)) + } + + for n.Op == OCONVNOP { + n = n.Left + Genlist(n.Ninit) + } + + if Thearch.Bgen_float != nil && n.Left != nil && Isfloat[n.Left.Type.Etype] { + if genval { + bvgenjump(n, res, wantTrue, false) + return + } + Thearch.Bgen_float(n, wantTrue, likely, to) + return + } + + switch n.Op { + default: + if genval { + Cgen(n, res) + if !wantTrue { + Thearch.Gins(Thearch.Optoas(OXOR, Types[TUINT8]), Nodintconst(1), res) + } + return + } + + var tmp Node + Regalloc(&tmp, n.Type, nil) + Cgen(n, &tmp) + bgenNonZero(&tmp, nil, wantTrue, likely, to) + Regfree(&tmp) + return + + case ONAME: + if genval { + // 5g, 7g, and 9g might need a temporary or other help here, + // but they don't support direct generation of a bool value yet. + // We can fix that as we go. + switch Ctxt.Arch.Thechar { + case '5', '7', '9': + Fatal("genval 5g, 7g, 9g ONAMES not fully implemented") + } + Cgen(n, res) + if !wantTrue { + Thearch.Gins(Thearch.Optoas(OXOR, Types[TUINT8]), Nodintconst(1), res) + } + return + } + + if n.Addable && Ctxt.Arch.Thechar != '5' && Ctxt.Arch.Thechar != '7' && Ctxt.Arch.Thechar != '9' { + // no need for a temporary + bgenNonZero(n, nil, wantTrue, likely, to) + return + } + var tmp Node + Regalloc(&tmp, n.Type, nil) + Cgen(n, &tmp) + bgenNonZero(&tmp, nil, wantTrue, likely, to) + Regfree(&tmp) + return + + case OLITERAL: + // n is a constant. + if !Isconst(n, CTBOOL) { + Fatal("bgen: non-bool const %v\n", Nconv(n, obj.FmtLong)) + } + if genval { + Cgen(Nodbool(wantTrue == n.Val.U.(bool)), res) + return + } + // If n == wantTrue, jump; otherwise do nothing. + if wantTrue == n.Val.U.(bool) { + Patch(Gbranch(obj.AJMP, nil, likely), to) + } + return + + case OANDAND, OOROR: + and := (n.Op == OANDAND) == wantTrue + if genval { + p1 := Gbranch(obj.AJMP, nil, 0) + p2 := Gbranch(obj.AJMP, nil, 0) + Patch(p2, Pc) + Cgen(Nodbool(!and), res) + p3 := Gbranch(obj.AJMP, nil, 0) + Patch(p1, Pc) + Bgen(n.Left, wantTrue != and, 0, p2) + Bvgen(n.Right, res, wantTrue) + Patch(p3, Pc) + return + } + + if and { + p1 := Gbranch(obj.AJMP, nil, 0) + p2 := Gbranch(obj.AJMP, nil, 0) + Patch(p1, Pc) + Bgen(n.Left, !wantTrue, -likely, p2) + Bgen(n.Right, !wantTrue, -likely, p2) + p1 = Gbranch(obj.AJMP, nil, 0) + Patch(p1, to) + Patch(p2, Pc) + } else { + Bgen(n.Left, wantTrue, likely, to) + Bgen(n.Right, wantTrue, likely, to) + } + return + + case ONOT: // unary + if n.Left == nil || n.Left.Type == nil { + return + } + bgenx(n.Left, res, !wantTrue, likely, to) + return + + case OEQ, ONE, OLT, OGT, OLE, OGE: + if n.Left == nil || n.Left.Type == nil || n.Right == nil || n.Right.Type == nil { + return + } + } + + // n.Op is one of OEQ, ONE, OLT, OGT, OLE, OGE + nl := n.Left + nr := n.Right + a := int(n.Op) + + if !wantTrue { + if Isfloat[nr.Type.Etype] { + // Brcom is not valid on floats when NaN is involved. + ll := n.Ninit // avoid re-genning Ninit + n.Ninit = nil + if genval { + bgenx(n, res, true, likely, to) + Thearch.Gins(Thearch.Optoas(OXOR, Types[TUINT8]), Nodintconst(1), res) // res = !res + n.Ninit = ll + return + } + p1 := Gbranch(obj.AJMP, nil, 0) + p2 := Gbranch(obj.AJMP, nil, 0) + Patch(p1, Pc) + bgenx(n, res, true, -likely, p2) + Patch(Gbranch(obj.AJMP, nil, 0), to) + Patch(p2, Pc) + n.Ninit = ll + return + } + + a = Brcom(a) + } + wantTrue = true + + // make simplest on right + if nl.Op == OLITERAL || (nl.Ullman < nr.Ullman && nl.Ullman < UINF) { + a = Brrev(a) + nl, nr = nr, nl + } + + if Isslice(nl.Type) || Isinter(nl.Type) { + // front end should only leave cmp to literal nil + if (a != OEQ && a != ONE) || nr.Op != OLITERAL { + if Isslice(nl.Type) { + Yyerror("illegal slice comparison") + } else { + Yyerror("illegal interface comparison") + } + return + } + + var ptr Node + Igen(nl, &ptr, nil) + if Isslice(nl.Type) { + ptr.Xoffset += int64(Array_array) + } + ptr.Type = Types[Tptr] + var tmp Node + Regalloc(&tmp, ptr.Type, &ptr) + Cgen(&ptr, &tmp) + Regfree(&ptr) + bgenNonZero(&tmp, res, a == OEQ != wantTrue, likely, to) + Regfree(&tmp) + return + } + + if Iscomplex[nl.Type.Etype] { + complexbool(a, nl, nr, res, wantTrue, likely, to) + return + } + + if Ctxt.Arch.Regsize == 4 && Is64(nr.Type) { + if genval { + // TODO: Teach Cmp64 to generate boolean values and remove this. + bvgenjump(n, res, wantTrue, false) + return + } + if !nl.Addable || Isconst(nl, CTINT) { + nl = CgenTemp(nl) + } + if !nr.Addable { + nr = CgenTemp(nr) + } + Thearch.Cmp64(nl, nr, a, likely, to) + return + } + + if nr.Ullman >= UINF { + var n1 Node + Regalloc(&n1, nl.Type, nil) + Cgen(nl, &n1) + + var tmp Node + Tempname(&tmp, nl.Type) + Thearch.Gmove(&n1, &tmp) + Regfree(&n1) + + var n2 Node + Regalloc(&n2, nr.Type, nil) + Cgen(nr, &n2) + Regfree(&n2) + + Regalloc(&n1, nl.Type, nil) + Cgen(&tmp, &n1) + Regfree(&n1) + } else { + var n1 Node + if !nl.Addable && Ctxt.Arch.Thechar == '8' { + Tempname(&n1, nl.Type) + } else { + Regalloc(&n1, nl.Type, nil) + defer Regfree(&n1) + } + Cgen(nl, &n1) + nl = &n1 + + if Smallintconst(nr) && Ctxt.Arch.Thechar != '9' { + Thearch.Gins(Thearch.Optoas(OCMP, nr.Type), nl, nr) + bins(nr.Type, res, a, likely, to) + return + } + + if !nr.Addable && Ctxt.Arch.Thechar == '8' { + nr = CgenTemp(nr) + } + + var n2 Node + Regalloc(&n2, nr.Type, nil) + Cgen(nr, &n2) + nr = &n2 + Regfree(&n2) + } + + l, r := nl, nr + + // On x86, only < and <= work right with NaN; reverse if needed + if Ctxt.Arch.Thechar == '6' && Isfloat[nl.Type.Etype] && (a == OGT || a == OGE) { + l, r = r, l + a = Brrev(a) + } + + // Do the comparison. + Thearch.Gins(Thearch.Optoas(OCMP, nr.Type), l, r) + + // Handle floating point special cases. + // Note that 8g has Bgen_float and is handled above. + if Isfloat[nl.Type.Etype] { + switch Ctxt.Arch.Thechar { + case '5': + if genval { + Fatal("genval 5g Isfloat special cases not implemented") + } + switch n.Op { + case ONE: + Patch(Gbranch(Thearch.Optoas(OPS, nr.Type), nr.Type, likely), to) + Patch(Gbranch(Thearch.Optoas(a, nr.Type), nr.Type, likely), to) + default: + p := Gbranch(Thearch.Optoas(OPS, nr.Type), nr.Type, -likely) + Patch(Gbranch(Thearch.Optoas(a, nr.Type), nr.Type, likely), to) + Patch(p, Pc) + } + return + case '6': + switch n.Op { + case OEQ: + // neither NE nor P + if genval { + var reg Node + Regalloc(®, Types[TBOOL], nil) + Thearch.Ginsboolval(Thearch.Optoas(OEQ, nr.Type), ®) + Thearch.Ginsboolval(Thearch.Optoas(OPC, nr.Type), res) + Thearch.Gins(Thearch.Optoas(OAND, Types[TBOOL]), ®, res) + Regfree(®) + } else { + p1 := Gbranch(Thearch.Optoas(ONE, nr.Type), nil, -likely) + p2 := Gbranch(Thearch.Optoas(OPS, nr.Type), nil, -likely) + Patch(Gbranch(obj.AJMP, nil, 0), to) + Patch(p1, Pc) + Patch(p2, Pc) + } + return + case ONE: + // either NE or P + if genval { + var reg Node + Regalloc(®, Types[TBOOL], nil) + Thearch.Ginsboolval(Thearch.Optoas(ONE, nr.Type), ®) + Thearch.Ginsboolval(Thearch.Optoas(OPS, nr.Type), res) + Thearch.Gins(Thearch.Optoas(OOR, Types[TBOOL]), ®, res) + Regfree(®) + } else { + Patch(Gbranch(Thearch.Optoas(ONE, nr.Type), nil, likely), to) + Patch(Gbranch(Thearch.Optoas(OPS, nr.Type), nil, likely), to) + } + return + } + case '7', '9': + if genval { + Fatal("genval 7g, 9g Isfloat special cases not implemented") + } + switch n.Op { + // On arm64 and ppc64, <= and >= mishandle NaN. Must decompose into < or > and =. + // TODO(josh): Convert a <= b to b > a instead? + case OLE, OGE: + if a == OLE { + a = OLT + } else { + a = OGT + } + Patch(Gbranch(Thearch.Optoas(a, nr.Type), nr.Type, likely), to) + Patch(Gbranch(Thearch.Optoas(OEQ, nr.Type), nr.Type, likely), to) + return + } + } + } + + // Not a special case. Insert the conditional jump or value gen. + bins(nr.Type, res, a, likely, to) +} + +func bgenNonZero(n, res *Node, wantTrue bool, likely int, to *obj.Prog) { + // TODO: Optimize on systems that can compare to zero easily. + a := ONE + if !wantTrue { + a = OEQ + } + var zero Node + Nodconst(&zero, n.Type, 0) + Thearch.Gins(Thearch.Optoas(OCMP, n.Type), n, &zero) + bins(n.Type, res, a, likely, to) +} + +// bins inserts an instruction to handle the result of a compare. +// If res is non-nil, it inserts appropriate value generation instructions. +// If res is nil, it inserts a branch to to. +func bins(typ *Type, res *Node, a, likely int, to *obj.Prog) { + a = Thearch.Optoas(a, typ) + if res != nil { + // value gen + Thearch.Ginsboolval(a, res) + } else { + // jump + Patch(Gbranch(a, typ, likely), to) + } +} + +// 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: + t := n.Left.Type + if Isptr[t.Etype] { + break + } + off := stkof(n.Left) + if off == -1000 || off == +1000 { + return off + } + return off + n.Xoffset + + case OINDEX: + t := n.Left.Type + if !Isfixedarray(t) { + break + } + off := stkof(n.Left) + 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 // on stack but not sure exactly where + + case OCALLMETH, OCALLINTER, OCALLFUNC: + t := n.Left.Type + if Isptr[t.Etype] { + t = t.Type + } + + var flist Iter + t = Structfirst(&flist, Getoutarg(t)) + if t != nil { + w := t.Width + if HasLinkRegister() { + w += int64(Ctxt.Arch.Ptrsize) + } + return w + } + } + + // botch - probably failing to recognize address + // arithmetic on the above. eg INDEX and DOT + return -1000 // not on stack +} + +/* + * block copy: + * memmove(&ns, &n, w); + * if wb is true, needs write barrier. + */ +func sgen_wb(n *Node, ns *Node, w int64, wb bool) { + if Debug['g'] != 0 { + op := "sgen" + if wb { + op = "sgen-wb" + } + fmt.Printf("\n%s w=%d\n", op, w) + Dump("r", n) + Dump("res", ns) + } + + if n.Ullman >= UINF && ns.Ullman >= UINF { + Fatal("sgen UINF") + } + + if w < 0 { + Fatal("sgen copy %d", w) + } + + // If copying .args, that's all the results, so record definition sites + // for them for the liveness analysis. + if ns.Op == ONAME && ns.Sym.Name == ".args" { + for l := Curfn.Func.Dcl; l != nil; l = l.Next { + if l.N.Class == PPARAMOUT { + Gvardef(l.N) + } + } + } + + // Avoid taking the address for simple enough types. + if componentgen_wb(n, ns, wb) { + return + } + + if w == 0 { + // evaluate side effects only + var nodr Node + Regalloc(&nodr, Types[Tptr], nil) + Agen(ns, &nodr) + Agen(n, &nodr) + Regfree(&nodr) + return + } + + // offset on the stack + osrc := stkof(n) + odst := stkof(ns) + + if odst != -1000 { + // on stack, write barrier not needed after all + wb = false + } + + if osrc != -1000 && odst != -1000 && (osrc == 1000 || odst == 1000) || wb && osrc != -1000 { + // osrc and odst both on stack, and at least one is in + // an unknown position. Could generate code to test + // for forward/backward copy, but instead just copy + // to a temporary location first. + // + // OR: write barrier needed and source is on stack. + // Invoking the write barrier will use the stack to prepare its call. + // Copy to temporary. + var tmp Node + Tempname(&tmp, n.Type) + sgen_wb(n, &tmp, w, false) + sgen_wb(&tmp, ns, w, wb) + return + } + + if wb { + cgen_wbfat(n, ns) + return + } + + Thearch.Blockcopy(n, ns, osrc, odst, w) +} + +/* + * generate: + * call f + * proc=-1 normal call but no return + * proc=0 normal call + * proc=1 goroutine run in new proc + * proc=2 defer call save away stack + * proc=3 normal call to C pointer (not Go func value) +*/ +func Ginscall(f *Node, proc int) { + if f.Type != nil { + extra := int32(0) + if proc == 1 || proc == 2 { + extra = 2 * int32(Widthptr) + } + Setmaxarg(f.Type, extra) + } + + switch proc { + default: + Fatal("Ginscall: bad proc %d", proc) + + case 0, // normal call + -1: // normal call but no return + if f.Op == ONAME && f.Class == PFUNC { + if f == Deferreturn { + // Deferred calls will appear to be returning to + // the CALL deferreturn(SB) that we are about to emit. + // However, the stack trace code will show the line + // of the instruction byte before the return PC. + // To avoid that being an unrelated instruction, + // insert an actual hardware NOP that will have the right line number. + // This is different from obj.ANOP, which is a virtual no-op + // that doesn't make it into the instruction stream. + Thearch.Ginsnop() + } + + p := Thearch.Gins(obj.ACALL, nil, f) + Afunclit(&p.To, f) + if proc == -1 || Noreturn(p) { + Thearch.Gins(obj.AUNDEF, nil, nil) + } + break + } + + var reg Node + Nodreg(®, Types[Tptr], Thearch.REGCTXT) + var r1 Node + Nodreg(&r1, Types[Tptr], Thearch.REGCALLX) + Thearch.Gmove(f, ®) + reg.Op = OINDREG + Thearch.Gmove(®, &r1) + reg.Op = OREGISTER + Thearch.Gins(obj.ACALL, ®, &r1) + + case 3: // normal call of c function pointer + Thearch.Gins(obj.ACALL, nil, f) + + case 1, // call in new proc (go) + 2: // deferred call (defer) + var stk Node + + // size of arguments at 0(SP) + stk.Op = OINDREG + stk.Reg = int16(Thearch.REGSP) + stk.Xoffset = 0 + if HasLinkRegister() { + stk.Xoffset += int64(Ctxt.Arch.Ptrsize) + } + Thearch.Ginscon(Thearch.Optoas(OAS, Types[TINT32]), int64(Argsize(f.Type)), &stk) + + // FuncVal* at 8(SP) + stk.Xoffset = int64(Widthptr) + if HasLinkRegister() { + stk.Xoffset += int64(Ctxt.Arch.Ptrsize) + } + + var reg Node + Nodreg(®, Types[Tptr], Thearch.REGCALLX2) + Thearch.Gmove(f, ®) + Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), ®, &stk) + + if proc == 1 { + Ginscall(Newproc, 0) + } else { + if Hasdefer == 0 { + Fatal("hasdefer=0 but has defer") + } + Ginscall(Deferproc, 0) + } + + if proc == 2 { + Nodreg(®, Types[TINT32], Thearch.REGRETURN) + p := Thearch.Ginscmp(OEQ, Types[TINT32], ®, Nodintconst(0), +1) + cgen_ret(nil) + Patch(p, Pc) + } + } +} + +/* + * n is call to interface method. + * generate res = n. + */ +func cgen_callinter(n *Node, res *Node, proc int) { + i := n.Left + if i.Op != ODOTINTER { + Fatal("cgen_callinter: not ODOTINTER %v", Oconv(int(i.Op), 0)) + } + + f := i.Right // field + if f.Op != ONAME { + Fatal("cgen_callinter: not ONAME %v", Oconv(int(f.Op), 0)) + } + + i = i.Left // interface + + if !i.Addable { + var tmpi Node + Tempname(&tmpi, i.Type) + Cgen(i, &tmpi) + i = &tmpi + } + + Genlist(n.List) // assign the args + + // i is now addable, prepare an indirected + // register to hold its address. + var nodi Node + Igen(i, &nodi, res) // REG = &inter + + var nodsp Node + Nodindreg(&nodsp, Types[Tptr], Thearch.REGSP) + nodsp.Xoffset = 0 + if HasLinkRegister() { + nodsp.Xoffset += int64(Ctxt.Arch.Ptrsize) + } + if proc != 0 { + nodsp.Xoffset += 2 * int64(Widthptr) // leave room for size & fn + } + nodi.Type = Types[Tptr] + nodi.Xoffset += int64(Widthptr) + Cgen(&nodi, &nodsp) // {0, 8(nacl), or 16}(SP) = 8(REG) -- i.data + + var nodo Node + Regalloc(&nodo, Types[Tptr], res) + + nodi.Type = Types[Tptr] + nodi.Xoffset -= int64(Widthptr) + Cgen(&nodi, &nodo) // REG = 0(REG) -- i.tab + Regfree(&nodi) + + var nodr Node + Regalloc(&nodr, Types[Tptr], &nodo) + if n.Left.Xoffset == BADWIDTH { + Fatal("cgen_callinter: badwidth") + } + Cgen_checknil(&nodo) // in case offset is huge + nodo.Op = OINDREG + nodo.Xoffset = n.Left.Xoffset + 3*int64(Widthptr) + 8 + if proc == 0 { + // plain call: use direct c function pointer - more efficient + Cgen(&nodo, &nodr) // REG = 32+offset(REG) -- i.tab->fun[f] + proc = 3 + } else { + // go/defer. generate go func value. + Agen(&nodo, &nodr) // REG = &(32+offset(REG)) -- i.tab->fun[f] + } + + nodr.Type = n.Left.Type + Ginscall(&nodr, proc) + + Regfree(&nodr) + Regfree(&nodo) +} + +/* + * generate function call; + * proc=0 normal call + * proc=1 goroutine run in new proc + * proc=2 defer call save away stack + */ +func cgen_call(n *Node, proc int) { + if n == nil { + return + } + + var afun Node + if n.Left.Ullman >= UINF { + // if name involves a fn call + // precompute the address of the fn + Tempname(&afun, Types[Tptr]) + + Cgen(n.Left, &afun) + } + + Genlist(n.List) // assign the args + t := n.Left.Type + + // call tempname pointer + if n.Left.Ullman >= UINF { + var nod Node + Regalloc(&nod, Types[Tptr], nil) + Cgen_as(&nod, &afun) + nod.Type = t + Ginscall(&nod, proc) + Regfree(&nod) + return + } + + // call pointer + if n.Left.Op != ONAME || n.Left.Class != PFUNC { + var nod Node + Regalloc(&nod, Types[Tptr], nil) + Cgen_as(&nod, n.Left) + nod.Type = t + Ginscall(&nod, proc) + Regfree(&nod) + return + } + + // call direct + n.Left.Name.Method = true + + Ginscall(n.Left, proc) +} + +func HasLinkRegister() bool { + c := Ctxt.Arch.Thechar + return c != '6' && c != '8' +} + +/* + * call to n has already been generated. + * generate: + * res = return value from call. + */ +func cgen_callret(n *Node, res *Node) { + t := n.Left.Type + if t.Etype == TPTR32 || t.Etype == TPTR64 { + t = t.Type + } + + var flist Iter + fp := Structfirst(&flist, Getoutarg(t)) + if fp == nil { + Fatal("cgen_callret: nil") + } + + var nod Node + nod.Op = OINDREG + nod.Reg = int16(Thearch.REGSP) + nod.Addable = true + + nod.Xoffset = fp.Width + if HasLinkRegister() { + nod.Xoffset += int64(Ctxt.Arch.Ptrsize) + } + nod.Type = fp.Type + Cgen_as(res, &nod) +} + +/* + * call to n has already been generated. + * generate: + * res = &return value from call. + */ +func cgen_aret(n *Node, res *Node) { + t := n.Left.Type + if Isptr[t.Etype] { + t = t.Type + } + + var flist Iter + fp := Structfirst(&flist, Getoutarg(t)) + if fp == nil { + Fatal("cgen_aret: nil") + } + + var nod1 Node + nod1.Op = OINDREG + nod1.Reg = int16(Thearch.REGSP) + nod1.Addable = true + nod1.Xoffset = fp.Width + if HasLinkRegister() { + nod1.Xoffset += int64(Ctxt.Arch.Ptrsize) + } + nod1.Type = fp.Type + + if res.Op != OREGISTER { + var nod2 Node + Regalloc(&nod2, Types[Tptr], res) + Agen(&nod1, &nod2) + Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &nod2, res) + Regfree(&nod2) + } else { + Agen(&nod1, res) + } +} + +/* + * generate return. + * n->left is assignments to return values. + */ +func cgen_ret(n *Node) { + if n != nil { + Genlist(n.List) // copy out args + } + if Hasdefer != 0 { + Ginscall(Deferreturn, 0) + } + Genlist(Curfn.Func.Exit) + p := Thearch.Gins(obj.ARET, nil, nil) + if n != nil && n.Op == ORETJMP { + p.To.Type = obj.TYPE_MEM + p.To.Name = obj.NAME_EXTERN + p.To.Sym = Linksym(n.Left.Sym) + } +} + +/* + * generate division according to op, one of: + * res = nl / nr + * res = nl % nr + */ +func cgen_div(op int, nl *Node, nr *Node, res *Node) { + var w int + + // TODO(rsc): arm64 needs to support the relevant instructions + // in peep and optoas in order to enable this. + // TODO(rsc): ppc64 needs to support the relevant instructions + // in peep and optoas in order to enable this. + if nr.Op != OLITERAL || Ctxt.Arch.Thechar == '7' || Ctxt.Arch.Thechar == '9' { + goto longdiv + } + w = int(nl.Type.Width * 8) + + // Front end handled 32-bit division. We only need to handle 64-bit. + // try to do division by multiply by (2^w)/d + // see hacker's delight chapter 10 + switch Simtype[nl.Type.Etype] { + default: + goto longdiv + + case TUINT64: + var m Magic + m.W = w + m.Ud = uint64(Mpgetfix(nr.Val.U.(*Mpint))) + Umagic(&m) + if m.Bad != 0 { + break + } + if op == OMOD { + goto longmod + } + + var n1 Node + Cgenr(nl, &n1, nil) + var n2 Node + Nodconst(&n2, nl.Type, int64(m.Um)) + var n3 Node + Regalloc(&n3, nl.Type, res) + Thearch.Cgen_hmul(&n1, &n2, &n3) + + if m.Ua != 0 { + // need to add numerator accounting for overflow + Thearch.Gins(Thearch.Optoas(OADD, nl.Type), &n1, &n3) + + Nodconst(&n2, nl.Type, 1) + Thearch.Gins(Thearch.Optoas(ORROTC, nl.Type), &n2, &n3) + Nodconst(&n2, nl.Type, int64(m.S)-1) + Thearch.Gins(Thearch.Optoas(ORSH, nl.Type), &n2, &n3) + } else { + Nodconst(&n2, nl.Type, int64(m.S)) + Thearch.Gins(Thearch.Optoas(ORSH, nl.Type), &n2, &n3) // shift dx + } + + Thearch.Gmove(&n3, res) + Regfree(&n1) + Regfree(&n3) + return + + case TINT64: + var m Magic + m.W = w + m.Sd = Mpgetfix(nr.Val.U.(*Mpint)) + Smagic(&m) + if m.Bad != 0 { + break + } + if op == OMOD { + goto longmod + } + + var n1 Node + Cgenr(nl, &n1, res) + var n2 Node + Nodconst(&n2, nl.Type, m.Sm) + var n3 Node + Regalloc(&n3, nl.Type, nil) + Thearch.Cgen_hmul(&n1, &n2, &n3) + + if m.Sm < 0 { + // need to add numerator + Thearch.Gins(Thearch.Optoas(OADD, nl.Type), &n1, &n3) + } + + Nodconst(&n2, nl.Type, int64(m.S)) + Thearch.Gins(Thearch.Optoas(ORSH, nl.Type), &n2, &n3) // shift n3 + + Nodconst(&n2, nl.Type, int64(w)-1) + + Thearch.Gins(Thearch.Optoas(ORSH, nl.Type), &n2, &n1) // -1 iff num is neg + Thearch.Gins(Thearch.Optoas(OSUB, nl.Type), &n1, &n3) // added + + if m.Sd < 0 { + // this could probably be removed + // by factoring it into the multiplier + Thearch.Gins(Thearch.Optoas(OMINUS, nl.Type), nil, &n3) + } + + Thearch.Gmove(&n3, res) + Regfree(&n1) + Regfree(&n3) + return + } + + goto longdiv + + // division and mod using (slow) hardware instruction +longdiv: + Thearch.Dodiv(op, nl, nr, res) + + return + + // mod using formula A%B = A-(A/B*B) but + // we know that there is a fast algorithm for A/B +longmod: + var n1 Node + Regalloc(&n1, nl.Type, res) + + Cgen(nl, &n1) + var n2 Node + Regalloc(&n2, nl.Type, nil) + cgen_div(ODIV, &n1, nr, &n2) + a := Thearch.Optoas(OMUL, nl.Type) + if w == 8 { + // use 2-operand 16-bit multiply + // because there is no 2-operand 8-bit multiply + a = Thearch.Optoas(OMUL, Types[TINT16]) // XXX was IMULW + } + + if !Smallintconst(nr) { + var n3 Node + Regalloc(&n3, nl.Type, nil) + Cgen(nr, &n3) + Thearch.Gins(a, &n3, &n2) + Regfree(&n3) + } else { + Thearch.Gins(a, nr, &n2) + } + Thearch.Gins(Thearch.Optoas(OSUB, nl.Type), &n2, &n1) + Thearch.Gmove(&n1, res) + Regfree(&n1) + Regfree(&n2) +} + +func Fixlargeoffset(n *Node) { + if n == nil { + return + } + if n.Op != OINDREG { + return + } + if n.Reg == int16(Thearch.REGSP) { // stack offset cannot be large + return + } + if n.Xoffset != int64(int32(n.Xoffset)) { + // offset too large, add to register instead. + a := *n + + a.Op = OREGISTER + a.Type = Types[Tptr] + a.Xoffset = 0 + Cgen_checknil(&a) + Thearch.Ginscon(Thearch.Optoas(OADD, Types[Tptr]), n.Xoffset, &a) + 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++ + } +} + +// 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) { + 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) + // 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.(string)))) + 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.(string))) + } + if Isconst(&i, CTINT) { + 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.(*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.(*Mpint), 0) < 0 || bound >= 0 && mpcmpfixc(k.Val.U.(*Mpint), 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.(*Mpint), n2.Val.U.(*Mpint)) == 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.(*Mpint), n2.Val.U.(*Mpint)) <= 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) { + // 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) + } + + 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.(*Mpint))-Mpgetfix(i.Val.U.(*Mpint))) + if Debug_slice > 0 { + Warn("slice: result len == %d", Mpgetfix(j.Val.U.(*Mpint))) + } + 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.(*Mpint)) + 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.(*Mpint))-Mpgetfix(i.Val.U.(*Mpint))) + if Debug_slice > 0 { + Warn("slice: result cap == %d", Mpgetfix(k.Val.U.(*Mpint))) + } + 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.(*Mpint)) + 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.(*Mpint))*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/compile/internal/gc/closure.go b/src/cmd/compile/internal/gc/closure.go new file mode 100644 index 0000000000..b51e74b77d --- /dev/null +++ b/src/cmd/compile/internal/gc/closure.go @@ -0,0 +1,689 @@ +// Copyright 2009 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 gc + +import ( + "cmd/internal/obj" + "fmt" +) + +/* + * function literals aka closures + */ +func closurehdr(ntype *Node) { + var name *Node + var a *Node + + n := Nod(OCLOSURE, nil, nil) + n.Ntype = ntype + n.Funcdepth = Funcdepth + n.Func.Outerfunc = Curfn + + funchdr(n) + + // steal ntype's argument names and + // leave a fresh copy in their place. + // references to these variables need to + // refer to the variables in the external + // function declared below; see walkclosure. + n.List = ntype.List + + n.Rlist = ntype.Rlist + ntype.List = nil + ntype.Rlist = nil + for l := n.List; l != nil; l = l.Next { + name = l.N.Left + if name != nil { + name = newname(name.Sym) + } + a = Nod(ODCLFIELD, name, l.N.Right) + a.Isddd = l.N.Isddd + if name != nil { + name.Isddd = a.Isddd + } + ntype.List = list(ntype.List, a) + } + + for l := n.Rlist; l != nil; l = l.Next { + name = l.N.Left + if name != nil { + name = newname(name.Sym) + } + ntype.Rlist = list(ntype.Rlist, Nod(ODCLFIELD, name, l.N.Right)) + } +} + +func closurebody(body *NodeList) *Node { + if body == nil { + body = list1(Nod(OEMPTY, nil, nil)) + } + + func_ := Curfn + func_.Nbody = body + func_.Func.Endlineno = lineno + funcbody(func_) + + // closure-specific variables are hanging off the + // ordinary ones in the symbol table; see oldname. + // unhook them. + // make the list of pointers for the closure call. + 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) + } + + return func_ +} + +func typecheckclosure(func_ *Node, top int) { + var n *Node + + for l := func_.Func.Cvars; l != nil; l = l.Next { + n = l.N.Closure + 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.Name.Decldepth == decldepth { + n.Assigned = false + } + } + } + + 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.Name.Decldepth = 1 + } + } + + oldfn := Curfn + typecheck(&func_.Ntype, Etype) + func_.Type = func_.Ntype.Type + func_.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 + // ready to type check code yet; we'll check it later, because the + // underlying closure function we create is added to xtop. + if Curfn != nil && func_.Type != nil { + Curfn = func_ + olddd := decldepth + decldepth = 1 + typechecklist(func_.Nbody, Etop) + decldepth = olddd + Curfn = oldfn + } + + // Create top-level function + xtop = list(xtop, makeclosure(func_)) +} + +// closurename returns name for OCLOSURE n. +// It is not as simple as it ought to be, because we typecheck nested closures +// starting from the innermost one. So when we check the inner closure, +// we don't yet have name for the outer closure. This function uses recursion +// to generate names all the way up if necessary. + +var closurename_closgen int + +func closurename(n *Node) *Sym { + if n.Sym != nil { + return n.Sym + } + gen := 0 + outer := "" + prefix := "" + if n.Func.Outerfunc == nil { + // Global closure. + outer = "glob" + + prefix = "func" + closurename_closgen++ + gen = closurename_closgen + } else if n.Func.Outerfunc.Op == ODCLFUNC { + // The outermost closure inside of a named function. + outer = n.Func.Outerfunc.Nname.Sym.Name + + prefix = "func" + + // Yes, functions can be named _. + // Can't use function closgen in such case, + // because it would lead to name clashes. + if !isblank(n.Func.Outerfunc.Nname) { + n.Func.Outerfunc.Func.Closgen++ + gen = n.Func.Outerfunc.Func.Closgen + } else { + closurename_closgen++ + gen = closurename_closgen + } + } else if n.Func.Outerfunc.Op == OCLOSURE { + // Nested closure, recurse. + outer = closurename(n.Func.Outerfunc).Name + + prefix = "" + n.Func.Outerfunc.Func.Closgen++ + gen = n.Func.Outerfunc.Func.Closgen + } else { + Fatal("closurename called for %v", Nconv(n, obj.FmtShort)) + } + n.Sym = Lookupf("%s.%s%d", outer, prefix, gen) + return n.Sym +} + +func makeclosure(func_ *Node) *Node { + /* + * wrap body in external function + * that begins by reading closure parameters. + */ + xtype := Nod(OTFUNC, nil, nil) + + xtype.List = func_.List + xtype.Rlist = func_.Rlist + + // create the function + xfunc := Nod(ODCLFUNC, nil, nil) + + xfunc.Nname = newfuncname(closurename(func_)) + xfunc.Nname.Sym.Flags |= SymExported // disable export + xfunc.Nname.Ntype = xtype + xfunc.Nname.Defn = xfunc + declare(xfunc.Nname, PFUNC) + xfunc.Nname.Funcdepth = func_.Funcdepth + xfunc.Funcdepth = func_.Funcdepth + xfunc.Func.Endlineno = func_.Func.Endlineno + + xfunc.Nbody = func_.Nbody + xfunc.Func.Dcl = concat(func_.Func.Dcl, xfunc.Func.Dcl) + if xfunc.Nbody == nil { + Fatal("empty body - won't generate any code") + } + typecheck(&xfunc, Etop) + + xfunc.Closure = func_ + func_.Closure = xfunc + + func_.Nbody = nil + func_.List = nil + func_.Rlist = nil + + return xfunc +} + +// capturevars is called in a separate phase after all typechecking is done. +// It decides whether each variable captured by a closure should be captured +// by value or by reference. +// We use value capturing for values <= 128 bytes that are never reassigned +// after capturing (effectively constant). +func capturevars(xfunc *Node) { + var v *Node + var outer *Node + + lno := int(lineno) + lineno = xfunc.Lineno + + func_ := xfunc.Closure + func_.Func.Enter = nil + for l := func_.Func.Cvars; l != nil; l = l.Next { + v = l.N + if v.Type == nil { + // if v->type is nil, it means v looked like it was + // going to be used in the closure but wasn't. + // this happens because when parsing a, b, c := f() + // the a, b, c gets parsed as references to older + // a, b, c before the parser figures out this is a + // declaration. + v.Op = OXXX + + continue + } + + // type check the & of closed variables outside the closure, + // so that the outer frame also grabs them and knows they escape. + dowidth(v.Type) + + outer = v.Outerexpr + v.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 { + v.Name.Byval = true + } else { + v.Closure.Addrtaken = true + outer = Nod(OADDR, outer, nil) + } + + if Debug['m'] > 1 { + var name *Sym + if v.Curfn != nil && v.Curfn.Nname != nil { + name = v.Curfn.Nname.Sym + } + how := "ref" + 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)) + } + + typecheck(&outer, Erv) + func_.Func.Enter = list(func_.Func.Enter, outer) + } + + lineno = int32(lno) +} + +// transformclosure is called in a separate phase after escape analysis. +// It transform closure bodies to properly reference captured variables. +func transformclosure(xfunc *Node) { + lno := int(lineno) + lineno = xfunc.Lineno + func_ := xfunc.Closure + + if func_.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) + // will complete the transformation later. + // For illustration, the following closure: + // func(a int) { + // println(byval) + // byref++ + // }(42) + // becomes: + // func(a int, byval int, &byref *int) { + // println(byval) + // (*&byref)++ + // }(42, byval, &byref) + + // f is ONAME of the actual function. + f := xfunc.Nname + + // Get pointer to input arguments and rewind to the end. + // We are going to append captured variables to input args. + param := &getinargx(f.Type).Type + + for ; *param != nil; param = &(*param).Down { + } + var v *Node + var addr *Node + var fld *Type + for l := func_.Func.Cvars; l != nil; l = l.Next { + v = l.N + if v.Op == OXXX { + continue + } + fld = typ(TFIELD) + fld.Funarg = 1 + if v.Name.Byval { + // If v is captured by value, we merely downgrade it to PPARAM. + v.Class = PPARAM + + v.Ullman = 1 + fld.Nname = v + } else { + // If v of type T is captured by reference, + // we introduce function param &v *T + // and v remains PPARAMREF with &v heapaddr + // (accesses will implicitly deref &v). + addr = newname(Lookupf("&%s", v.Sym.Name)) + addr.Type = Ptrto(v.Type) + addr.Class = PPARAM + v.Name.Heapaddr = addr + fld.Nname = addr + } + + fld.Type = fld.Nname.Type + fld.Sym = fld.Nname.Sym + + // Declare the new param and append it to input arguments. + xfunc.Func.Dcl = list(xfunc.Func.Dcl, fld.Nname) + + *param = fld + param = &fld.Down + } + + // Recalculate param offsets. + if f.Type.Width > 0 { + Fatal("transformclosure: width is already calculated") + } + dowidth(f.Type) + xfunc.Type = f.Type // update type of ODCLFUNC + } else { + // The closure is not called, so it is going to stay as closure. + nvar := 0 + + var body *NodeList + offset := int64(Widthptr) + var addr *Node + var v *Node + var cv *Node + for l := func_.Func.Cvars; l != nil; l = l.Next { + v = l.N + if v.Op == OXXX { + continue + } + nvar++ + + // cv refers to the field inside of closure OSTRUCTLIT. + cv = Nod(OCLOSUREVAR, nil, nil) + + cv.Type = v.Type + 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.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 + v.Class = PAUTO + + v.Ullman = 1 + xfunc.Func.Dcl = list(xfunc.Func.Dcl, v) + body = list(body, Nod(OAS, v, cv)) + } else { + // 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.Class = PAUTO + addr.Used = true + addr.Curfn = xfunc + xfunc.Func.Dcl = list(xfunc.Func.Dcl, addr) + v.Name.Heapaddr = addr + if v.Name.Byval { + cv = Nod(OADDR, cv, nil) + } + body = list(body, Nod(OAS, addr, cv)) + } + } + + typechecklist(body, Etop) + walkstmtlist(body) + xfunc.Func.Enter = body + xfunc.Func.Needctxt = nvar > 0 + } + + lineno = int32(lno) +} + +func walkclosure(func_ *Node, init **NodeList) *Node { + // If no closure vars, don't bother wrapping. + if func_.Func.Cvars == nil { + return func_.Closure.Nname + } + + // Create closure in the form of a composite literal. + // supposing the closure captures an int i and a string s + // and has one float64 argument and no results, + // the generated code looks like: + // + // clos = &struct{.F uintptr; i *int; s *string}{func.1, &i, &s} + // + // The use of the struct provides type information to the garbage + // collector so that it can walk the closure. We could use (in this case) + // [3]unsafe.Pointer instead, but that would leave the gc in the dark. + // The information appears in the binary in the form of type descriptors; + // the struct is unnamed so that closures in multiple packages with the + // same struct type can share the descriptor. + + typ := Nod(OTSTRUCT, nil, nil) + + typ.List = list1(Nod(ODCLFIELD, newname(Lookup(".F")), typenod(Types[TUINTPTR]))) + var typ1 *Node + var v *Node + for l := func_.Func.Cvars; l != nil; l = l.Next { + v = l.N + if v.Op == OXXX { + continue + } + typ1 = typenod(v.Type) + if !v.Name.Byval { + typ1 = Nod(OIND, typ1, nil) + } + typ.List = list(typ.List, Nod(ODCLFIELD, newname(v.Sym), typ1)) + } + + 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) + + // Force type conversion from *struct to the func type. + clos = Nod(OCONVNOP, clos, nil) + + clos.Type = func_.Type + + typecheck(&clos, Erv) + + // typecheck will insert a PTRLIT node under CONVNOP, + // tag it with escape analysis result. + clos.Left.Esc = func_.Esc + + // non-escaping temp to use, if any. + // orderexpr did not compute the type; fill it in now. + if func_.Alloc != nil { + func_.Alloc.Type = clos.Left.Left.Type + func_.Alloc.Orig.Type = func_.Alloc.Type + clos.Left.Right = func_.Alloc + func_.Alloc = nil + } + + walkexpr(&clos, init) + + return clos +} + +func typecheckpartialcall(fn *Node, sym *Node) { + switch fn.Op { + case ODOTINTER, ODOTMETH: + break + + default: + Fatal("invalid typecheckpartialcall") + } + + // Create top-level function. + fn.Nname = makepartialcall(fn, fn.Type, sym) + + fn.Right = sym + fn.Op = OCALLPART + fn.Type = fn.Nname.Type +} + +var makepartialcall_gopkg *Pkg + +func makepartialcall(fn *Node, t0 *Type, meth *Node) *Node { + var p string + + rcvrtype := fn.Left.Type + if exportname(meth.Sym.Name) { + p = fmt.Sprintf("(%v).%s-fm", Tconv(rcvrtype, obj.FmtLeft|obj.FmtShort), meth.Sym.Name) + } else { + p = fmt.Sprintf("(%v).(%v)-fm", Tconv(rcvrtype, obj.FmtLeft|obj.FmtShort), Sconv(meth.Sym, obj.FmtLeft)) + } + basetype := rcvrtype + if Isptr[rcvrtype.Etype] { + basetype = basetype.Type + } + if basetype.Etype != TINTER && basetype.Sym == nil { + Fatal("missing base type for %v", rcvrtype) + } + + var spkg *Pkg + if basetype.Sym != nil { + spkg = basetype.Sym.Pkg + } + if spkg == nil { + if makepartialcall_gopkg == nil { + makepartialcall_gopkg = mkpkg("go") + } + spkg = makepartialcall_gopkg + } + + sym := Pkglookup(p, spkg) + + if sym.Flags&SymUniq != 0 { + return sym.Def + } + sym.Flags |= SymUniq + + savecurfn := Curfn + Curfn = nil + + xtype := Nod(OTFUNC, nil, nil) + i := 0 + var l *NodeList + var callargs *NodeList + ddd := false + xfunc := Nod(ODCLFUNC, nil, nil) + Curfn = xfunc + var fld *Node + var n *Node + for t := getinargx(t0).Type; t != nil; t = t.Down { + n = newname(Lookupf("a%d", i)) + i++ + n.Class = PPARAM + xfunc.Func.Dcl = list(xfunc.Func.Dcl, n) + callargs = list(callargs, n) + fld = Nod(ODCLFIELD, n, typenod(t.Type)) + if t.Isddd { + fld.Isddd = true + ddd = true + } + + l = list(l, fld) + } + + xtype.List = l + i = 0 + l = nil + var retargs *NodeList + for t := getoutargx(t0).Type; t != nil; t = t.Down { + n = newname(Lookupf("r%d", i)) + i++ + n.Class = PPARAMOUT + xfunc.Func.Dcl = list(xfunc.Func.Dcl, n) + retargs = list(retargs, n) + l = list(l, Nod(ODCLFIELD, n, typenod(t.Type))) + } + + xtype.Rlist = l + + xfunc.Func.Dupok = true + xfunc.Nname = newfuncname(sym) + xfunc.Nname.Sym.Flags |= SymExported // disable export + xfunc.Nname.Ntype = xtype + xfunc.Nname.Defn = xfunc + declare(xfunc.Nname, PFUNC) + + // Declare and initialize variable holding receiver. + + xfunc.Func.Needctxt = true + cv := Nod(OCLOSUREVAR, nil, nil) + cv.Xoffset = int64(Widthptr) + cv.Type = rcvrtype + if int(cv.Type.Align) > Widthptr { + cv.Xoffset = int64(cv.Type.Align) + } + ptr := Nod(ONAME, nil, nil) + ptr.Sym = Lookup("rcvr") + ptr.Class = PAUTO + ptr.Addable = true + ptr.Ullman = 1 + ptr.Used = true + ptr.Curfn = xfunc + xfunc.Func.Dcl = list(xfunc.Func.Dcl, ptr) + var body *NodeList + if Isptr[rcvrtype.Etype] || Isinter(rcvrtype) { + ptr.Ntype = typenod(rcvrtype) + body = list(body, Nod(OAS, ptr, cv)) + } else { + ptr.Ntype = typenod(Ptrto(rcvrtype)) + body = list(body, Nod(OAS, ptr, Nod(OADDR, cv, nil))) + } + + call := Nod(OCALL, Nod(OXDOT, ptr, meth), nil) + call.List = callargs + call.Isddd = ddd + if t0.Outtuple == 0 { + body = list(body, call) + } else { + n := Nod(OAS2, nil, nil) + n.List = retargs + n.Rlist = list1(call) + body = list(body, n) + n = Nod(ORETURN, nil, nil) + body = list(body, n) + } + + xfunc.Nbody = body + + typecheck(&xfunc, Etop) + sym.Def = xfunc + xtop = list(xtop, xfunc) + Curfn = savecurfn + + return xfunc +} + +func walkpartialcall(n *Node, init **NodeList) *Node { + // Create closure in the form of a composite literal. + // For x.M with receiver (x) type T, the generated code looks like: + // + // clos = &struct{F uintptr; R T}{M.T·f, x} + // + // Like walkclosure above. + + if Isinter(n.Left.Type) { + // Trigger panic for method on nil interface now. + // Otherwise it happens in the wrapper and is confusing. + n.Left = cheapexpr(n.Left, init) + + checknil(n.Left, init) + } + + typ := Nod(OTSTRUCT, nil, nil) + typ.List = list1(Nod(ODCLFIELD, newname(Lookup("F")), typenod(Types[TUINTPTR]))) + typ.List = list(typ.List, Nod(ODCLFIELD, newname(Lookup("R")), typenod(n.Left.Type))) + + clos := Nod(OCOMPLIT, nil, Nod(OIND, typ, nil)) + clos.Esc = n.Esc + clos.Right.Implicit = true + clos.List = list1(Nod(OCFUNC, n.Nname.Nname, nil)) + clos.List = list(clos.List, n.Left) + + // Force type conversion from *struct to the func type. + clos = Nod(OCONVNOP, clos, nil) + + clos.Type = n.Type + + typecheck(&clos, Erv) + + // typecheck will insert a PTRLIT node under CONVNOP, + // tag it with escape analysis result. + clos.Left.Esc = n.Esc + + // non-escaping temp to use, if any. + // orderexpr did not compute the type; fill it in now. + if n.Alloc != nil { + n.Alloc.Type = clos.Left.Left.Type + n.Alloc.Orig.Type = n.Alloc.Type + clos.Left.Right = n.Alloc + n.Alloc = nil + } + + walkexpr(&clos, init) + + return clos +} diff --git a/src/cmd/compile/internal/gc/const.go b/src/cmd/compile/internal/gc/const.go new file mode 100644 index 0000000000..b3605ab206 --- /dev/null +++ b/src/cmd/compile/internal/gc/const.go @@ -0,0 +1,1717 @@ +// Copyright 2009 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 gc + +import ( + "cmd/compile/internal/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.(*Mpint)) +} + +// 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.(*Mpint), 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.(*Mpint).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.(bool) +} + +/* + * truncate float literal fv to 32-bit or 64-bit precision + * according to type; return truncated value. + */ +func truncfltlit(oldv *Mpflt, t *Type) *Mpflt { + if t == nil { + return oldv + } + + var v Val + v.Ctype = CTFLT + v.U = oldv + overflow(v, t) + + fv := newMpflt() + mpmovefltflt(fv, oldv) + + // convert large precision literal floating + // into limited precision (float64 or float32) + switch t.Etype { + case TFLOAT64: + d := mpgetflt(fv) + Mpmovecflt(fv, d) + + case TFLOAT32: + d := mpgetflt32(fv) + Mpmovecflt(fv, d) + } + + return fv +} + +/* + * convert n, if literal, to type t. + * implicit conversion. + */ +func Convlit(np **Node, t *Type) { + convlit1(np, t, false) +} + +/* + * convert n, if literal, to type t. + * return a new node if necessary + * (if n is a named constant, can't edit n->type directly). + */ +func convlit1(np **Node, t *Type, explicit bool) { + n := *np + if n == nil || t == nil || n.Type == nil || isideal(t) || n.Type == t { + return + } + if !explicit && !isideal(n.Type) { + return + } + + if n.Op == OLITERAL { + nn := Nod(OXXX, nil, nil) + *nn = *n + n = nn + *np = n + } + + switch n.Op { + default: + if n.Type == idealbool { + if t.Etype == TBOOL { + n.Type = t + } else { + n.Type = Types[TBOOL] + } + } + + if n.Type.Etype == TIDEAL { + Convlit(&n.Left, t) + Convlit(&n.Right, t) + n.Type = t + } + + return + + // target is invalid type for a constant? leave alone. + case OLITERAL: + if !okforconst[t.Etype] && n.Type.Etype != TNIL { + defaultlit(&n, nil) + *np = n + return + } + + case OLSH, ORSH: + convlit1(&n.Left, t, explicit && isideal(n.Left.Type)) + t = n.Left.Type + if t != nil && t.Etype == TIDEAL && n.Val.Ctype != CTINT { + n.Val = toint(n.Val) + } + if t != nil && !Isint[t.Etype] { + Yyerror("invalid operation: %v (shift of type %v)", n, t) + t = nil + } + + n.Type = t + return + + case OCOMPLEX: + if n.Type.Etype == TIDEAL { + switch t.Etype { + // If trying to convert to non-complex type, + // leave as complex128 and let typechecker complain. + default: + t = Types[TCOMPLEX128] + fallthrough + + //fallthrough + case TCOMPLEX128: + n.Type = t + + Convlit(&n.Left, Types[TFLOAT64]) + Convlit(&n.Right, Types[TFLOAT64]) + + case TCOMPLEX64: + n.Type = t + Convlit(&n.Left, Types[TFLOAT32]) + Convlit(&n.Right, Types[TFLOAT32]) + } + } + + return + } + + // avoided repeated calculations, errors + if Eqtype(n.Type, t) { + return + } + + ct := consttype(n) + var et int + if ct < 0 { + goto bad + } + + et = int(t.Etype) + if et == TINTER { + if ct == CTNIL && n.Type == Types[TNIL] { + n.Type = t + return + } + + defaultlit(np, nil) + return + } + + switch ct { + default: + goto bad + + case CTNIL: + switch et { + default: + n.Type = nil + goto bad + + // let normal conversion code handle it + case TSTRING: + return + + case TARRAY: + if !Isslice(t) { + goto bad + } + + case TPTR32, + TPTR64, + TINTER, + TMAP, + TCHAN, + TFUNC, + TUNSAFEPTR: + break + + // A nil literal may be converted to uintptr + // if it is an unsafe.Pointer + case TUINTPTR: + if n.Type.Etype == TUNSAFEPTR { + n.Val.U = new(Mpint) + Mpmovecfix(n.Val.U.(*Mpint), 0) + n.Val.Ctype = CTINT + } else { + goto bad + } + } + + case CTSTR, CTBOOL: + if et != int(n.Type.Etype) { + goto bad + } + + 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 { + default: + goto bad + + case CTCPLX, CTFLT, CTRUNE: + n.Val = toint(n.Val) + fallthrough + + // flowthrough + case CTINT: + overflow(n.Val, t) + } + } else if Isfloat[et] { + switch ct { + default: + goto bad + + case CTCPLX, CTINT, CTRUNE: + n.Val = toflt(n.Val) + fallthrough + + // flowthrough + case CTFLT: + n.Val.U = truncfltlit(n.Val.U.(*Mpflt), t) + } + } else if Iscomplex[et] { + switch ct { + default: + goto bad + + case CTFLT, CTINT, CTRUNE: + n.Val = tocplx(n.Val) + + case CTCPLX: + overflow(n.Val, t) + } + } else if et == TSTRING && (ct == CTINT || ct == CTRUNE) && explicit { + n.Val = tostr(n.Val) + } else { + goto bad + } + } + + n.Type = t + return + +bad: + if n.Diag == 0 { + if t.Broke == 0 { + Yyerror("cannot convert %v to type %v", n, t) + } + n.Diag = 1 + } + + if isideal(n.Type) { + defaultlit(&n, nil) + *np = n + } +} + +func copyval(v Val) Val { + switch v.Ctype { + case CTINT, CTRUNE: + i := new(Mpint) + mpmovefixfix(i, v.U.(*Mpint)) + v.U = i + + case CTFLT: + f := newMpflt() + mpmovefltflt(f, v.U.(*Mpflt)) + v.U = f + + case CTCPLX: + c := new(Mpcplx) + mpmovefltflt(&c.Real, &v.U.(*Mpcplx).Real) + mpmovefltflt(&c.Imag, &v.U.(*Mpcplx).Imag) + v.U = c + } + + return v +} + +func tocplx(v Val) Val { + switch v.Ctype { + case CTINT, CTRUNE: + c := new(Mpcplx) + Mpmovefixflt(&c.Real, v.U.(*Mpint)) + Mpmovecflt(&c.Imag, 0.0) + v.Ctype = CTCPLX + v.U = c + + case CTFLT: + c := new(Mpcplx) + mpmovefltflt(&c.Real, v.U.(*Mpflt)) + Mpmovecflt(&c.Imag, 0.0) + v.Ctype = CTCPLX + v.U = c + } + + return v +} + +func toflt(v Val) Val { + switch v.Ctype { + case CTINT, CTRUNE: + f := newMpflt() + Mpmovefixflt(f, v.U.(*Mpint)) + v.Ctype = CTFLT + v.U = f + + case CTCPLX: + f := newMpflt() + 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 = f + } + + return v +} + +func toint(v Val) Val { + switch v.Ctype { + case CTRUNE: + v.Ctype = CTINT + + case CTFLT: + i := new(Mpint) + if mpmovefltfix(i, v.U.(*Mpflt)) < 0 { + Yyerror("constant %v truncated to integer", Fconv(v.U.(*Mpflt), obj.FmtSharp)) + } + v.Ctype = CTINT + v.U = i + + case CTCPLX: + i := new(Mpint) + 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.(*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 = i + } + + return v +} + +func doesoverflow(v Val, t *Type) bool { + switch v.Ctype { + case CTINT, CTRUNE: + if !Isint[t.Etype] { + Fatal("overflow: %v integer constant", t) + } + if Mpcmpfixfix(v.U.(*Mpint), Minintval[t.Etype]) < 0 || Mpcmpfixfix(v.U.(*Mpint), Maxintval[t.Etype]) > 0 { + return true + } + + case CTFLT: + if !Isfloat[t.Etype] { + Fatal("overflow: %v floating-point constant", t) + } + if mpcmpfltflt(v.U.(*Mpflt), minfltval[t.Etype]) <= 0 || mpcmpfltflt(v.U.(*Mpflt), maxfltval[t.Etype]) >= 0 { + return true + } + + case CTCPLX: + if !Iscomplex[t.Etype] { + Fatal("overflow: %v complex constant", t) + } + 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 + } + } + + return false +} + +func overflow(v Val, t *Type) { + // v has already been converted + // to appropriate form for t. + if t == nil || t.Etype == TIDEAL { + return + } + + // Only uintptrs may be converted to unsafe.Pointer, which cannot overflow. + if t.Etype == TUNSAFEPTR { + return + } + + if !doesoverflow(v, t) { + return + } + + switch v.Ctype { + case CTINT, CTRUNE: + Yyerror("constant %v overflows %v", v.U.(*Mpint), t) + + case CTFLT: + Yyerror("constant %v overflows %v", Fconv(v.U.(*Mpflt), obj.FmtSharp), t) + + case CTCPLX: + 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.(*Mpint), Minintval[TINT]) < 0 || Mpcmpfixfix(v.U.(*Mpint), Maxintval[TINT]) > 0 { + Yyerror("overflow in int -> string") + } + r := uint(Mpgetfix(v.U.(*Mpint))) + v = Val{} + v.Ctype = CTSTR + v.U = string(r) + + case CTFLT: + Yyerror("no float -> string") + fallthrough + + case CTNIL: + v = Val{} + v.Ctype = CTSTR + v.U = "" + } + + return v +} + +func consttype(n *Node) int { + if n == nil || n.Op != OLITERAL { + return -1 + } + return int(n.Val.Ctype) +} + +func Isconst(n *Node, ct int) bool { + t := consttype(n) + + // If the caller is asking for CTINT, allow CTRUNE too. + // Makes life easier for back ends. + return t == ct || (ct == CTINT && t == CTRUNE) +} + +func saveorig(n *Node) *Node { + if n == n.Orig { + // duplicate node for n->orig. + n1 := Nod(OLITERAL, nil, nil) + + n.Orig = n1 + *n1 = *n + } + + return n.Orig +} + +/* + * if n is constant, rewrite as OLITERAL node. + */ +func evconst(n *Node) { + // pick off just the opcodes that can be + // constant evaluated. + switch n.Op { + default: + return + + case OADD, + OAND, + OANDAND, + OANDNOT, + OARRAYBYTESTR, + OCOM, + ODIV, + OEQ, + OGE, + OGT, + OLE, + OLSH, + OLT, + OMINUS, + OMOD, + OMUL, + ONE, + ONOT, + OOR, + OOROR, + OPLUS, + ORSH, + OSUB, + OXOR: + break + + case OCONV: + if n.Type == nil { + return + } + if !okforconst[n.Type.Etype] && n.Type.Etype != TNIL { + return + } + + // merge adjacent constants in the argument list. + case OADDSTR: + var nr *Node + var nl *Node + var l2 *NodeList + for l1 := n.List; l1 != nil; l1 = l1.Next { + if Isconst(l1.N, CTSTR) && l1.Next != nil && Isconst(l1.Next.N, CTSTR) { + // merge from l1 up to but not including l2 + var strs []string + l2 = l1 + for l2 != nil && Isconst(l2.N, CTSTR) { + nr = l2.N + strs = append(strs, nr.Val.U.(string)) + l2 = l2.Next + } + + nl = Nod(OXXX, nil, nil) + *nl = *l1.N + nl.Orig = nl + nl.Val.Ctype = CTSTR + nl.Val.U = strings.Join(strs, "") + l1.N = nl + l1.Next = l2 + } + } + + // fix list end pointer. + for l2 := n.List; l2 != nil; l2 = l2.Next { + n.List.End = l2 + } + + // collapse single-constant list to single constant. + if count(n.List) == 1 && Isconst(n.List.N, CTSTR) { + n.Op = OLITERAL + n.Val = n.List.N.Val + } + + return + } + + nl := n.Left + if nl == nil || nl.Type == nil { + return + } + if consttype(nl) < 0 { + return + } + wl := int(nl.Type.Etype) + if Isint[wl] || Isfloat[wl] || Iscomplex[wl] { + wl = TIDEAL + } + + nr := n.Right + var rv Val + var lno int + var wr int + var v Val + var norig *Node + if nr == nil { + // copy numeric value to avoid modifying + // nl, in case someone still refers to it (e.g. iota). + v = nl.Val + + if wl == TIDEAL { + v = copyval(v) + } + + switch uint32(n.Op)<<16 | uint32(v.Ctype) { + default: + if n.Diag == 0 { + Yyerror("illegal constant expression %v %v", Oconv(int(n.Op), 0), nl.Type) + n.Diag = 1 + } + + return + + case OCONV<<16 | CTNIL, + OARRAYBYTESTR<<16 | CTNIL: + if n.Type.Etype == TSTRING { + v = tostr(v) + nl.Type = n.Type + break + } + fallthrough + + // fall through + case OCONV<<16 | CTINT, + OCONV<<16 | CTRUNE, + OCONV<<16 | CTFLT, + OCONV<<16 | CTSTR: + convlit1(&nl, n.Type, true) + + v = nl.Val + + case OPLUS<<16 | CTINT, + OPLUS<<16 | CTRUNE: + break + + case OMINUS<<16 | CTINT, + OMINUS<<16 | CTRUNE: + mpnegfix(v.U.(*Mpint)) + + case OCOM<<16 | CTINT, + OCOM<<16 | CTRUNE: + et := Txxx + if nl.Type != nil { + et = int(nl.Type.Etype) + } + + // calculate the mask in b + // result will be (a ^ mask) + var b Mpint + switch et { + // signed guys change sign + default: + Mpmovecfix(&b, -1) + + // unsigned guys invert their bits + case TUINT8, + TUINT16, + TUINT32, + TUINT64, + TUINT, + TUINTPTR: + mpmovefixfix(&b, Maxintval[et]) + } + + mpxorfixfix(v.U.(*Mpint), &b) + + case OPLUS<<16 | CTFLT: + break + + case OMINUS<<16 | CTFLT: + mpnegflt(v.U.(*Mpflt)) + + case OPLUS<<16 | CTCPLX: + break + + case OMINUS<<16 | CTCPLX: + mpnegflt(&v.U.(*Mpcplx).Real) + mpnegflt(&v.U.(*Mpcplx).Imag) + + case ONOT<<16 | CTBOOL: + if !v.U.(bool) { + goto settrue + } + goto setfalse + } + goto ret + } + if nr.Type == nil { + return + } + if consttype(nr) < 0 { + return + } + wr = int(nr.Type.Etype) + if Isint[wr] || Isfloat[wr] || Iscomplex[wr] { + wr = TIDEAL + } + + // check for compatible general types (numeric, string, etc) + if wl != wr { + goto illegal + } + + // check for compatible types. + switch n.Op { + // ideal const mixes with anything but otherwise must match. + default: + if nl.Type.Etype != TIDEAL { + defaultlit(&nr, nl.Type) + n.Right = nr + } + + if nr.Type.Etype != TIDEAL { + defaultlit(&nl, nr.Type) + n.Left = nl + } + + if nl.Type.Etype != nr.Type.Etype { + goto illegal + } + + // right must be unsigned. + // left can be ideal. + case OLSH, ORSH: + defaultlit(&nr, Types[TUINT]) + + n.Right = nr + if nr.Type != nil && (Issigned[nr.Type.Etype] || !Isint[nr.Type.Etype]) { + goto illegal + } + if nl.Val.Ctype != CTRUNE { + nl.Val = toint(nl.Val) + } + nr.Val = toint(nr.Val) + } + + // copy numeric value to avoid modifying + // n->left, in case someone still refers to it (e.g. iota). + v = nl.Val + + if wl == TIDEAL { + v = copyval(v) + } + + rv = nr.Val + + // convert to common ideal + if v.Ctype == CTCPLX || rv.Ctype == CTCPLX { + v = tocplx(v) + rv = tocplx(rv) + } + + if v.Ctype == CTFLT || rv.Ctype == CTFLT { + v = toflt(v) + rv = toflt(rv) + } + + // Rune and int turns into rune. + if v.Ctype == CTRUNE && rv.Ctype == CTINT { + rv.Ctype = CTRUNE + } + if v.Ctype == CTINT && rv.Ctype == CTRUNE { + if n.Op == OLSH || n.Op == ORSH { + rv.Ctype = CTINT + } else { + v.Ctype = CTRUNE + } + } + + if v.Ctype != rv.Ctype { + // Use of undefined name as constant? + if (v.Ctype == 0 || rv.Ctype == 0) && nerrors > 0 { + return + } + Fatal("constant type mismatch %v(%d) %v(%d)", nl.Type, v.Ctype, nr.Type, rv.Ctype) + } + + // run op + switch uint32(n.Op)<<16 | uint32(v.Ctype) { + default: + goto illegal + + case OADD<<16 | CTINT, + OADD<<16 | CTRUNE: + mpaddfixfix(v.U.(*Mpint), rv.U.(*Mpint), 0) + + case OSUB<<16 | CTINT, + OSUB<<16 | CTRUNE: + mpsubfixfix(v.U.(*Mpint), rv.U.(*Mpint)) + + case OMUL<<16 | CTINT, + OMUL<<16 | CTRUNE: + mpmulfixfix(v.U.(*Mpint), rv.U.(*Mpint)) + + case ODIV<<16 | CTINT, + ODIV<<16 | CTRUNE: + if mpcmpfixc(rv.U.(*Mpint), 0) == 0 { + Yyerror("division by zero") + mpsetovf(v.U.(*Mpint)) + break + } + + mpdivfixfix(v.U.(*Mpint), rv.U.(*Mpint)) + + case OMOD<<16 | CTINT, + OMOD<<16 | CTRUNE: + if mpcmpfixc(rv.U.(*Mpint), 0) == 0 { + Yyerror("division by zero") + mpsetovf(v.U.(*Mpint)) + break + } + + mpmodfixfix(v.U.(*Mpint), rv.U.(*Mpint)) + + case OLSH<<16 | CTINT, + OLSH<<16 | CTRUNE: + mplshfixfix(v.U.(*Mpint), rv.U.(*Mpint)) + + case ORSH<<16 | CTINT, + ORSH<<16 | CTRUNE: + mprshfixfix(v.U.(*Mpint), rv.U.(*Mpint)) + + case OOR<<16 | CTINT, + OOR<<16 | CTRUNE: + mporfixfix(v.U.(*Mpint), rv.U.(*Mpint)) + + case OAND<<16 | CTINT, + OAND<<16 | CTRUNE: + mpandfixfix(v.U.(*Mpint), rv.U.(*Mpint)) + + case OANDNOT<<16 | CTINT, + OANDNOT<<16 | CTRUNE: + mpandnotfixfix(v.U.(*Mpint), rv.U.(*Mpint)) + + case OXOR<<16 | CTINT, + OXOR<<16 | CTRUNE: + mpxorfixfix(v.U.(*Mpint), rv.U.(*Mpint)) + + case OADD<<16 | CTFLT: + mpaddfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) + + case OSUB<<16 | CTFLT: + mpsubfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) + + case OMUL<<16 | CTFLT: + mpmulfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) + + case ODIV<<16 | CTFLT: + if mpcmpfltc(rv.U.(*Mpflt), 0) == 0 { + Yyerror("division by zero") + Mpmovecflt(v.U.(*Mpflt), 1.0) + break + } + + mpdivfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) + + // The default case above would print 'ideal % ideal', + // which is not quite an ideal error. + case OMOD<<16 | CTFLT: + if n.Diag == 0 { + Yyerror("illegal constant expression: floating-point %% operation") + n.Diag = 1 + } + + return + + case OADD<<16 | CTCPLX: + 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.(*Mpcplx).Real, &rv.U.(*Mpcplx).Real) + mpsubfltflt(&v.U.(*Mpcplx).Imag, &rv.U.(*Mpcplx).Imag) + + case OMUL<<16 | CTCPLX: + cmplxmpy(v.U.(*Mpcplx), rv.U.(*Mpcplx)) + + case ODIV<<16 | CTCPLX: + if mpcmpfltc(&rv.U.(*Mpcplx).Real, 0) == 0 && mpcmpfltc(&rv.U.(*Mpcplx).Imag, 0) == 0 { + Yyerror("complex division by zero") + Mpmovecflt(&rv.U.(*Mpcplx).Real, 1.0) + Mpmovecflt(&rv.U.(*Mpcplx).Imag, 0.0) + break + } + + cmplxdiv(v.U.(*Mpcplx), rv.U.(*Mpcplx)) + + case OEQ<<16 | CTNIL: + goto settrue + + case ONE<<16 | CTNIL: + goto setfalse + + case OEQ<<16 | CTINT, + OEQ<<16 | CTRUNE: + if Mpcmpfixfix(v.U.(*Mpint), rv.U.(*Mpint)) == 0 { + goto settrue + } + goto setfalse + + case ONE<<16 | CTINT, + ONE<<16 | CTRUNE: + if Mpcmpfixfix(v.U.(*Mpint), rv.U.(*Mpint)) != 0 { + goto settrue + } + goto setfalse + + case OLT<<16 | CTINT, + OLT<<16 | CTRUNE: + if Mpcmpfixfix(v.U.(*Mpint), rv.U.(*Mpint)) < 0 { + goto settrue + } + goto setfalse + + case OLE<<16 | CTINT, + OLE<<16 | CTRUNE: + if Mpcmpfixfix(v.U.(*Mpint), rv.U.(*Mpint)) <= 0 { + goto settrue + } + goto setfalse + + case OGE<<16 | CTINT, + OGE<<16 | CTRUNE: + if Mpcmpfixfix(v.U.(*Mpint), rv.U.(*Mpint)) >= 0 { + goto settrue + } + goto setfalse + + case OGT<<16 | CTINT, + OGT<<16 | CTRUNE: + if Mpcmpfixfix(v.U.(*Mpint), rv.U.(*Mpint)) > 0 { + goto settrue + } + goto setfalse + + case OEQ<<16 | CTFLT: + if mpcmpfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) == 0 { + goto settrue + } + goto setfalse + + case ONE<<16 | CTFLT: + if mpcmpfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) != 0 { + goto settrue + } + goto setfalse + + case OLT<<16 | CTFLT: + if mpcmpfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) < 0 { + goto settrue + } + goto setfalse + + case OLE<<16 | CTFLT: + if mpcmpfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) <= 0 { + goto settrue + } + goto setfalse + + case OGE<<16 | CTFLT: + if mpcmpfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) >= 0 { + goto settrue + } + goto setfalse + + case OGT<<16 | CTFLT: + if mpcmpfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) > 0 { + goto settrue + } + goto setfalse + + case OEQ<<16 | CTCPLX: + 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.(*Mpcplx).Real, &rv.U.(*Mpcplx).Real) != 0 || mpcmpfltflt(&v.U.(*Mpcplx).Imag, &rv.U.(*Mpcplx).Imag) != 0 { + goto settrue + } + goto setfalse + + case OEQ<<16 | CTSTR: + if cmpslit(nl, nr) == 0 { + goto settrue + } + goto setfalse + + case ONE<<16 | CTSTR: + if cmpslit(nl, nr) != 0 { + goto settrue + } + goto setfalse + + case OLT<<16 | CTSTR: + if cmpslit(nl, nr) < 0 { + goto settrue + } + goto setfalse + + case OLE<<16 | CTSTR: + if cmpslit(nl, nr) <= 0 { + goto settrue + } + goto setfalse + + case OGE<<16 | CTSTR: + if cmpslit(nl, nr) >= 0 { + goto settrue + } + goto setfalse + + case OGT<<16 | CTSTR: + if cmpslit(nl, nr) > 0 { + goto settrue + } + goto setfalse + + case OOROR<<16 | CTBOOL: + if v.U.(bool) || rv.U.(bool) { + goto settrue + } + goto setfalse + + case OANDAND<<16 | CTBOOL: + if v.U.(bool) && rv.U.(bool) { + goto settrue + } + goto setfalse + + case OEQ<<16 | CTBOOL: + if v.U.(bool) == rv.U.(bool) { + goto settrue + } + goto setfalse + + case ONE<<16 | CTBOOL: + if v.U.(bool) != rv.U.(bool) { + goto settrue + } + goto setfalse + } + + goto ret + +ret: + norig = saveorig(n) + *n = *nl + + // restore value of n->orig. + n.Orig = norig + + n.Val = v + + // check range. + lno = int(setlineno(n)) + + overflow(v, n.Type) + lineno = int32(lno) + + // truncate precision for non-ideal float. + if v.Ctype == CTFLT && n.Type.Etype != TIDEAL { + n.Val.U = truncfltlit(v.U.(*Mpflt), n.Type) + } + return + +settrue: + norig = saveorig(n) + *n = *Nodbool(true) + n.Orig = norig + return + +setfalse: + norig = saveorig(n) + *n = *Nodbool(false) + n.Orig = norig + return + +illegal: + if n.Diag == 0 { + Yyerror("illegal constant expression: %v %v %v", nl.Type, Oconv(int(n.Op), 0), nr.Type) + n.Diag = 1 + } + + return +} + +func nodlit(v Val) *Node { + n := Nod(OLITERAL, nil, nil) + n.Val = v + switch v.Ctype { + default: + Fatal("nodlit ctype %d", v.Ctype) + + case CTSTR: + n.Type = idealstring + + case CTBOOL: + n.Type = idealbool + + case CTINT, CTRUNE, CTFLT, CTCPLX: + n.Type = Types[TIDEAL] + + case CTNIL: + n.Type = Types[TNIL] + } + + return n +} + +func nodcplxlit(r Val, i Val) *Node { + r = toflt(r) + i = toflt(i) + + c := new(Mpcplx) + n := Nod(OLITERAL, nil, nil) + n.Type = Types[TIDEAL] + 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.(*Mpflt)) + mpmovefltflt(&c.Imag, i.U.(*Mpflt)) + return n +} + +// idealkind returns a constant kind like consttype +// but for an arbitrary "ideal" (untyped constant) expression. +func idealkind(n *Node) int { + if n == nil || !isideal(n.Type) { + return CTxxx + } + + switch n.Op { + default: + return CTxxx + + case OLITERAL: + return int(n.Val.Ctype) + + // numeric kinds. + case OADD, + OAND, + OANDNOT, + OCOM, + ODIV, + OMINUS, + OMOD, + OMUL, + OSUB, + OXOR, + OOR, + OPLUS: + k1 := idealkind(n.Left) + + k2 := idealkind(n.Right) + if k1 > k2 { + return k1 + } else { + return k2 + } + + case OREAL, OIMAG: + return CTFLT + + case OCOMPLEX: + return CTCPLX + + case OADDSTR: + return CTSTR + + case OANDAND, + OEQ, + OGE, + OGT, + OLE, + OLT, + ONE, + ONOT, + OOROR, + OCMPSTR, + OCMPIFACE: + return CTBOOL + + // shifts (beware!). + case OLSH, ORSH: + return idealkind(n.Left) + } +} + +func defaultlit(np **Node, t *Type) { + n := *np + if n == nil || !isideal(n.Type) { + return + } + + if n.Op == OLITERAL { + nn := Nod(OXXX, nil, nil) + *nn = *n + n = nn + *np = n + } + + lno := int(setlineno(n)) + ctype := idealkind(n) + var t1 *Type + switch ctype { + default: + if t != nil { + Convlit(np, t) + return + } + + if n.Val.Ctype == CTNIL { + lineno = int32(lno) + if n.Diag == 0 { + Yyerror("use of untyped nil") + n.Diag = 1 + } + + n.Type = nil + break + } + + if n.Val.Ctype == CTSTR { + t1 := Types[TSTRING] + Convlit(np, t1) + break + } + + Yyerror("defaultlit: unknown literal: %v", n) + + case CTxxx: + Fatal("defaultlit: idealkind is CTxxx: %v", Nconv(n, obj.FmtSign)) + + case CTBOOL: + t1 := Types[TBOOL] + if t != nil && t.Etype == TBOOL { + t1 = t + } + Convlit(np, t1) + + case CTINT: + t1 = Types[TINT] + goto num + + case CTRUNE: + t1 = runetype + goto num + + case CTFLT: + t1 = Types[TFLOAT64] + goto num + + case CTCPLX: + t1 = Types[TCOMPLEX128] + goto num + } + + lineno = int32(lno) + return + +num: + if t != nil { + if Isint[t.Etype] { + t1 = t + n.Val = toint(n.Val) + } else if Isfloat[t.Etype] { + t1 = t + n.Val = toflt(n.Val) + } else if Iscomplex[t.Etype] { + t1 = t + n.Val = tocplx(n.Val) + } + } + + overflow(n.Val, t1) + Convlit(np, t1) + lineno = int32(lno) + return +} + +/* + * defaultlit on both nodes simultaneously; + * if they're both ideal going in they better + * get the same type going out. + * force means must assign concrete (non-ideal) type. + */ +func defaultlit2(lp **Node, rp **Node, force int) { + l := *lp + r := *rp + if l.Type == nil || r.Type == nil { + return + } + if !isideal(l.Type) { + Convlit(rp, l.Type) + return + } + + if !isideal(r.Type) { + Convlit(lp, r.Type) + return + } + + if force == 0 { + return + } + if l.Type.Etype == TBOOL { + Convlit(lp, Types[TBOOL]) + Convlit(rp, Types[TBOOL]) + } + + lkind := idealkind(l) + rkind := idealkind(r) + if lkind == CTCPLX || rkind == CTCPLX { + Convlit(lp, Types[TCOMPLEX128]) + Convlit(rp, Types[TCOMPLEX128]) + return + } + + if lkind == CTFLT || rkind == CTFLT { + Convlit(lp, Types[TFLOAT64]) + Convlit(rp, Types[TFLOAT64]) + return + } + + if lkind == CTRUNE || rkind == CTRUNE { + Convlit(lp, runetype) + Convlit(rp, runetype) + return + } + + Convlit(lp, Types[TINT]) + Convlit(rp, Types[TINT]) +} + +func cmpslit(l, r *Node) int { + return stringsCompare(l.Val.U.(string), r.Val.U.(string)) +} + +func Smallintconst(n *Node) bool { + if n.Op == OLITERAL && Isconst(n, CTINT) && n.Type != nil { + switch Simtype[n.Type.Etype] { + case TINT8, + TUINT8, + TINT16, + TUINT16, + TINT32, + TUINT32, + TBOOL, + TPTR32: + return true + + case TIDEAL, TINT64, TUINT64, TPTR64: + if Mpcmpfixfix(n.Val.U.(*Mpint), Minintval[TINT32]) < 0 || Mpcmpfixfix(n.Val.U.(*Mpint), Maxintval[TINT32]) > 0 { + break + } + return true + } + } + + return false +} + +func nonnegconst(n *Node) int { + if n.Op == OLITERAL && n.Type != nil { + switch Simtype[n.Type.Etype] { + // check negative and 2^31 + case TINT8, + TUINT8, + TINT16, + TUINT16, + TINT32, + TUINT32, + TINT64, + TUINT64, + TIDEAL: + if Mpcmpfixfix(n.Val.U.(*Mpint), Minintval[TUINT32]) < 0 || Mpcmpfixfix(n.Val.U.(*Mpint), Maxintval[TINT32]) > 0 { + break + } + return int(Mpgetfix(n.Val.U.(*Mpint))) + } + } + + return -1 +} + +/* + * convert x to type et and back to int64 + * for sign extension and truncation. + */ +func iconv(x int64, et int) int64 { + switch et { + case TINT8: + x = int64(int8(x)) + + case TUINT8: + x = int64(uint8(x)) + + case TINT16: + x = int64(int16(x)) + + case TUINT16: + x = int64(uint64(x)) + + case TINT32: + x = int64(int32(x)) + + case TUINT32: + x = int64(uint32(x)) + + case TINT64, TUINT64: + break + } + + return x +} + +// 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 = n.Val + + if Isint[tt] { + con.Val.Ctype = CTINT + 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.(*Mpint)) + + case CTBOOL: + i = int64(obj.Bool2int(n.Val.U.(bool))) + + case CTNIL: + i = 0 + } + + i = iconv(i, tt) + Mpmovecfix(con.Val.U.(*Mpint), i) + return + } + + if Isfloat[tt] { + con.Val = toflt(con.Val) + if con.Val.Ctype != CTFLT { + Fatal("convconst ctype=%d %v", con.Val.Ctype, t) + } + if tt == TFLOAT32 { + con.Val.U = truncfltlit(con.Val.U.(*Mpflt), t) + } + return + } + + if Iscomplex[tt] { + con.Val = tocplx(con.Val) + if tt == TCOMPLEX64 { + 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 + } + + Fatal("convconst %v constant", Tconv(t, obj.FmtLong)) +} + +// complex multiply v *= rv +// (a, b) * (c, d) = (a*c - b*d, b*c + a*d) +func cmplxmpy(v *Mpcplx, rv *Mpcplx) { + var ac Mpflt + var bd Mpflt + var bc Mpflt + var ad Mpflt + + mpmovefltflt(&ac, &v.Real) + mpmulfltflt(&ac, &rv.Real) // ac + + mpmovefltflt(&bd, &v.Imag) + + mpmulfltflt(&bd, &rv.Imag) // bd + + mpmovefltflt(&bc, &v.Imag) + + mpmulfltflt(&bc, &rv.Real) // bc + + mpmovefltflt(&ad, &v.Real) + + mpmulfltflt(&ad, &rv.Imag) // ad + + mpmovefltflt(&v.Real, &ac) + + mpsubfltflt(&v.Real, &bd) // ac-bd + + mpmovefltflt(&v.Imag, &bc) + + mpaddfltflt(&v.Imag, &ad) // bc+ad +} + +// complex divide v /= rv +// (a, b) / (c, d) = ((a*c + b*d), (b*c - a*d))/(c*c + d*d) +func cmplxdiv(v *Mpcplx, rv *Mpcplx) { + var ac Mpflt + var bd Mpflt + var bc Mpflt + var ad Mpflt + var cc_plus_dd Mpflt + + mpmovefltflt(&cc_plus_dd, &rv.Real) + mpmulfltflt(&cc_plus_dd, &rv.Real) // cc + + mpmovefltflt(&ac, &rv.Imag) + + mpmulfltflt(&ac, &rv.Imag) // dd + + mpaddfltflt(&cc_plus_dd, &ac) // cc+dd + + mpmovefltflt(&ac, &v.Real) + + mpmulfltflt(&ac, &rv.Real) // ac + + mpmovefltflt(&bd, &v.Imag) + + mpmulfltflt(&bd, &rv.Imag) // bd + + mpmovefltflt(&bc, &v.Imag) + + mpmulfltflt(&bc, &rv.Real) // bc + + mpmovefltflt(&ad, &v.Real) + + mpmulfltflt(&ad, &rv.Imag) // ad + + mpmovefltflt(&v.Real, &ac) + + mpaddfltflt(&v.Real, &bd) // ac+bd + mpdivfltflt(&v.Real, &cc_plus_dd) // (ac+bd)/(cc+dd) + + mpmovefltflt(&v.Imag, &bc) + + mpsubfltflt(&v.Imag, &ad) // bc-ad + mpdivfltflt(&v.Imag, &cc_plus_dd) // (bc+ad)/(cc+dd) +} + +// Is n a Go language constant (as opposed to a compile-time constant)? +// Expressions derived from nil, like string([]byte(nil)), while they +// may be known at compile time, are not Go language constants. +// Only called for expressions known to evaluated to compile-time +// constants. +func isgoconst(n *Node) bool { + if n.Orig != nil { + n = n.Orig + } + + switch n.Op { + case OADD, + OADDSTR, + OAND, + OANDAND, + OANDNOT, + OCOM, + ODIV, + OEQ, + OGE, + OGT, + OLE, + OLSH, + OLT, + OMINUS, + OMOD, + OMUL, + ONE, + ONOT, + OOR, + OOROR, + OPLUS, + ORSH, + OSUB, + OXOR, + OIOTA, + OCOMPLEX, + OREAL, + OIMAG: + if isgoconst(n.Left) && (n.Right == nil || isgoconst(n.Right)) { + return true + } + + case OCONV: + if okforconst[n.Type.Etype] && isgoconst(n.Left) { + return true + } + + case OLEN, OCAP: + l := n.Left + if isgoconst(l) { + return true + } + + // Special case: len/cap is constant when applied to array or + // pointer to array when the expression does not contain + // function calls or channel receive operations. + t := l.Type + + if t != nil && Isptr[t.Etype] { + t = t.Type + } + if Isfixedarray(t) && !hascallchan(l) { + return true + } + + case OLITERAL: + if n.Val.Ctype != CTNIL { + return true + } + + case ONAME: + l := n.Sym.Def + if l != nil && l.Op == OLITERAL && n.Val.Ctype != CTNIL { + return true + } + + case ONONAME: + if n.Sym.Def != nil && n.Sym.Def.Op == OIOTA { + return true + } + + // Only constant calls are unsafe.Alignof, Offsetof, and Sizeof. + case OCALL: + l := n.Left + + for l.Op == OPAREN { + l = l.Left + } + if l.Op != ONAME || l.Sym.Pkg != unsafepkg { + break + } + if l.Sym.Name == "Alignof" || l.Sym.Name == "Offsetof" || l.Sym.Name == "Sizeof" { + return true + } + } + + //dump("nonconst", n); + return false +} + +func hascallchan(n *Node) bool { + if n == nil { + return false + } + switch n.Op { + case OAPPEND, + OCALL, + OCALLFUNC, + OCALLINTER, + OCALLMETH, + OCAP, + OCLOSE, + OCOMPLEX, + OCOPY, + ODELETE, + OIMAG, + OLEN, + OMAKE, + ONEW, + OPANIC, + OPRINT, + OPRINTN, + OREAL, + ORECOVER, + ORECV: + return true + } + + if hascallchan(n.Left) || hascallchan(n.Right) { + return true + } + + for l := n.List; l != nil; l = l.Next { + if hascallchan(l.N) { + return true + } + } + for l := n.Rlist; l != nil; l = l.Next { + if hascallchan(l.N) { + return true + } + } + + return false +} diff --git a/src/cmd/compile/internal/gc/cplx.go b/src/cmd/compile/internal/gc/cplx.go new file mode 100644 index 0000000000..56a4892636 --- /dev/null +++ b/src/cmd/compile/internal/gc/cplx.go @@ -0,0 +1,480 @@ +// Copyright 2009 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 gc + +import "cmd/internal/obj" + +func overlap_cplx(f *Node, t *Node) bool { + // check whether f and t could be overlapping stack references. + // not exact, because it's hard to check for the stack register + // in portable code. close enough: worst case we will allocate + // an extra temporary and the registerizer will clean it up. + return f.Op == OINDREG && t.Op == OINDREG && f.Xoffset+f.Type.Width >= t.Xoffset && t.Xoffset+t.Type.Width >= f.Xoffset +} + +func complexbool(op int, nl, nr, res *Node, wantTrue bool, likely int, to *obj.Prog) { + // make both sides addable in ullman order + if nr != nil { + if nl.Ullman > nr.Ullman && !nl.Addable { + nl = CgenTemp(nl) + } + + if !nr.Addable { + nr = CgenTemp(nr) + } + } + if !nl.Addable { + nl = CgenTemp(nl) + } + + // Break nl and nr into real and imaginary components. + var lreal, limag, rreal, rimag Node + subnode(&lreal, &limag, nl) + subnode(&rreal, &rimag, nr) + + // build tree + // if branching: + // real(l) == real(r) && imag(l) == imag(r) + // if generating a value, use a branch-free version: + // real(l) == real(r) & imag(l) == imag(r) + realeq := Node{ + Op: OEQ, + Left: &lreal, + Right: &rreal, + Type: Types[TBOOL], + } + imageq := Node{ + Op: OEQ, + Left: &limag, + Right: &rimag, + Type: Types[TBOOL], + } + and := Node{ + Op: OANDAND, + Left: &realeq, + Right: &imageq, + Type: Types[TBOOL], + } + + if res != nil { + // generating a value + and.Op = OAND + if op == ONE { + and.Op = OOR + realeq.Op = ONE + imageq.Op = ONE + } + Bvgen(&and, res, true) + return + } + + // generating a branch + if op == ONE { + wantTrue = !wantTrue + } + + Bgen(&and, wantTrue, likely, to) +} + +// break addable nc-complex into nr-real and ni-imaginary +func subnode(nr *Node, ni *Node, nc *Node) { + if !nc.Addable { + Fatal("subnode not addable") + } + + tc := Simsimtype(nc.Type) + tc = cplxsubtype(tc) + t := Types[tc] + + if nc.Op == OLITERAL { + nodfconst(nr, t, &nc.Val.U.(*Mpcplx).Real) + nodfconst(ni, t, &nc.Val.U.(*Mpcplx).Imag) + return + } + + *nr = *nc + nr.Type = t + + *ni = *nc + ni.Type = t + ni.Xoffset += t.Width +} + +// generate code res = -nl +func minus(nl *Node, res *Node) { + var ra Node + ra.Op = OMINUS + ra.Left = nl + ra.Type = nl.Type + Cgen(&ra, res) +} + +// build and execute tree +// real(res) = -real(nl) +// imag(res) = -imag(nl) +func complexminus(nl *Node, res *Node) { + var n1 Node + var n2 Node + var n5 Node + var n6 Node + + subnode(&n1, &n2, nl) + subnode(&n5, &n6, res) + + minus(&n1, &n5) + minus(&n2, &n6) +} + +// build and execute tree +// real(res) = real(nl) op real(nr) +// imag(res) = imag(nl) op imag(nr) +func complexadd(op int, nl *Node, nr *Node, res *Node) { + var n1 Node + var n2 Node + var n3 Node + var n4 Node + var n5 Node + var n6 Node + + subnode(&n1, &n2, nl) + subnode(&n3, &n4, nr) + subnode(&n5, &n6, res) + + var ra Node + ra.Op = uint8(op) + ra.Left = &n1 + ra.Right = &n3 + ra.Type = n1.Type + Cgen(&ra, &n5) + + ra = Node{} + ra.Op = uint8(op) + ra.Left = &n2 + ra.Right = &n4 + ra.Type = n2.Type + Cgen(&ra, &n6) +} + +// build and execute tree +// tmp = real(nl)*real(nr) - imag(nl)*imag(nr) +// imag(res) = real(nl)*imag(nr) + imag(nl)*real(nr) +// real(res) = tmp +func complexmul(nl *Node, nr *Node, res *Node) { + var n1 Node + var n2 Node + var n3 Node + var n4 Node + var n5 Node + var n6 Node + var tmp Node + + subnode(&n1, &n2, nl) + subnode(&n3, &n4, nr) + subnode(&n5, &n6, res) + Tempname(&tmp, n5.Type) + + // real part -> tmp + var rm1 Node + + rm1.Op = OMUL + rm1.Left = &n1 + rm1.Right = &n3 + rm1.Type = n1.Type + + var rm2 Node + rm2.Op = OMUL + rm2.Left = &n2 + rm2.Right = &n4 + rm2.Type = n2.Type + + var ra Node + ra.Op = OSUB + ra.Left = &rm1 + ra.Right = &rm2 + ra.Type = rm1.Type + Cgen(&ra, &tmp) + + // imag part + rm1 = Node{} + + rm1.Op = OMUL + rm1.Left = &n1 + rm1.Right = &n4 + rm1.Type = n1.Type + + rm2 = Node{} + rm2.Op = OMUL + rm2.Left = &n2 + rm2.Right = &n3 + rm2.Type = n2.Type + + ra = Node{} + ra.Op = OADD + ra.Left = &rm1 + ra.Right = &rm2 + ra.Type = rm1.Type + Cgen(&ra, &n6) + + // tmp ->real part + Cgen(&tmp, &n5) +} + +func nodfconst(n *Node, t *Type, fval *Mpflt) { + *n = Node{} + n.Op = OLITERAL + n.Addable = true + ullmancalc(n) + n.Val.U = fval + n.Val.Ctype = CTFLT + n.Type = t + + if !Isfloat[t.Etype] { + Fatal("nodfconst: bad type %v", t) + } +} + +func Complexop(n *Node, res *Node) bool { + if n != nil && n.Type != nil { + if Iscomplex[n.Type.Etype] { + goto maybe + } + } + + if res != nil && res.Type != nil { + if Iscomplex[res.Type.Etype] { + goto maybe + } + } + + if n.Op == OREAL || n.Op == OIMAG { + //dump("\ncomplex-yes", n); + return true + } + + //dump("\ncomplex-no", n); + return false + +maybe: + switch n.Op { + case OCONV, // implemented ops + OADD, + OSUB, + OMUL, + OMINUS, + OCOMPLEX, + OREAL, + OIMAG: + //dump("\ncomplex-yes", n); + return true + + case ODOT, + ODOTPTR, + OINDEX, + OIND, + ONAME: + //dump("\ncomplex-yes", n); + return true + } + + //dump("\ncomplex-no", n); + return false +} + +func Complexmove(f *Node, t *Node) { + if Debug['g'] != 0 { + Dump("\ncomplexmove-f", f) + Dump("complexmove-t", t) + } + + if !t.Addable { + Fatal("complexmove: to not addable") + } + + ft := Simsimtype(f.Type) + tt := Simsimtype(t.Type) + switch uint32(ft)<<16 | uint32(tt) { + default: + Fatal("complexmove: unknown conversion: %v -> %v\n", f.Type, t.Type) + + // complex to complex move/convert. + // make f addable. + // also use temporary if possible stack overlap. + case TCOMPLEX64<<16 | TCOMPLEX64, + TCOMPLEX64<<16 | TCOMPLEX128, + TCOMPLEX128<<16 | TCOMPLEX64, + TCOMPLEX128<<16 | TCOMPLEX128: + if !f.Addable || overlap_cplx(f, t) { + var tmp Node + Tempname(&tmp, f.Type) + Complexmove(f, &tmp) + f = &tmp + } + + var n1 Node + var n2 Node + subnode(&n1, &n2, f) + var n4 Node + var n3 Node + subnode(&n3, &n4, t) + + Cgen(&n1, &n3) + Cgen(&n2, &n4) + } +} + +func Complexgen(n *Node, res *Node) { + if Debug['g'] != 0 { + Dump("\ncomplexgen-n", n) + Dump("complexgen-res", res) + } + + for n.Op == OCONVNOP { + n = n.Left + } + + // pick off float/complex opcodes + switch n.Op { + case OCOMPLEX: + if res.Addable { + var n1 Node + var n2 Node + subnode(&n1, &n2, res) + var tmp Node + Tempname(&tmp, n1.Type) + Cgen(n.Left, &tmp) + Cgen(n.Right, &n2) + Cgen(&tmp, &n1) + return + } + + case OREAL, OIMAG: + nl := n.Left + if !nl.Addable { + var tmp Node + Tempname(&tmp, nl.Type) + Complexgen(nl, &tmp) + nl = &tmp + } + + var n1 Node + var n2 Node + subnode(&n1, &n2, nl) + if n.Op == OREAL { + Cgen(&n1, res) + return + } + + Cgen(&n2, res) + return + } + + // perform conversion from n to res + tl := Simsimtype(res.Type) + + tl = cplxsubtype(tl) + tr := Simsimtype(n.Type) + tr = cplxsubtype(tr) + if tl != tr { + if !n.Addable { + var n1 Node + Tempname(&n1, n.Type) + Complexmove(n, &n1) + n = &n1 + } + + Complexmove(n, res) + return + } + + if !res.Addable { + var n1 Node + Igen(res, &n1, nil) + Cgen(n, &n1) + Regfree(&n1) + return + } + + if n.Addable { + Complexmove(n, res) + return + } + + switch n.Op { + default: + Dump("complexgen: unknown op", n) + Fatal("complexgen: unknown op %v", Oconv(int(n.Op), 0)) + + case ODOT, + ODOTPTR, + OINDEX, + OIND, + ONAME, // PHEAP or PPARAMREF var + OCALLFUNC, + OCALLMETH, + OCALLINTER: + var n1 Node + Igen(n, &n1, res) + + Complexmove(&n1, res) + Regfree(&n1) + return + + case OCONV, + OADD, + OSUB, + OMUL, + OMINUS, + OCOMPLEX, + OREAL, + OIMAG: + break + } + + nl := n.Left + if nl == nil { + return + } + nr := n.Right + + // make both sides addable in ullman order + var tnl Node + if nr != nil { + if nl.Ullman > nr.Ullman && !nl.Addable { + Tempname(&tnl, nl.Type) + Cgen(nl, &tnl) + nl = &tnl + } + + if !nr.Addable { + var tnr Node + Tempname(&tnr, nr.Type) + Cgen(nr, &tnr) + nr = &tnr + } + } + + if !nl.Addable { + Tempname(&tnl, nl.Type) + Cgen(nl, &tnl) + nl = &tnl + } + + switch n.Op { + default: + Fatal("complexgen: unknown op %v", Oconv(int(n.Op), 0)) + + case OCONV: + Complexmove(nl, res) + + case OMINUS: + complexminus(nl, res) + + case OADD, OSUB: + complexadd(int(n.Op), nl, nr, res) + + case OMUL: + complexmul(nl, nr, res) + } +} diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go new file mode 100644 index 0000000000..85a33bec3f --- /dev/null +++ b/src/cmd/compile/internal/gc/dcl.go @@ -0,0 +1,1494 @@ +// Copyright 2009 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 gc + +import ( + "cmd/internal/obj" + "fmt" + "strings" +) + +func dflag() bool { + if Debug['d'] == 0 { + return false + } + if Debug['y'] != 0 { + return true + } + if incannedimport != 0 { + return false + } + return true +} + +/* + * declaration stack & operations + */ +func dcopy(a *Sym, b *Sym) { + a.Pkg = b.Pkg + a.Name = b.Name + a.Def = b.Def + a.Block = b.Block + a.Lastlineno = b.Lastlineno +} + +func push() *Sym { + d := new(Sym) + d.Lastlineno = lineno + d.Link = dclstack + dclstack = d + return d +} + +func pushdcl(s *Sym) *Sym { + d := push() + dcopy(d, s) + if dflag() { + fmt.Printf("\t%v push %v %p\n", Ctxt.Line(int(lineno)), s, s.Def) + } + return d +} + +func popdcl() { + var d *Sym + var s *Sym + var lno int + + // if(dflag()) + // print("revert\n"); + + for d = dclstack; d != nil; d = d.Link { + if d.Name == "" { + break + } + s = Pkglookup(d.Name, d.Pkg) + lno = int(s.Lastlineno) + dcopy(s, d) + d.Lastlineno = int32(lno) + if dflag() { + fmt.Printf("\t%v pop %v %p\n", Ctxt.Line(int(lineno)), s, s.Def) + } + } + + if d == nil { + Fatal("popdcl: no mark") + } + dclstack = d.Link + block = d.Block +} + +func poptodcl() { + // pop the old marker and push a new one + // (cannot reuse the existing one) + // because we use the markers to identify blocks + // for the goto restriction checks. + popdcl() + + markdcl() +} + +func markdcl() { + d := push() + d.Name = "" // used as a mark in fifo + d.Block = block + + blockgen++ + block = blockgen +} + +// if(dflag()) +// print("markdcl\n"); +func dumpdcl(st string) { + var s *Sym + + i := 0 + for d := dclstack; d != nil; d = d.Link { + i++ + fmt.Printf(" %.2d %p", i, d) + if d.Name == "" { + fmt.Printf("\n") + continue + } + + fmt.Printf(" '%s'", d.Name) + s = Pkglookup(d.Name, d.Pkg) + fmt.Printf(" %v\n", s) + } +} + +func testdclstack() { + for d := dclstack; d != nil; d = d.Link { + if d.Name == "" { + if nerrors != 0 { + errorexit() + } + Yyerror("mark left on the stack") + continue + } + } +} + +func redeclare(s *Sym, where string) { + if s.Lastlineno == 0 { + var tmp string + if s.Origpkg != nil { + tmp = s.Origpkg.Path + } else { + tmp = s.Pkg.Path + } + pkgstr := tmp + Yyerror("%v redeclared %s\n"+"\tprevious declaration during import %q", s, where, pkgstr) + } else { + line1 := parserline() + line2 := int(s.Lastlineno) + + // When an import and a declaration collide in separate files, + // present the import as the "redeclared", because the declaration + // is visible where the import is, but not vice versa. + // See issue 4510. + if s.Def == nil { + line2 = line1 + line1 = int(s.Lastlineno) + } + + yyerrorl(int(line1), "%v redeclared %s\n"+"\tprevious declaration at %v", s, where, Ctxt.Line(line2)) + } +} + +var vargen int + +/* + * declare individual names - var, typ, const + */ + +var declare_typegen int + +func declare(n *Node, ctxt uint8) { + if ctxt == PDISCARD { + return + } + + if isblank(n) { + return + } + + n.Lineno = int32(parserline()) + s := n.Sym + + // kludgy: typecheckok means we're past parsing. Eg genwrapper may declare out of package names later. + if importpkg == nil && typecheckok == 0 && s.Pkg != localpkg { + Yyerror("cannot declare name %v", s) + } + + if ctxt == PEXTERN && s.Name == "init" { + Yyerror("cannot declare init - must be func") + } + + gen := 0 + if ctxt == PEXTERN { + externdcl = list(externdcl, n) + if dflag() { + fmt.Printf("\t%v global decl %v %p\n", Ctxt.Line(int(lineno)), s, n) + } + } else { + if Curfn == nil && ctxt == PAUTO { + Fatal("automatic outside function") + } + if Curfn != nil { + Curfn.Func.Dcl = list(Curfn.Func.Dcl, n) + } + if n.Op == OTYPE { + declare_typegen++ + gen = declare_typegen + } else if n.Op == ONAME && ctxt == PAUTO && !strings.Contains(s.Name, "·") { + vargen++ + gen = vargen + } + pushdcl(s) + n.Curfn = Curfn + } + + if ctxt == PAUTO { + n.Xoffset = 0 + } + + if s.Block == block { + // functype will print errors about duplicate function arguments. + // Don't repeat the error here. + if ctxt != PPARAM && ctxt != PPARAMOUT { + redeclare(s, "in this block") + } + } + + s.Block = block + s.Lastlineno = int32(parserline()) + s.Def = n + n.Vargen = int32(gen) + n.Funcdepth = Funcdepth + n.Class = uint8(ctxt) + + autoexport(n, ctxt) +} + +func addvar(n *Node, t *Type, ctxt uint8) { + if n == nil || n.Sym == nil || (n.Op != ONAME && n.Op != ONONAME) || t == nil { + Fatal("addvar: n=%v t=%v nil", n, t) + } + + n.Op = ONAME + declare(n, ctxt) + n.Type = t +} + +/* + * declare variables from grammar + * new_name_list (type | [type] = expr_list) + */ +func variter(vl *NodeList, t *Node, el *NodeList) *NodeList { + var init *NodeList + doexpr := el != nil + + if count(el) == 1 && count(vl) > 1 { + e := el.N + as2 := Nod(OAS2, nil, nil) + as2.List = vl + as2.Rlist = list1(e) + var v *Node + for ; vl != nil; vl = vl.Next { + v = vl.N + v.Op = ONAME + declare(v, dclcontext) + v.Ntype = t + v.Defn = as2 + if Funcdepth > 0 { + init = list(init, Nod(ODCL, v, nil)) + } + } + + return list(init, as2) + } + + var v *Node + var e *Node + for ; vl != nil; vl = vl.Next { + if doexpr { + if el == nil { + Yyerror("missing expression in var declaration") + break + } + + e = el.N + el = el.Next + } else { + e = nil + } + + v = vl.N + v.Op = ONAME + declare(v, dclcontext) + v.Ntype = t + + if e != nil || Funcdepth > 0 || isblank(v) { + if Funcdepth > 0 { + init = list(init, Nod(ODCL, v, nil)) + } + e = Nod(OAS, v, e) + init = list(init, e) + if e.Right != nil { + v.Defn = e + } + } + } + + if el != nil { + Yyerror("extra expression in var declaration") + } + return init +} + +/* + * declare constants from grammar + * new_name_list [[type] = expr_list] + */ +func constiter(vl *NodeList, t *Node, cl *NodeList) *NodeList { + if cl == nil { + if t != nil { + Yyerror("const declaration cannot have type without expression") + } + cl = lastconst + t = lasttype + } else { + lastconst = cl + lasttype = t + } + + cl = listtreecopy(cl) + + var v *Node + var c *Node + var vv *NodeList + for ; vl != nil; vl = vl.Next { + if cl == nil { + Yyerror("missing value in const declaration") + break + } + + c = cl.N + cl = cl.Next + + v = vl.N + v.Op = OLITERAL + declare(v, dclcontext) + + v.Ntype = t + v.Defn = c + + vv = list(vv, Nod(ODCLCONST, v, nil)) + } + + if cl != nil { + Yyerror("extra expression in const declaration") + } + iota_ += 1 + return vv +} + +/* + * this generates a new name node, + * typically for labels or other one-off names. + */ +func newname(s *Sym) *Node { + if s == nil { + Fatal("newname nil") + } + + n := Nod(ONAME, nil, nil) + n.Sym = s + n.Type = nil + n.Addable = true + n.Ullman = 1 + n.Xoffset = 0 + return n +} + +// newfuncname generates a new name node for a function or method. +// TODO(rsc): Use an ODCLFUNC node instead. See comment in CL 7360. +func newfuncname(s *Sym) *Node { + n := newname(s) + n.Func = new(Func) + return n +} + +/* + * this generates a new name node for a name + * being declared. + */ +func dclname(s *Sym) *Node { + n := newname(s) + n.Op = ONONAME // caller will correct it + return n +} + +func typenod(t *Type) *Node { + // if we copied another type with *t = *u + // then t->nod might be out of date, so + // check t->nod->type too + if t.Nod == nil || t.Nod.Type != t { + t.Nod = Nod(OTYPE, nil, nil) + t.Nod.Type = t + t.Nod.Sym = t.Sym + } + + return t.Nod +} + +/* + * this will return an old name + * that has already been pushed on the + * declaration list. a diagnostic is + * generated if no name has been defined. + */ +func oldname(s *Sym) *Node { + n := s.Def + if n == nil { + // maybe a top-level name will come along + // to give this a definition later. + // walkdef will check s->def again once + // all the input source has been processed. + n = newname(s) + + n.Op = ONONAME + n.Iota = iota_ // save current iota value in const declarations + } + + if Curfn != nil && n.Funcdepth > 0 && n.Funcdepth != Funcdepth && n.Op == ONAME { + // inner func is referring to var in outer func. + // + // TODO(rsc): If there is an outer variable x and we + // 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 { + // create new closure var. + c := Nod(ONAME, nil, nil) + + c.Sym = s + c.Class = PPARAMREF + c.Isddd = n.Isddd + c.Defn = n + c.Addable = false + c.Ullman = 2 + c.Funcdepth = Funcdepth + c.Outer = n.Closure + n.Closure = c + c.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 +} + +/* + * := declarations + */ +func colasname(n *Node) bool { + switch n.Op { + case ONAME, + ONONAME, + OPACK, + OTYPE, + OLITERAL: + return n.Sym != nil + } + + return false +} + +func colasdefn(left *NodeList, defn *Node) { + for l := left; l != nil; l = l.Next { + if l.N.Sym != nil { + l.N.Sym.Flags |= SymUniq + } + } + + nnew := 0 + nerr := 0 + var n *Node + for l := left; l != nil; l = l.Next { + n = l.N + if isblank(n) { + continue + } + if !colasname(n) { + yyerrorl(int(defn.Lineno), "non-name %v on left side of :=", n) + nerr++ + continue + } + + if n.Sym.Flags&SymUniq == 0 { + yyerrorl(int(defn.Lineno), "%v repeated on left side of :=", n.Sym) + n.Diag++ + nerr++ + continue + } + + n.Sym.Flags &^= SymUniq + if n.Sym.Block == block { + continue + } + + nnew++ + n = newname(n.Sym) + declare(n, dclcontext) + n.Defn = defn + defn.Ninit = list(defn.Ninit, Nod(ODCL, n, nil)) + l.N = n + } + + if nnew == 0 && nerr == 0 { + yyerrorl(int(defn.Lineno), "no new variables on left side of :=") + } +} + +func colas(left *NodeList, right *NodeList, lno int32) *Node { + as := Nod(OAS2, nil, nil) + as.List = left + as.Rlist = right + as.Colas = true + as.Lineno = lno + colasdefn(left, as) + + // make the tree prettier; not necessary + if count(left) == 1 && count(right) == 1 { + as.Left = as.List.N + as.Right = as.Rlist.N + as.List = nil + as.Rlist = nil + as.Op = OAS + } + + return as +} + +/* + * declare the arguments in an + * interface field declaration. + */ +func ifacedcl(n *Node) { + if n.Op != ODCLFIELD || n.Right == nil { + Fatal("ifacedcl") + } + + if isblank(n.Left) { + Yyerror("methods must have a unique non-blank name") + } + + n.Func = new(Func) + dclcontext = PPARAM + markdcl() + Funcdepth++ + n.Outer = Curfn + Curfn = n + funcargs(n.Right) + + // funcbody is normally called after the parser has + // seen the body of a function but since an interface + // field declaration does not have a body, we must + // call it now to pop the current declaration context. + dclcontext = PAUTO + + funcbody(n) +} + +/* + * declare the function proper + * and declare the arguments. + * called in extern-declaration context + * returns in auto-declaration context. + */ +func funchdr(n *Node) { + // change the declaration context from extern to auto + if Funcdepth == 0 && dclcontext != PEXTERN { + Fatal("funchdr: dclcontext") + } + + dclcontext = PAUTO + markdcl() + Funcdepth++ + + n.Outer = Curfn + Curfn = n + + if n.Nname != nil { + funcargs(n.Nname.Ntype) + } else if n.Ntype != nil { + funcargs(n.Ntype) + } else { + funcargs2(n.Type) + } +} + +func funcargs(nt *Node) { + if nt.Op != OTFUNC { + Fatal("funcargs %v", Oconv(int(nt.Op), 0)) + } + + // re-start the variable generation number + // we want to use small numbers for the return variables, + // so let them have the chunk starting at 1. + vargen = count(nt.Rlist) + + // declare the receiver and in arguments. + // no n->defn because type checking of func header + // will not fill in the types until later + if nt.Left != nil { + n := nt.Left + if n.Op != ODCLFIELD { + Fatal("funcargs receiver %v", Oconv(int(n.Op), 0)) + } + if n.Left != nil { + n.Left.Op = ONAME + n.Left.Ntype = n.Right + declare(n.Left, PPARAM) + if dclcontext == PAUTO { + vargen++ + n.Left.Vargen = int32(vargen) + } + } + } + + var n *Node + for l := nt.List; l != nil; l = l.Next { + n = l.N + if n.Op != ODCLFIELD { + Fatal("funcargs in %v", Oconv(int(n.Op), 0)) + } + if n.Left != nil { + n.Left.Op = ONAME + n.Left.Ntype = n.Right + declare(n.Left, PPARAM) + if dclcontext == PAUTO { + vargen++ + n.Left.Vargen = int32(vargen) + } + } + } + + // declare the out arguments. + gen := count(nt.List) + var i int = 0 + var nn *Node + for l := nt.Rlist; l != nil; l = l.Next { + n = l.N + + if n.Op != ODCLFIELD { + Fatal("funcargs out %v", Oconv(int(n.Op), 0)) + } + + if n.Left == nil { + // Name so that escape analysis can track it. ~r stands for 'result'. + n.Left = newname(Lookupf("~r%d", gen)) + gen++ + } + + // TODO: n->left->missing = 1; + n.Left.Op = ONAME + + if isblank(n.Left) { + // Give it a name so we can assign to it during return. ~b stands for 'blank'. + // The name must be different from ~r above because if you have + // func f() (_ int) + // func g() int + // f is allowed to use a plain 'return' with no arguments, while g is not. + // So the two cases must be distinguished. + // We do not record a pointer to the original node (n->orig). + // Having multiple names causes too much confusion in later passes. + nn = Nod(OXXX, nil, nil) + + *nn = *n.Left + nn.Orig = nn + nn.Sym = Lookupf("~b%d", gen) + gen++ + n.Left = nn + } + + n.Left.Ntype = n.Right + declare(n.Left, PPARAMOUT) + if dclcontext == PAUTO { + i++ + n.Left.Vargen = int32(i) + } + } +} + +/* + * Same as funcargs, except run over an already constructed TFUNC. + * This happens during import, where the hidden_fndcl rule has + * used functype directly to parse the function's type. + */ +func funcargs2(t *Type) { + if t.Etype != TFUNC { + Fatal("funcargs2 %v", t) + } + + if t.Thistuple != 0 { + var n *Node + for ft := getthisx(t).Type; ft != nil; ft = ft.Down { + if ft.Nname == nil || ft.Nname.Sym == nil { + continue + } + n = ft.Nname // no need for newname(ft->nname->sym) + n.Type = ft.Type + declare(n, PPARAM) + } + } + + if t.Intuple != 0 { + var n *Node + for ft := getinargx(t).Type; ft != nil; ft = ft.Down { + if ft.Nname == nil || ft.Nname.Sym == nil { + continue + } + n = ft.Nname + n.Type = ft.Type + declare(n, PPARAM) + } + } + + if t.Outtuple != 0 { + var n *Node + for ft := getoutargx(t).Type; ft != nil; ft = ft.Down { + if ft.Nname == nil || ft.Nname.Sym == nil { + continue + } + n = ft.Nname + n.Type = ft.Type + declare(n, PPARAMOUT) + } + } +} + +/* + * finish the body. + * called in auto-declaration context. + * returns in extern-declaration context. + */ +func funcbody(n *Node) { + // change the declaration context from auto to extern + if dclcontext != PAUTO { + Fatal("funcbody: dclcontext") + } + popdcl() + Funcdepth-- + Curfn = n.Outer + n.Outer = nil + if Funcdepth == 0 { + dclcontext = PEXTERN + } +} + +/* + * new type being defined with name s. + */ +func typedcl0(s *Sym) *Node { + n := newname(s) + n.Op = OTYPE + declare(n, dclcontext) + return n +} + +/* + * node n, which was returned by typedcl0 + * is being declared to have uncompiled type t. + * return the ODCLTYPE node to use. + */ +func typedcl1(n *Node, t *Node, local bool) *Node { + n.Ntype = t + n.Local = local + return Nod(ODCLTYPE, n, nil) +} + +/* + * structs, functions, and methods. + * they don't belong here, but where do they belong? + */ +func checkembeddedtype(t *Type) { + if t == nil { + return + } + + if t.Sym == nil && Isptr[t.Etype] { + t = t.Type + if t.Etype == TINTER { + Yyerror("embedded type cannot be a pointer to interface") + } + } + + if Isptr[t.Etype] { + Yyerror("embedded type cannot be a pointer") + } else if t.Etype == TFORW && t.Embedlineno == 0 { + t.Embedlineno = lineno + } +} + +func structfield(n *Node) *Type { + lno := int(lineno) + lineno = n.Lineno + + if n.Op != ODCLFIELD { + Fatal("structfield: oops %v\n", n) + } + + f := typ(TFIELD) + f.Isddd = n.Isddd + + if n.Right != nil { + typecheck(&n.Right, Etype) + n.Type = n.Right.Type + if n.Left != nil { + n.Left.Type = n.Type + } + if n.Embedded != 0 { + checkembeddedtype(n.Type) + } + } + + n.Right = nil + + f.Type = n.Type + if f.Type == nil { + f.Broke = 1 + } + + switch n.Val.Ctype { + case CTSTR: + f.Note = new(string) + *f.Note = n.Val.U.(string) + + default: + Yyerror("field annotation must be string") + fallthrough + + case CTxxx: + f.Note = nil + } + + if n.Left != nil && n.Left.Op == ONAME { + f.Nname = n.Left + f.Embedded = n.Embedded + f.Sym = f.Nname.Sym + } + + lineno = int32(lno) + return f +} + +var uniqgen uint32 + +func checkdupfields(t *Type, what string) { + lno := int(lineno) + + for ; t != nil; t = t.Down { + if t.Sym != nil && t.Nname != nil && !isblank(t.Nname) { + if t.Sym.Uniqgen == uniqgen { + lineno = t.Nname.Lineno + Yyerror("duplicate %s %s", what, t.Sym.Name) + } else { + t.Sym.Uniqgen = uniqgen + } + } + } + + lineno = int32(lno) +} + +/* + * convert a parsed id/type list into + * a type for struct/interface/arglist + */ +func tostruct(l *NodeList) *Type { + var f *Type + t := typ(TSTRUCT) + + for tp := &t.Type; l != nil; l = l.Next { + f = structfield(l.N) + + *tp = f + tp = &f.Down + } + + for f := t.Type; f != nil && t.Broke == 0; f = f.Down { + if f.Broke != 0 { + t.Broke = 1 + } + } + + uniqgen++ + checkdupfields(t.Type, "field") + + if t.Broke == 0 { + checkwidth(t) + } + + return t +} + +func tofunargs(l *NodeList) *Type { + var f *Type + + t := typ(TSTRUCT) + t.Funarg = 1 + + for tp := &t.Type; l != nil; l = l.Next { + f = structfield(l.N) + f.Funarg = 1 + + // esc.c needs to find f given a PPARAM to add the tag. + if l.N.Left != nil && l.N.Left.Class == PPARAM { + l.N.Left.Paramfld = f + } + + *tp = f + tp = &f.Down + } + + for f := t.Type; f != nil && t.Broke == 0; f = f.Down { + if f.Broke != 0 { + t.Broke = 1 + } + } + + return t +} + +func interfacefield(n *Node) *Type { + lno := int(lineno) + lineno = n.Lineno + + if n.Op != ODCLFIELD { + Fatal("interfacefield: oops %v\n", n) + } + + if n.Val.Ctype != CTxxx { + Yyerror("interface method cannot have annotation") + } + + f := typ(TFIELD) + f.Isddd = n.Isddd + + if n.Right != nil { + if n.Left != nil { + // queue resolution of method type for later. + // right now all we need is the name list. + // avoids cycles for recursive interface types. + n.Type = typ(TINTERMETH) + + n.Type.Nname = n.Right + n.Left.Type = n.Type + queuemethod(n) + + if n.Left.Op == ONAME { + f.Nname = n.Left + f.Embedded = n.Embedded + f.Sym = f.Nname.Sym + } + } else { + typecheck(&n.Right, Etype) + n.Type = n.Right.Type + + if n.Embedded != 0 { + checkembeddedtype(n.Type) + } + + if n.Type != nil { + switch n.Type.Etype { + case TINTER: + break + + case TFORW: + Yyerror("interface type loop involving %v", n.Type) + f.Broke = 1 + + default: + Yyerror("interface contains embedded non-interface %v", n.Type) + f.Broke = 1 + } + } + } + } + + n.Right = nil + + f.Type = n.Type + if f.Type == nil { + f.Broke = 1 + } + + lineno = int32(lno) + return f +} + +func tointerface(l *NodeList) *Type { + var f *Type + var t1 *Type + + t := typ(TINTER) + + tp := &t.Type + for ; l != nil; l = l.Next { + f = interfacefield(l.N) + + if l.N.Left == nil && f.Type.Etype == TINTER { + // embedded interface, inline methods + for t1 = f.Type.Type; t1 != nil; t1 = t1.Down { + f = typ(TFIELD) + f.Type = t1.Type + f.Broke = t1.Broke + f.Sym = t1.Sym + if f.Sym != nil { + f.Nname = newname(f.Sym) + } + *tp = f + tp = &f.Down + } + } else { + *tp = f + tp = &f.Down + } + } + + for f := t.Type; f != nil && t.Broke == 0; f = f.Down { + if f.Broke != 0 { + t.Broke = 1 + } + } + + uniqgen++ + checkdupfields(t.Type, "method") + t = sortinter(t) + checkwidth(t) + + return t +} + +func embedded(s *Sym, pkg *Pkg) *Node { + const ( + CenterDot = 0xB7 + ) + // Names sometimes have disambiguation junk + // appended after a center dot. Discard it when + // making the name for the embedded struct field. + name := s.Name + + if i := strings.Index(s.Name, string(CenterDot)); i >= 0 { + name = s.Name[:i] + } + + var n *Node + if exportname(name) { + n = newname(Lookup(name)) + } else if s.Pkg == builtinpkg { + // The name of embedded builtins belongs to pkg. + n = newname(Pkglookup(name, pkg)) + } else { + n = newname(Pkglookup(name, s.Pkg)) + } + n = Nod(ODCLFIELD, n, oldname(s)) + n.Embedded = 1 + return n +} + +/* + * check that the list of declarations is either all anonymous or all named + */ +func findtype(l *NodeList) *Node { + for ; l != nil; l = l.Next { + if l.N.Op == OKEY { + return l.N.Right + } + } + return nil +} + +func checkarglist(all *NodeList, input int) *NodeList { + named := 0 + for l := all; l != nil; l = l.Next { + if l.N.Op == OKEY { + named = 1 + break + } + } + + if named != 0 { + var n *Node + var l *NodeList + for l = all; l != nil; l = l.Next { + n = l.N + if n.Op != OKEY && n.Sym == nil { + Yyerror("mixed named and unnamed function parameters") + break + } + } + + if l == nil && n != nil && n.Op != OKEY { + Yyerror("final function parameter must have type") + } + } + + var nextt *Node + var t *Node + var n *Node + for l := all; l != nil; l = l.Next { + // can cache result from findtype to avoid + // quadratic behavior here, but unlikely to matter. + n = l.N + + if named != 0 { + if n.Op == OKEY { + t = n.Right + n = n.Left + nextt = nil + } else { + if nextt == nil { + nextt = findtype(l) + } + t = nextt + } + } else { + t = n + n = nil + } + + // during import l->n->op is OKEY, but l->n->left->sym == S + // means it was a '?', not that it was + // a lone type This doesn't matter for the exported + // declarations, which are parsed by rules that don't + // use checkargs, but can happen for func literals in + // the inline bodies. + // TODO(rsc) this can go when typefmt case TFIELD in exportmode fmt.c prints _ instead of ? + if importpkg != nil && n.Sym == nil { + n = nil + } + + if n != nil && n.Sym == nil { + t = n + n = nil + } + + if n != nil { + n = newname(n.Sym) + } + n = Nod(ODCLFIELD, n, t) + if n.Right != nil && n.Right.Op == ODDD { + if input == 0 { + Yyerror("cannot use ... in output argument list") + } else if l.Next != nil { + Yyerror("can only use ... as final argument in list") + } + n.Right.Op = OTARRAY + n.Right.Right = n.Right.Left + n.Right.Left = nil + n.Isddd = true + if n.Left != nil { + n.Left.Isddd = true + } + } + + l.N = n + } + + return all +} + +func fakethis() *Node { + n := Nod(ODCLFIELD, nil, typenod(Ptrto(typ(TSTRUCT)))) + return n +} + +/* + * Is this field a method on an interface? + * Those methods have an anonymous + * *struct{} as the receiver. + * (See fakethis above.) + */ +func isifacemethod(f *Type) bool { + rcvr := getthisx(f).Type + if rcvr.Sym != nil { + return false + } + t := rcvr.Type + if !Isptr[t.Etype] { + return false + } + t = t.Type + if t.Sym != nil || t.Etype != TSTRUCT || t.Type != nil { + return false + } + return true +} + +/* + * turn a parsed function declaration + * into a type + */ +func functype(this *Node, in *NodeList, out *NodeList) *Type { + t := typ(TFUNC) + + var rcvr *NodeList + if this != nil { + rcvr = list1(this) + } + t.Type = tofunargs(rcvr) + t.Type.Down = tofunargs(out) + t.Type.Down.Down = tofunargs(in) + + uniqgen++ + checkdupfields(t.Type.Type, "argument") + checkdupfields(t.Type.Down.Type, "argument") + checkdupfields(t.Type.Down.Down.Type, "argument") + + if t.Type.Broke != 0 || t.Type.Down.Broke != 0 || t.Type.Down.Down.Broke != 0 { + t.Broke = 1 + } + + if this != nil { + t.Thistuple = 1 + } + t.Outtuple = count(out) + t.Intuple = count(in) + t.Outnamed = 0 + if t.Outtuple > 0 && out.N.Left != nil && out.N.Left.Orig != nil { + s := out.N.Left.Orig.Sym + if s != nil && (s.Name[0] != '~' || s.Name[1] != 'r') { // ~r%d is the name invented for an unnamed result + t.Outnamed = 1 + } + } + + return t +} + +var methodsym_toppkg *Pkg + +func methodsym(nsym *Sym, t0 *Type, iface int) *Sym { + var s *Sym + var p string + var suffix string + var spkg *Pkg + + t := t0 + if t == nil { + goto bad + } + s = t.Sym + if s == nil && Isptr[t.Etype] { + t = t.Type + if t == nil { + goto bad + } + s = t.Sym + } + + spkg = nil + if s != nil { + spkg = s.Pkg + } + + // if t0 == *t and t0 has a sym, + // we want to see *t, not t0, in the method name. + if t != t0 && t0.Sym != nil { + t0 = Ptrto(t) + } + + suffix = "" + if iface != 0 { + dowidth(t0) + if t0.Width < Types[Tptr].Width { + suffix = "·i" + } + } + + if (spkg == nil || nsym.Pkg != spkg) && !exportname(nsym.Name) { + if t0.Sym == nil && Isptr[t0.Etype] { + p = fmt.Sprintf("(%v).%s.%s%s", Tconv(t0, obj.FmtLeft|obj.FmtShort), nsym.Pkg.Prefix, nsym.Name, suffix) + } else { + p = fmt.Sprintf("%v.%s.%s%s", Tconv(t0, obj.FmtLeft|obj.FmtShort), nsym.Pkg.Prefix, nsym.Name, suffix) + } + } else { + if t0.Sym == nil && Isptr[t0.Etype] { + p = fmt.Sprintf("(%v).%s%s", Tconv(t0, obj.FmtLeft|obj.FmtShort), nsym.Name, suffix) + } else { + p = fmt.Sprintf("%v.%s%s", Tconv(t0, obj.FmtLeft|obj.FmtShort), nsym.Name, suffix) + } + } + + if spkg == nil { + if methodsym_toppkg == nil { + methodsym_toppkg = mkpkg("go") + } + spkg = methodsym_toppkg + } + + s = Pkglookup(p, spkg) + + return s + +bad: + Yyerror("illegal receiver type: %v", t0) + return nil +} + +func methodname(n *Node, t *Type) *Node { + s := methodsym(n.Sym, t, 0) + if s == nil { + return n + } + return newname(s) +} + +func methodname1(n *Node, t *Node) *Node { + star := "" + if t.Op == OIND { + star = "*" + t = t.Left + } + + if t.Sym == nil || isblank(n) { + return newfuncname(n.Sym) + } + + var p string + if star != "" { + p = fmt.Sprintf("(%s%v).%v", star, t.Sym, n.Sym) + } else { + p = fmt.Sprintf("%v.%v", t.Sym, n.Sym) + } + + if exportname(t.Sym.Name) { + n = newfuncname(Lookup(p)) + } else { + n = newfuncname(Pkglookup(p, t.Sym.Pkg)) + } + + return n +} + +/* + * add a method, declared as a function, + * n is fieldname, pa is base type, t is function type + */ +func addmethod(sf *Sym, t *Type, local bool, nointerface bool) { + // get field sym + if sf == nil { + Fatal("no method symbol") + } + + // get parent type sym + pa := getthisx(t).Type // ptr to this structure + if pa == nil { + Yyerror("missing receiver") + return + } + + pa = pa.Type + f := methtype(pa, 1) + if f == nil { + t = pa + if t == nil { // rely on typecheck having complained before + return + } + if t != nil { + if Isptr[t.Etype] { + if t.Sym != nil { + Yyerror("invalid receiver type %v (%v is a pointer type)", pa, t) + return + } + + t = t.Type + } + + if t.Broke != 0 { // rely on typecheck having complained before + return + } + if t.Sym == nil { + Yyerror("invalid receiver type %v (%v is an unnamed type)", pa, t) + return + } + + if Isptr[t.Etype] { + Yyerror("invalid receiver type %v (%v is a pointer type)", pa, t) + return + } + + if t.Etype == TINTER { + Yyerror("invalid receiver type %v (%v is an interface type)", pa, t) + return + } + } + + // Should have picked off all the reasons above, + // but just in case, fall back to generic error. + Yyerror("invalid receiver type %v (%v / %v)", pa, Tconv(pa, obj.FmtLong), Tconv(t, obj.FmtLong)) + + return + } + + pa = f + if pa.Etype == TSTRUCT { + for f := pa.Type; f != nil; f = f.Down { + if f.Sym == sf { + Yyerror("type %v has both field and method named %v", pa, sf) + return + } + } + } + + if local && !pa.Local { + // defining method on non-local type. + Yyerror("cannot define new methods on non-local type %v", pa) + + return + } + + n := Nod(ODCLFIELD, newname(sf), nil) + n.Type = t + + var d *Type // last found + for f := pa.Method; f != nil; f = f.Down { + d = f + if f.Etype != TFIELD { + Fatal("addmethod: not TFIELD: %v", Tconv(f, obj.FmtLong)) + } + if sf.Name != f.Sym.Name { + continue + } + if !Eqtype(t, f.Type) { + Yyerror("method redeclared: %v.%v\n\t%v\n\t%v", pa, sf, f.Type, t) + } + return + } + + f = structfield(n) + f.Nointerface = nointerface + + // during import unexported method names should be in the type's package + if importpkg != nil && f.Sym != nil && !exportname(f.Sym.Name) && f.Sym.Pkg != structpkg { + Fatal("imported method name %v in wrong package %s\n", Sconv(f.Sym, obj.FmtSign), structpkg.Name) + } + + if d == nil { + pa.Method = f + } else { + d.Down = f + } + return +} + +func funccompile(n *Node) { + Stksize = BADWIDTH + Maxarg = 0 + + if n.Type == nil { + if nerrors == 0 { + Fatal("funccompile missing type") + } + return + } + + // assign parameter offsets + checkwidth(n.Type) + + if Curfn != nil { + Fatal("funccompile %v inside %v", n.Nname.Sym, Curfn.Nname.Sym) + } + + Stksize = 0 + dclcontext = PAUTO + Funcdepth = n.Funcdepth + 1 + compile(n) + Curfn = nil + Funcdepth = 0 + dclcontext = PEXTERN +} + +func funcsym(s *Sym) *Sym { + if s.Fsym != nil { + return s.Fsym + } + + s1 := Pkglookup(s.Name+"·f", s.Pkg) + if s1.Def == nil { + s1.Def = newfuncname(s1) + s1.Def.Func.Shortname = newname(s) + funcsyms = list(funcsyms, s1.Def) + } + s.Fsym = s1 + + return s1 +} diff --git a/src/cmd/compile/internal/gc/esc.go b/src/cmd/compile/internal/gc/esc.go new file mode 100644 index 0000000000..a9a1748b9a --- /dev/null +++ b/src/cmd/compile/internal/gc/esc.go @@ -0,0 +1,1758 @@ +// Copyright 2011 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 gc + +import ( + "cmd/internal/obj" + "fmt" + "strings" +) + +// Run analysis on minimal sets of mutually recursive functions +// or single non-recursive functions, bottom up. +// +// Finding these sets is finding strongly connected components +// in the static call graph. The algorithm for doing that is taken +// from Sedgewick, Algorithms, Second Edition, p. 482, with two +// adaptations. +// +// First, a hidden closure function (n->curfn != N) cannot be the +// root of a connected component. Refusing to use it as a root +// forces it into the component of the function in which it appears. +// This is more convenient for escape analysis. +// +// Second, each function becomes two virtual nodes in the graph, +// with numbers n and n+1. We record the function's node number as n +// but search from node n+1. If the search tells us that the component +// number (min) is n+1, we know that this is a trivial component: one function +// plus its closures. If the search tells us that the component number is +// n, then there was a path from node n+1 back to node n, meaning that +// the function set is mutually recursive. The escape analysis can be +// more precise when analyzing a single non-recursive function than +// when analyzing a set of mutually recursive functions. + +// TODO(rsc): Look into using a map[*Node]bool instead of walkgen, +// to allow analysis passes to use walkgen themselves. + +type bottomUpVisitor struct { + analyze func(*NodeList, bool) + visitgen uint32 + stack *NodeList +} + +// visitBottomUp invokes analyze on the ODCLFUNC nodes listed in list. +// It calls analyze with successive groups of functions, working from +// the bottom of the call graph upward. Each time analyze is called with +// a list of functions, every function on that list only calls other functions +// on the list or functions that have been passed in previous invocations of +// analyze. Closures appear in the same list as their outer functions. +// The lists are as short as possible while preserving those requirements. +// (In a typical program, many invocations of analyze will be passed just +// a single function.) The boolean argument 'recursive' passed to analyze +// specifies whether the functions on the list are mutually recursive. +// If recursive is false, the list consists of only a single function and its closures. +// If recursive is true, the list may still contain only a single function, +// if that function is itself recursive. +func visitBottomUp(list *NodeList, analyze func(list *NodeList, recursive bool)) { + for l := list; l != nil; l = l.Next { + l.N.Walkgen = 0 + } + + var v bottomUpVisitor + v.analyze = analyze + for l := list; l != nil; l = l.Next { + if l.N.Op == ODCLFUNC && l.N.Curfn == nil { + v.visit(l.N) + } + } + + for l := list; l != nil; l = l.Next { + l.N.Walkgen = 0 + } +} + +func (v *bottomUpVisitor) visit(n *Node) uint32 { + if n.Walkgen > 0 { + // already visited + return n.Walkgen + } + + v.visitgen++ + n.Walkgen = v.visitgen + v.visitgen++ + min := v.visitgen + + l := new(NodeList) + l.Next = v.stack + l.N = n + v.stack = l + min = v.visitcodelist(n.Nbody, min) + if (min == n.Walkgen || min == n.Walkgen+1) && n.Curfn == nil { + // This node is the root of a strongly connected component. + + // The original min passed to visitcodelist was n->walkgen+1. + // If visitcodelist found its way back to n->walkgen, then this + // block is a set of mutually recursive functions. + // Otherwise it's just a lone function that does not recurse. + recursive := min == n.Walkgen + + // Remove connected component from stack. + // Mark walkgen so that future visits return a large number + // so as not to affect the caller's min. + block := v.stack + + var l *NodeList + for l = v.stack; l.N != n; l = l.Next { + l.N.Walkgen = ^uint32(0) + } + n.Walkgen = ^uint32(0) + v.stack = l.Next + l.Next = nil + + // Run escape analysis on this set of functions. + v.analyze(block, recursive) + } + + return min +} + +func (v *bottomUpVisitor) visitcodelist(l *NodeList, min uint32) uint32 { + for ; l != nil; l = l.Next { + min = v.visitcode(l.N, min) + } + return min +} + +func (v *bottomUpVisitor) visitcode(n *Node, min uint32) uint32 { + if n == nil { + return min + } + + min = v.visitcodelist(n.Ninit, min) + min = v.visitcode(n.Left, min) + min = v.visitcode(n.Right, min) + min = v.visitcodelist(n.List, min) + min = v.visitcode(n.Ntest, min) + min = v.visitcode(n.Nincr, min) + min = v.visitcodelist(n.Nbody, min) + min = v.visitcodelist(n.Nelse, min) + min = v.visitcodelist(n.Rlist, min) + + if n.Op == OCALLFUNC || n.Op == OCALLMETH { + fn := n.Left + if n.Op == OCALLMETH { + fn = n.Left.Right.Sym.Def + } + if fn != nil && fn.Op == ONAME && fn.Class == PFUNC && fn.Defn != nil { + m := v.visit(fn.Defn) + if m < min { + min = m + } + } + } + + if n.Op == OCLOSURE { + m := v.visit(n.Closure) + if m < min { + min = m + } + } + + return min +} + +// Escape analysis. + +// An escape analysis pass for a set of functions. +// The analysis assumes that closures and the functions in which they +// appear are analyzed together, so that the aliasing between their +// variables can be modeled more precisely. +// +// First escfunc, esc and escassign recurse over the ast of each +// function to dig out flow(dst,src) edges between any +// pointer-containing nodes and store them in dst->escflowsrc. For +// variables assigned to a variable in an outer scope or used as a +// return value, they store a flow(theSink, src) edge to a fake node +// 'the Sink'. For variables referenced in closures, an edge +// flow(closure, &var) is recorded and the flow of a closure itself to +// an outer scope is tracked the same way as other variables. +// +// Then escflood walks the graph starting at theSink and tags all +// variables of it can reach an & node as escaping and all function +// parameters it can reach as leaking. +// +// If a value's address is taken but the address does not escape, +// then the value can stay on the stack. If the value new(T) does +// not escape, then new(T) can be rewritten into a stack allocation. +// The same is true of slice literals. +// +// If optimizations are disabled (-N), this code is not used. +// Instead, the compiler assumes that any value whose address +// is taken without being immediately dereferenced +// needs to be moved to the heap, and new(T) and slice +// literals are always real allocations. + +func escapes(all *NodeList) { + visitBottomUp(all, escAnalyze) +} + +const ( + EscFuncUnknown = 0 + iota + EscFuncPlanned + EscFuncStarted + EscFuncTagged +) + +// There appear to be some loops in the escape graph, causing +// arbitrary recursion into deeper and deeper levels. +// Cut this off safely by making minLevel sticky: once you +// get that deep, you cannot go down any further but you also +// cannot go up any further. This is a conservative fix. +// Making minLevel smaller (more negative) would handle more +// complex chains of indirections followed by address-of operations, +// at the cost of repeating the traversal once for each additional +// allowed level when a loop is encountered. Using -2 suffices to +// pass all the tests we have written so far, which we assume matches +// the level of complexity we want the escape analysis code to handle. +const ( + MinLevel = -2 +) + +// A Level encodes the reference state and context applied to +// (stack, heap) allocated memory. +// +// value is the overall sum of *(1) and &(-1) operations encountered +// along a path from a destination (sink, return value) to a source +// (allocation, parameter). +// +// suffixValue is the maximum-copy-started-suffix-level applied to a sink. +// For example: +// sink = x.left.left --> level=2, x is dereferenced twice and does not escape to sink. +// sink = &Node{x} --> level=-1, x is accessible from sink via one "address of" +// sink = &Node{&Node{x}} --> level=-2, x is accessible from sink via two "address of" +// sink = &Node{&Node{x.left}} --> level=-1, but x is NOT accessible from sink because it was indirected and then copied. +// (The copy operations are sometimes implicit in the source code; in this case, +// value of x.left was copied into a field of a newly allocated Node) +// +// There's one of these for each Node, and the integer values +// rarely exceed even what can be stored in 4 bits, never mind 8. +type Level struct { + value, suffixValue int8 +} + +func (l Level) int() int { + return int(l.value) +} + +func levelFrom(i int) Level { + if i <= MinLevel { + return Level{value: MinLevel} + } + return Level{value: int8(i)} +} + +func satInc8(x int8) int8 { + if x == 127 { + return 127 + } + return x + 1 +} + +func satAdd8(x, y int8) int8 { + z := x + y + if x^y < 0 || x^z >= 0 { + return z + } + if x < 0 { + return -128 + } + return 127 +} + +func min8(a, b int8) int8 { + if a < b { + return a + } + return b +} + +func max8(a, b int8) int8 { + if a > b { + return a + } + return b +} + +// inc returns the level l + 1, representing the effect of an indirect (*) operation. +func (l Level) inc() Level { + if l.value <= MinLevel { + return Level{value: MinLevel} + } + return Level{value: satInc8(l.value), suffixValue: satInc8(l.suffixValue)} +} + +// dec returns the level l - 1, representing the effect of an address-of (&) operation. +func (l Level) dec() Level { + if l.value <= MinLevel { + return Level{value: MinLevel} + } + return Level{value: l.value - 1, suffixValue: l.suffixValue - 1} +} + +// copy returns the level for a copy of a value with level l. +func (l Level) copy() Level { + return Level{value: l.value, suffixValue: max8(l.suffixValue, 0)} +} + +func (l1 Level) min(l2 Level) Level { + return Level{ + value: min8(l1.value, l2.value), + suffixValue: min8(l1.suffixValue, l2.suffixValue)} +} + +// guaranteedDereference returns the number of dereferences +// applied to a pointer before addresses are taken/generated. +// This is the maximum level computed from path suffixes starting +// with copies where paths flow from destination to source. +func (l Level) guaranteedDereference() int { + return int(l.suffixValue) +} + +// Escape constants are numbered in order of increasing "escapiness" +// to help make inferences be monotonic. With the exception of +// EscNever which is sticky, eX < eY means that eY is more exposed +// than eX, and hence replaces it in a conservative analysis. +const ( + EscUnknown = iota + EscNone // Does not escape to heap, result, or parameters. + EscReturn // Is returned or reachable from returned. + EscScope // Allocated in an inner loop scope, assigned to an outer loop scope, + // which allows the construction of non-escaping but arbitrarily large linked + // data structures (i.e., not eligible for allocation in a fixed-size stack frame). + EscHeap // Reachable from the heap + EscNever // By construction will not escape. + EscBits = 3 + EscMask = (1 << EscBits) - 1 + EscContentEscapes = 1 << EscBits // value obtained by indirect of parameter escapes to heap + EscReturnBits = EscBits + 1 + // Node.esc encoding = | escapeReturnEncoding:(width-4) | contentEscapes:1 | escEnum:3 +) + +// escMax returns the maximum of an existing escape value +// (and its additional parameter flow flags) and a new escape type. +func escMax(e, etype uint16) uint16 { + if e&EscMask >= EscScope { + // normalize + if e&^EscMask != 0 { + Fatal("Escape information had unexpected return encoding bits (w/ EscScope, EscHeap, EscNever), e&EscMask=%v", e&EscMask) + } + } + if e&EscMask > etype { + return e + } + if etype == EscNone || etype == EscReturn { + return (e &^ EscMask) | etype + } + return etype +} + +// For each input parameter to a function, the escapeReturnEncoding describes +// how the parameter may leak to the function's outputs. This is currently the +// "level" of the leak where level is 0 or larger (negative level means stored into +// something whose address is returned -- but that implies stored into the heap, +// hence EscHeap, which means that the details are not currently relevant. ) +const ( + bitsPerOutputInTag = 3 // For each output, the number of bits for a tag + bitsMaskForTag = uint16(1<dsts, %d edges\n", e->dstcount, e->edgecount); + + // visit the upstream of each dst, mark address nodes with + // addrescapes, mark parameters unsafe + for l := e.dsts; l != nil; l = l.Next { + escflood(e, l.N) + } + + // for all top level functions, tag the typenodes corresponding to the param nodes + for l := all; l != nil; l = l.Next { + if l.N.Op == ODCLFUNC { + esctag(e, l.N) + } + } + + if Debug['m'] != 0 { + for l := e.noesc; l != nil; l = l.Next { + if l.N.Esc == EscNone { + Warnl(int(l.N.Lineno), "%v %v does not escape", curfnSym(l.N), Nconv(l.N, obj.FmtShort)) + } + } + } +} + +func escfunc(e *EscState, func_ *Node) { + // print("escfunc %N %s\n", func->nname, e->recursive?"(recursive)":""); + + if func_.Esc != 1 { + Fatal("repeat escfunc %v", func_.Nname) + } + func_.Esc = EscFuncStarted + + saveld := e.loopdepth + e.loopdepth = 1 + savefn := Curfn + Curfn = func_ + + for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { + if ll.N.Op != ONAME { + continue + } + switch ll.N.Class { + // out params are in a loopdepth between the sink and all local variables + case PPARAMOUT: + ll.N.Escloopdepth = 0 + + case PPARAM: + ll.N.Escloopdepth = 1 + if ll.N.Type != nil && !haspointers(ll.N.Type) { + break + } + if Curfn.Nbody == nil && !Curfn.Noescape { + ll.N.Esc = EscHeap + } else { + ll.N.Esc = EscNone // prime for escflood later + } + e.noesc = list(e.noesc, ll.N) + } + } + + // in a mutually recursive group we lose track of the return values + if e.recursive { + for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { + if ll.N.Op == ONAME && ll.N.Class == PPARAMOUT { + escflows(e, &e.theSink, ll.N) + } + } + } + + escloopdepthlist(e, Curfn.Nbody) + esclist(e, Curfn.Nbody, Curfn) + Curfn = savefn + e.loopdepth = saveld +} + +// Mark labels that have no backjumps to them as not increasing e->loopdepth. +// Walk hasn't generated (goto|label)->left->sym->label yet, so we'll cheat +// and set it to one of the following two. Then in esc we'll clear it again. +var looping Label + +var nonlooping Label + +func escloopdepthlist(e *EscState, l *NodeList) { + for ; l != nil; l = l.Next { + escloopdepth(e, l.N) + } +} + +func escloopdepth(e *EscState, n *Node) { + if n == nil { + return + } + + escloopdepthlist(e, n.Ninit) + + switch n.Op { + case OLABEL: + if n.Left == nil || n.Left.Sym == nil { + Fatal("esc:label without label: %v", Nconv(n, obj.FmtSign)) + } + + // Walk will complain about this label being already defined, but that's not until + // after escape analysis. in the future, maybe pull label & goto analysis out of walk and put before esc + // if(n->left->sym->label != nil) + // fatal("escape analysis messed up analyzing label: %+N", n); + n.Left.Sym.Label = &nonlooping + + case OGOTO: + if n.Left == nil || n.Left.Sym == nil { + Fatal("esc:goto without label: %v", Nconv(n, obj.FmtSign)) + } + + // If we come past one that's uninitialized, this must be a (harmless) forward jump + // but if it's set to nonlooping the label must have preceded this goto. + if n.Left.Sym.Label == &nonlooping { + n.Left.Sym.Label = &looping + } + } + + escloopdepth(e, n.Left) + escloopdepth(e, n.Right) + escloopdepthlist(e, n.List) + escloopdepth(e, n.Ntest) + escloopdepth(e, n.Nincr) + escloopdepthlist(e, n.Nbody) + escloopdepthlist(e, n.Nelse) + escloopdepthlist(e, n.Rlist) +} + +func esclist(e *EscState, l *NodeList, up *Node) { + for ; l != nil; l = l.Next { + esc(e, l.N, up) + } +} + +func esc(e *EscState, n *Node, up *Node) { + if n == nil { + return + } + + lno := int(setlineno(n)) + + // ninit logically runs at a different loopdepth than the rest of the for loop. + esclist(e, n.Ninit, n) + + if n.Op == OFOR || n.Op == ORANGE { + e.loopdepth++ + } + + // type switch variables have no ODCL. + // process type switch as declaration. + // must happen before processing of switch body, + // so before recursion. + if n.Op == OSWITCH && n.Ntest != nil && n.Ntest.Op == OTYPESW { + for ll := n.List; ll != nil; ll = ll.Next { // cases + + // ll->n->nname is the variable per case + if ll.N.Nname != nil { + ll.N.Nname.Escloopdepth = e.loopdepth + } + } + } + + esc(e, n.Left, n) + esc(e, n.Right, n) + esc(e, n.Ntest, n) + esc(e, n.Nincr, n) + esclist(e, n.Nbody, n) + esclist(e, n.Nelse, n) + esclist(e, n.List, n) + esclist(e, n.Rlist, n) + + if n.Op == OFOR || n.Op == ORANGE { + e.loopdepth-- + } + + if Debug['m'] > 1 { + fmt.Printf("%v:[%d] %v esc: %v\n", Ctxt.Line(int(lineno)), e.loopdepth, funcSym(Curfn), n) + } + + switch n.Op { + // Record loop depth at declaration. + case ODCL: + if n.Left != nil { + n.Left.Escloopdepth = e.loopdepth + } + + case OLABEL: + if n.Left.Sym.Label == &nonlooping { + if Debug['m'] > 1 { + fmt.Printf("%v:%v non-looping label\n", Ctxt.Line(int(lineno)), n) + } + } else if n.Left.Sym.Label == &looping { + if Debug['m'] > 1 { + fmt.Printf("%v: %v looping label\n", Ctxt.Line(int(lineno)), n) + } + e.loopdepth++ + } + + // See case OLABEL in escloopdepth above + // else if(n->left->sym->label == nil) + // fatal("escape analysis missed or messed up a label: %+N", n); + + n.Left.Sym.Label = nil + + // Everything but fixed array is a dereference. + case ORANGE: + 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: + if n.Ntest != nil && n.Ntest.Op == OTYPESW { + for ll := n.List; ll != nil; ll = ll.Next { // cases + + // ntest->right is the argument of the .(type), + // ll->n->nname is the variable per case + escassign(e, ll.N.Nname, n.Ntest.Right) + } + } + + // Filter out the following special case. + // + // func (b *Buffer) Foo() { + // n, m := ... + // b.buf = b.buf[n:m] + // } + // + // This assignment is a no-op for escape analysis, + // it does not store any new pointers into b that were not already there. + // However, without this special case b will escape, because we assign to OIND/ODOTPTR. + case OAS, OASOP, OASWB: + if (n.Left.Op == OIND || n.Left.Op == ODOTPTR) && n.Left.Left.Op == ONAME && // dst is ONAME dereference + (n.Right.Op == OSLICE || n.Right.Op == OSLICE3 || n.Right.Op == OSLICESTR) && // src is slice operation + (n.Right.Left.Op == OIND || n.Right.Left.Op == ODOTPTR) && n.Right.Left.Left.Op == ONAME && // slice is applied to ONAME dereference + n.Left.Left == n.Right.Left.Left { // dst and src reference the same base ONAME + + // Here we also assume that the statement will not contain calls, + // that is, that order will move any calls to init. + // Otherwise base ONAME value could change between the moments + // when we evaluate it for dst and for src. + // + // Note, this optimization does not apply to OSLICEARR, + // because it does introduce a new pointer into b that was not already there + // (pointer to b itself). After such assignment, if b contents escape, + // 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 { + Warnl(int(n.Lineno), "%v ignoring self-assignment to %v", curfnSym(n), Nconv(n.Left, obj.FmtShort)) + } + + break + } + + escassign(e, n.Left, n.Right) + + case OAS2: // x,y = a,b + if count(n.List) == count(n.Rlist) { + ll := n.List + lr := n.Rlist + for ; ll != nil; ll, lr = ll.Next, lr.Next { + escassign(e, ll.N, lr.N) + } + } + + case OAS2RECV, // v, ok = <-ch + OAS2MAPR, // v, ok = m[k] + OAS2DOTTYPE: // v, ok = x.(type) + escassign(e, n.List.N, n.Rlist.N) + + case OSEND: // ch <- x + escassign(e, &e.theSink, n.Right) + + case ODEFER: + if e.loopdepth == 1 { // top level + break + } + // arguments leak out of scope + // TODO: leak to a dummy node instead + fallthrough + + case OPROC: + // go f(x) - f and x escape + escassign(e, &e.theSink, n.Left.Left) + + escassign(e, &e.theSink, n.Left.Right) // ODDDARG for call + for ll := n.Left.List; ll != nil; ll = ll.Next { + escassign(e, &e.theSink, ll.N) + } + + case OCALLMETH, OCALLFUNC, OCALLINTER: + esccall(e, n, up) + + // esccall already done on n->rlist->n. tie it's escretval to n->list + case OAS2FUNC: // x,y = f() + lr := n.Rlist.N.Escretval + + var ll *NodeList + for ll = n.List; lr != nil && ll != nil; lr, ll = lr.Next, ll.Next { + escassign(e, ll.N, lr.N) + } + if lr != nil || ll != nil { + Fatal("esc oas2func") + } + + case ORETURN: + ll := n.List + if count(n.List) == 1 && Curfn.Type.Outtuple > 1 { + // OAS2FUNC in disguise + // esccall already done on n->list->n + // tie n->list->n->escretval to curfn->dcl PPARAMOUT's + ll = n.List.N.Escretval + } + + for lr := Curfn.Func.Dcl; lr != nil && ll != nil; lr = lr.Next { + if lr.N.Op != ONAME || lr.N.Class != PPARAMOUT { + continue + } + escassign(e, lr.N, ll.N) + ll = ll.Next + } + + if ll != nil { + Fatal("esc return list") + } + + // Argument could leak through recover. + case OPANIC: + escassign(e, &e.theSink, n.Left) + + case OAPPEND: + if !n.Isddd { + 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) + + case OCONVIFACE: + n.Esc = EscNone // until proven otherwise + e.noesc = list(e.noesc, n) + n.Escloopdepth = e.loopdepth + escassign(e, n, n.Left) + + case OARRAYLIT: + if Isslice(n.Type) { + // Slice itself is not leaked until proven otherwise + n.Esc = EscNone + e.noesc = list(e.noesc, n) + n.Escloopdepth = e.loopdepth + } + + // Link values to array/slice + for ll := n.List; ll != nil; ll = ll.Next { + escassign(e, n, ll.N.Right) + } + + // Link values to struct. + case OSTRUCTLIT: + for ll := n.List; ll != nil; ll = ll.Next { + escassign(e, n, ll.N.Right) + } + + case OPTRLIT: + n.Esc = EscNone // until proven otherwise + e.noesc = list(e.noesc, n) + n.Escloopdepth = e.loopdepth + + // Link OSTRUCTLIT to OPTRLIT; if OPTRLIT escapes, OSTRUCTLIT elements do too. + escassign(e, n, n.Left) + + case OCALLPART: + n.Esc = EscNone // until proven otherwise + e.noesc = list(e.noesc, n) + n.Escloopdepth = e.loopdepth + + // Contents make it to memory, lose track. + escassign(e, &e.theSink, n.Left) + + case OMAPLIT: + n.Esc = EscNone // until proven otherwise + e.noesc = list(e.noesc, n) + n.Escloopdepth = e.loopdepth + + // Keys and values make it to memory, lose track. + for ll := n.List; ll != nil; ll = ll.Next { + escassign(e, &e.theSink, ll.N.Left) + escassign(e, &e.theSink, ll.N.Right) + } + + // Link addresses of captured variables to closure. + case OCLOSURE: + var a *Node + var v *Node + for ll := n.Func.Cvars; ll != nil; ll = ll.Next { + v = ll.N + if v.Op == OXXX { // unnamed out argument; see dcl.c:/^funcargs + continue + } + a = v.Closure + if !v.Name.Byval { + a = Nod(OADDR, a, nil) + a.Lineno = v.Lineno + a.Escloopdepth = e.loopdepth + typecheck(&a, Erv) + } + + escassign(e, n, a) + } + fallthrough + + case OMAKECHAN, + OMAKEMAP, + OMAKESLICE, + ONEW, + OARRAYRUNESTR, + OARRAYBYTESTR, + OSTRARRAYRUNE, + OSTRARRAYBYTE, + ORUNESTR: + n.Escloopdepth = e.loopdepth + + n.Esc = EscNone // until proven otherwise + e.noesc = list(e.noesc, n) + + case OADDSTR: + n.Escloopdepth = e.loopdepth + n.Esc = EscNone // until proven otherwise + e.noesc = list(e.noesc, n) + + // Arguments of OADDSTR do not escape. + + case OADDR: + n.Esc = EscNone // until proven otherwise + e.noesc = list(e.noesc, n) + + // current loop depth is an upper bound on actual loop depth + // of addressed value. + n.Escloopdepth = e.loopdepth + + // for &x, use loop depth of x if known. + // it should always be known, but if not, be conservative + // and keep the current loop depth. + if n.Left.Op == ONAME { + switch n.Left.Class { + case PAUTO: + if n.Left.Escloopdepth != 0 { + n.Escloopdepth = n.Left.Escloopdepth + } + + // PPARAM is loop depth 1 always. + // PPARAMOUT is loop depth 0 for writes + // but considered loop depth 1 for address-of, + // so that writing the address of one result + // to another (or the same) result makes the + // first result move to the heap. + case PPARAM, PPARAMOUT: + n.Escloopdepth = 1 + } + } + } + + lineno = int32(lno) +} + +// Assert that expr somehow gets assigned to dst, if non nil. for +// dst==nil, any name node expr still must be marked as being +// evaluated in curfn. For expr==nil, dst must still be examined for +// evaluations inside it (e.g *f(x) = y) +func escassign(e *EscState, dst *Node, src *Node) { + if isblank(dst) || dst == nil || src == nil || src.Op == ONONAME || src.Op == OXXX { + return + } + + if Debug['m'] > 1 { + fmt.Printf("%v:[%d] %v escassign: %v(%v)[%v] = %v(%v)[%v]\n", + 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)) + } + + setlineno(dst) + + // Analyze lhs of assignment. + // Replace dst with e->theSink if we can't track it. + switch dst.Op { + default: + Dump("dst", dst) + Fatal("escassign: unexpected dst") + + case OARRAYLIT, + OCLOSURE, + OCONV, + OCONVIFACE, + OCONVNOP, + OMAPLIT, + OSTRUCTLIT, + OPTRLIT, + OCALLPART: + break + + case ONAME: + if dst.Class == PEXTERN { + dst = &e.theSink + } + + case ODOT: // treat "dst.x = src" as "dst = src" + escassign(e, dst.Left, src) + + return + + case OINDEX: + if Isfixedarray(dst.Left.Type) { + escassign(e, dst.Left, src) + return + } + + dst = &e.theSink // lose track of dereference + + case OIND, ODOTPTR: + dst = &e.theSink // lose track of dereference + + // lose track of key and value + case OINDEXMAP: + escassign(e, &e.theSink, dst.Right) + + dst = &e.theSink + } + + lno := int(setlineno(src)) + e.pdepth++ + + switch src.Op { + case OADDR, // dst = &x + OIND, // dst = *x + ODOTPTR, // dst = (*x).f + ONAME, + OPARAM, + ODDDARG, + OPTRLIT, + OARRAYLIT, + OMAPLIT, + OSTRUCTLIT, + OMAKECHAN, + OMAKEMAP, + OMAKESLICE, + OARRAYRUNESTR, + OARRAYBYTESTR, + OSTRARRAYRUNE, + OSTRARRAYBYTE, + OADDSTR, + ONEW, + OCALLPART, + ORUNESTR, + OCONVIFACE: + escflows(e, dst, src) + + case OCLOSURE: + // OCLOSURE is lowered to OPTRLIT, + // insert OADDR to account for the additional indirection. + a := Nod(OADDR, src, nil) + a.Lineno = src.Lineno + a.Escloopdepth = src.Escloopdepth + a.Type = Ptrto(src.Type) + escflows(e, dst, a) + + // Flowing multiple returns to a single dst happens when + // analyzing "go f(g())": here g() flows to sink (issue 4529). + case OCALLMETH, OCALLFUNC, OCALLINTER: + for ll := src.Escretval; ll != nil; ll = ll.Next { + escflows(e, dst, ll.N) + } + + // A non-pointer escaping from a struct does not concern us. + case ODOT: + if src.Type != nil && !haspointers(src.Type) { + break + } + fallthrough + + // Conversions, field access, slice all preserve the input value. + case OCONV, + OCONVNOP, + ODOTMETH, + // treat recv.meth as a value with recv in it, only happens in ODEFER and OPROC + // iface.method already leaks iface in esccall, no need to put in extra ODOTINTER edge here + ODOTTYPE, + ODOTTYPE2, + OSLICE, + OSLICE3, + OSLICEARR, + OSLICE3ARR, + OSLICESTR: + // Conversions, field access, slice all preserve the input value. + escassign(e, dst, src.Left) + + 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 + // the operands flow into the result. + // TODO(rsc): Decide what the story is here. This is unsettling. + case OADD, + OSUB, + OOR, + OXOR, + OMUL, + ODIV, + OMOD, + OLSH, + ORSH, + OAND, + OANDNOT, + OPLUS, + OMINUS, + OCOM: + escassign(e, dst, src.Left) + + escassign(e, dst, src.Right) + } + + e.pdepth-- + lineno = int32(lno) +} + +// Common case for escapes is 16 bits 000000000xxxEEEE +// where commonest cases for xxx encoding in-to-out pointer +// flow are 000, 001, 010, 011 and EEEE is computed Esc bits. +// Note width of xxx depends on value of constant +// bitsPerOutputInTag -- expect 2 or 3, so in practice the +// tag cache array is 64 or 128 long. Some entries will +// never be populated. +var tags [1 << (bitsPerOutputInTag + EscReturnBits)]string + +// mktag returns the string representation for an escape analysis tag. +func mktag(mask int) *string { + switch mask & EscMask { + case EscNone, EscReturn: + break + + default: + Fatal("escape mktag") + } + + if mask < len(tags) && tags[mask] != "" { + return &tags[mask] + } + + s := fmt.Sprintf("esc:0x%x", mask) + if mask < len(tags) { + tags[mask] = s + } + return &s +} + +// parsetag decodes an escape analysis tag and returns the esc value. +func parsetag(note *string) uint16 { + if note == nil || !strings.HasPrefix(*note, "esc:") { + return EscUnknown + } + em := uint16(atoi((*note)[4:])) + if em == 0 { + return EscNone + } + return em +} + +// describeEscape returns a string describing the escape tag. +// The result is either one of {EscUnknown, EscNone, EscHeap} which all have no further annotation +// or a description of parameter flow, which takes the form of an optional "contentToHeap" +// indicating that the content of this parameter is leaked to the heap, followed by a sequence +// of level encodings separated by spaces, one for each parameter, where _ means no flow, +// = means direct flow, and N asterisks (*) encodes content (obtained by indirection) flow. +// e.g., "contentToHeap _ =" means that a parameter's content (one or more dereferences) +// escapes to the heap, the parameter does not leak to the first output, but does leak directly +// to the second output (and if there are more than two outputs, there is no flow to those.) +func describeEscape(em uint16) string { + var s string + if em&EscMask == EscUnknown { + s = "EscUnknown" + } + if em&EscMask == EscNone { + s = "EscNone" + } + if em&EscMask == EscHeap { + s = "EscHeap" + } + if em&EscMask == EscReturn { + s = "EscReturn" + } + if em&EscMask == EscScope { + s = "EscScope" + } + if em&EscContentEscapes != 0 { + if s != "" { + s += " " + } + s += "contentToHeap" + } + for em >>= EscReturnBits; em != 0; em = em >> bitsPerOutputInTag { + // See encoding description above + if s != "" { + s += " " + } + switch embits := em & bitsMaskForTag; embits { + case 0: + s += "_" + case 1: + s += "=" + default: + for i := uint16(0); i < embits-1; i++ { + s += "*" + } + } + + } + return s +} + +// escassignfromtag models the input-to-output assignment flow of one of a function +// calls arguments, where the flow is encoded in "note". +func escassignfromtag(e *EscState, note *string, dsts *NodeList, src *Node) uint16 { + em := parsetag(note) + + if Debug['m'] > 2 { + fmt.Printf("%v::assignfromtag:: src=%v, em=%s\n", + Ctxt.Line(int(lineno)), Nconv(src, obj.FmtShort), describeEscape(em)) + } + + if em == EscUnknown { + escassign(e, &e.theSink, src) + return em + } + + if em == EscNone { + return em + } + + // If content inside parameter (reached via indirection) + // escapes to heap, mark as such. + if em&EscContentEscapes != 0 { + escassign(e, &e.theSink, addDereference(src)) + } + + em0 := em + for em >>= EscReturnBits; em != 0 && dsts != nil; em, dsts = em>>bitsPerOutputInTag, dsts.Next { + // Prefer the lowest-level path to the reference (for escape purposes). + // Two-bit encoding (for example. 1, 3, and 4 bits are other options) + // 01 = 0-level + // 10 = 1-level, (content escapes), + // 11 = 2-level, (content of content escapes), + embits := em & bitsMaskForTag + if embits > 0 { + n := src + for i := uint16(0); i < embits-1; i++ { + n = addDereference(n) // encode level>0 as indirections + } + escassign(e, dsts.N, n) + } + } + // If there are too many outputs to fit in the tag, + // that is handled at the encoding end as EscHeap, + // so there is no need to check here. + + if em != 0 && dsts == nil { + Fatal("corrupt esc tag %q or messed up escretval list\n", note) + } + return em0 +} + +// addDereference constructs a suitable OIND note applied to src. +// Because this is for purposes of escape accounting, not execution, +// some semantically dubious node combinations are (currently) possible. +func addDereference(n *Node) *Node { + ind := Nod(OIND, n, nil) + ind.Escloopdepth = n.Escloopdepth + ind.Lineno = n.Lineno + t := n.Type + if Istype(t, Tptr) { + // This should model our own sloppy use of OIND to encode + // decreasing levels of indirection; i.e., "indirecting" an array + // might yield the type of an element. To be enhanced... + t = t.Type + } + ind.Type = t + return ind +} + +// escNoteOutputParamFlow encodes maxEncodedLevel/.../1/0-level flow to the vargen'th parameter. +// Levels greater than maxEncodedLevel are replaced with maxEncodedLevel. +// If the encoding cannot describe the modified input level and output number, then EscHeap is returned. +func escNoteOutputParamFlow(e uint16, vargen int32, level Level) uint16 { + // Flow+level is encoded in two bits. + // 00 = not flow, xx = level+1 for 0 <= level <= maxEncodedLevel + // 16 bits for Esc allows 6x2bits or 4x3bits or 3x4bits if additional information would be useful. + if level.int() <= 0 && level.guaranteedDereference() > 0 { + return escMax(e|EscContentEscapes, EscNone) // At least one deref, thus only content. + } + if level.int() < 0 { + return EscHeap + } + if level.int() > maxEncodedLevel { + // Cannot encode larger values than maxEncodedLevel. + level = levelFrom(maxEncodedLevel) + } + encoded := uint16(level.int() + 1) + + shift := uint(bitsPerOutputInTag*(vargen-1) + EscReturnBits) + old := (e >> shift) & bitsMaskForTag + if old == 0 || encoded != 0 && encoded < old { + old = encoded + } + + encodedFlow := old << shift + if (encodedFlow>>shift)&bitsMaskForTag != old { + // Encoding failure defaults to heap. + return EscHeap + } + + return (e &^ (bitsMaskForTag << shift)) | encodedFlow +} + +// 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 +// nodes, which may be marked "noescape". Navigating the ast is slightly +// different for methods vs plain functions and for imported vs +// this-package +func esccall(e *EscState, n *Node, up *Node) { + var fntype *Type + + var fn *Node + switch n.Op { + default: + Fatal("esccall") + + case OCALLFUNC: + fn = n.Left + fntype = fn.Type + + case OCALLMETH: + fn = n.Left.Right.Sym.Def + if fn != nil { + fntype = fn.Type + } else { + fntype = n.Left.Type + } + + case OCALLINTER: + fntype = n.Left.Type + } + + ll := n.List + if n.List != nil && n.List.Next == nil { + a := n.List.N + if a.Type.Etype == TSTRUCT && a.Type.Funarg != 0 { // f(g()). + ll = a.Escretval + } + } + + 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 { + fmt.Printf("%v::esccall:: %v in recursive group\n", Ctxt.Line(int(lineno)), Nconv(n, obj.FmtShort)) + } + + // function in same mutually recursive group. Incorporate into flow graph. + // print("esc local fn: %N\n", fn->ntype); + if fn.Defn.Esc == EscFuncUnknown || n.Escretval != nil { + Fatal("graph inconsistency") + } + + // set up out list on this call node + for lr := fn.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) + } + + var src *Node + for lr := fn.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. + src = Nod(ODDDARG, nil, nil) + + src.Type = typ(TARRAY) + src.Type.Type = lr.N.Type.Type + src.Type.Bound = int64(count(ll)) + src.Type = Ptrto(src.Type) // make pointer so it will be tracked + src.Escloopdepth = e.loopdepth + src.Lineno = n.Lineno + src.Esc = EscNone // until we find otherwise + e.noesc = list(e.noesc, src) + n.Right = src + } + + if lr.N.Left != nil { + escassign(e, lr.N.Left, src) + } + if src != ll.N { + break + } + } + + // "..." arguments are untracked + for ; ll != nil; ll = ll.Next { + if Debug['m'] > 2 { + fmt.Printf("%v::esccall:: ... <- %v, untracked\n", Ctxt.Line(int(lineno)), Nconv(ll.N, obj.FmtShort)) + } + escassign(e, &e.theSink, ll.N) + } + + return + } + + // Imported or completely analyzed function. Use the escape tags. + if n.Escretval != nil { + Fatal("esc already decorated call %v\n", Nconv(n, obj.FmtSign)) + } + + if Debug['m'] > 2 { + fmt.Printf("%v::esccall:: %v not recursive\n", Ctxt.Line(int(lineno)), Nconv(n, obj.FmtShort)) + } + + // 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) + } + + // print("esc analyzed fn: %#N (%+T) returning (%+H)\n", fn, fntype, n->escretval); + + // Receiver. + if n.Op != OCALLFUNC { + t := getthisx(fntype).Type + src := n.Left.Left + if haspointers(t.Type) { + escassignfromtag(e, t.Note, n.Escretval, src) + } + } + + var a *Node + for t := getinargx(fntype).Type; ll != nil; ll = ll.Next { + src = ll.N + if t.Isddd && !n.Isddd { + // Introduce ODDDARG node to represent ... allocation. + src = Nod(ODDDARG, nil, nil) + + src.Escloopdepth = e.loopdepth + src.Lineno = n.Lineno + src.Type = typ(TARRAY) + src.Type.Type = t.Type.Type + src.Type.Bound = int64(count(ll)) + src.Type = Ptrto(src.Type) // make pointer so it will be tracked + src.Esc = EscNone // until we find otherwise + e.noesc = list(e.noesc, src) + n.Right = src + } + + if haspointers(t.Type) { + if escassignfromtag(e, t.Note, n.Escretval, src) == EscNone && up.Op != ODEFER && up.Op != OPROC { + a = src + for a.Op == OCONVNOP { + a = a.Left + } + switch a.Op { + // The callee has already been analyzed, so its arguments have esc tags. + // The argument is marked as not escaping at all. + // Record that fact so that any temporary used for + // synthesizing this expression can be reclaimed when + // the function returns. + // This 'noescape' is even stronger than the usual esc == EscNone. + // src->esc == EscNone means that src does not escape the current function. + // src->noescape = 1 here means that src does not escape this statement + // in the current function. + case OCALLPART, + OCLOSURE, + ODDDARG, + OARRAYLIT, + OPTRLIT, + OSTRUCTLIT: + a.Noescape = true + } + } + } + + if src != ll.N { + break + } + t = t.Down + } + + // "..." arguments are untracked + for ; ll != nil; ll = ll.Next { + escassign(e, &e.theSink, ll.N) + if Debug['m'] > 2 { + fmt.Printf("%v::esccall:: ... <- %v, untracked\n", Ctxt.Line(int(lineno)), Nconv(ll.N, obj.FmtShort)) + } + } +} + +// escflows records the link src->dst in dst, throwing out some quick wins, +// and also ensuring that dst is noted as a flow destination. +func escflows(e *EscState, dst *Node, src *Node) { + if dst == nil || src == nil || dst == src { + return + } + + // Don't bother building a graph for scalars. + if src.Type != nil && !haspointers(src.Type) { + return + } + + if Debug['m'] > 2 { + fmt.Printf("%v::flows:: %v <- %v\n", Ctxt.Line(int(lineno)), Nconv(dst, obj.FmtShort), Nconv(src, obj.FmtShort)) + } + + if dst.Escflowsrc == nil { + e.dsts = list(e.dsts, dst) + e.dstcount++ + } + + e.edgecount++ + + dst.Escflowsrc = list(dst.Escflowsrc, src) +} + +// Whenever we hit a reference node, the level goes up by one, and whenever +// we hit an OADDR, the level goes down by one. as long as we're on a level > 0 +// finding an OADDR just means we're following the upstream of a dereference, +// so this address doesn't leak (yet). +// If level == 0, it means the /value/ of this node can reach the root of this flood. +// so if this node is an OADDR, it's argument should be marked as escaping iff +// it's currfn/e->loopdepth are different from the flood's root. +// Once an object has been moved to the heap, all of it's upstream should be considered +// escaping to the global scope. +func escflood(e *EscState, dst *Node) { + switch dst.Op { + case ONAME, OCLOSURE: + break + + default: + return + } + + if Debug['m'] > 1 { + 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 { + walkgen++ + escwalk(e, levelFrom(0), dst, l.N) + } +} + +// funcOutputAndInput reports whether dst and src correspond to output and input parameters of the same function. +func funcOutputAndInput(dst, src *Node) bool { + // Note if dst is marked as escaping, then "returned" is too weak. + return dst.Op == ONAME && dst.Class == PPARAMOUT && + src.Op == ONAME && src.Class == PPARAM && src.Curfn == dst.Curfn +} + +func escwalk(e *EscState, level Level, dst *Node, src *Node) { + + if src.Walkgen == walkgen { + // Esclevels are vectors, do not compare as integers, + // and must use "min" of old and new to guarantee + // convergence. + level = level.min(src.Esclevel) + if level == src.Esclevel { + return + } + } + + src.Walkgen = walkgen + src.Esclevel = level + + if Debug['m'] > 1 { + 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), curfnSym(src), src.Escloopdepth) + } + + e.pdepth++ + + // Input parameter flowing to output parameter? + var leaks bool + if funcOutputAndInput(dst, src) && src.Esc&EscMask < EscScope && dst.Esc != EscHeap { + // This case handles: + // 1. return in + // 2. return &in + // 3. tmp := in; return &tmp + // 4. return *in + if Debug['m'] != 0 { + if Debug['m'] == 1 { + Warnl(int(src.Lineno), "leaking param: %v to result %v level=%v", Nconv(src, obj.FmtShort), dst.Sym, level.int()) + } else { + Warnl(int(src.Lineno), "leaking param: %v to result %v level=%v", Nconv(src, obj.FmtShort), dst.Sym, level) + } + } + if src.Esc&EscMask != EscReturn { + src.Esc = EscReturn | src.Esc&EscContentEscapes + } + src.Esc = escNoteOutputParamFlow(src.Esc, dst.Vargen, level) + goto recurse + } + + // If parameter content escapes to heap, set EscContentEscapes + // Note minor confusion around escape from pointer-to-struct vs escape from struct + if dst.Esc == EscHeap && + src.Op == ONAME && src.Class == PPARAM && src.Esc&EscMask < EscScope && + level.int() > 0 { + src.Esc = escMax(EscContentEscapes|src.Esc, EscNone) + if Debug['m'] != 0 { + Warnl(int(src.Lineno), "mark escaped content: %v", Nconv(src, obj.FmtShort)) + } + } + + leaks = level.int() <= 0 && level.guaranteedDereference() <= 0 && dst.Escloopdepth < src.Escloopdepth + + switch src.Op { + case ONAME: + if src.Class == PPARAM && (leaks || dst.Escloopdepth < 0) && src.Esc&EscMask < EscScope { + if level.guaranteedDereference() > 0 { + src.Esc = escMax(EscContentEscapes|src.Esc, EscNone) + if Debug['m'] != 0 { + if Debug['m'] == 1 { + Warnl(int(src.Lineno), "leaking param content: %v", Nconv(src, obj.FmtShort)) + } else { + Warnl(int(src.Lineno), "leaking param content: %v level=%v dst.eld=%v src.eld=%v dst=%v", + Nconv(src, obj.FmtShort), level, dst.Escloopdepth, src.Escloopdepth, Nconv(dst, obj.FmtShort)) + } + } + } else { + src.Esc = EscScope + if Debug['m'] != 0 { + if Debug['m'] == 1 { + Warnl(int(src.Lineno), "leaking param: %v", Nconv(src, obj.FmtShort)) + } else { + Warnl(int(src.Lineno), "leaking param: %v level=%v dst.eld=%v src.eld=%v dst=%v", + Nconv(src, obj.FmtShort), level, dst.Escloopdepth, src.Escloopdepth, Nconv(dst, obj.FmtShort)) + } + } + } + } + + // Treat a PPARAMREF closure variable as equivalent to the + // original variable. + if src.Class == PPARAMREF { + if leaks && Debug['m'] != 0 { + Warnl(int(src.Lineno), "leaking closure reference %v", Nconv(src, obj.FmtShort)) + } + escwalk(e, level, dst, src.Closure) + } + + case OPTRLIT, OADDR: + if leaks { + src.Esc = EscHeap + addrescapes(src.Left) + if Debug['m'] != 0 { + p := src + if p.Left.Op == OCLOSURE { + p = p.Left // merely to satisfy error messages in tests + } + if Debug['m'] > 1 { + Warnl(int(src.Lineno), "%v escapes to heap, level=%v, dst.eld=%v, src.eld=%v", + Nconv(p, obj.FmtShort), level, dst.Escloopdepth, src.Escloopdepth) + } else { + Warnl(int(src.Lineno), "%v escapes to heap", Nconv(p, obj.FmtShort)) + } + } + } + + escwalk(e, level.dec(), dst, src.Left) + + case OAPPEND: + escwalk(e, level, dst, src.List.N) + + case OARRAYLIT: + 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, + OMAKECHAN, + OMAKEMAP, + OMAKESLICE, + OARRAYRUNESTR, + OARRAYBYTESTR, + OSTRARRAYRUNE, + OSTRARRAYBYTE, + OADDSTR, + OMAPLIT, + ONEW, + OCLOSURE, + OCALLPART, + ORUNESTR, + OCONVIFACE: + if leaks { + src.Esc = EscHeap + if Debug['m'] != 0 { + Warnl(int(src.Lineno), "%v escapes to heap", Nconv(src, obj.FmtShort)) + } + } + + case ODOT, + OSLICE, + OSLICEARR, + OSLICE3, + OSLICE3ARR, + OSLICESTR: + escwalk(e, level, dst, src.Left) + + case OINDEX: + if Isfixedarray(src.Left.Type) { + escwalk(e, level, dst, src.Left) + break + } + fallthrough + + case ODOTPTR, OINDEXMAP, OIND: + escwalk(e, level.inc(), dst, src.Left) + + // In this case a link went directly to a call, but should really go + // to the dummy .outN outputs that were created for the call that + // themselves link to the inputs with levels adjusted. + // See e.g. #10466 + // This can only happen with functions returning a single result. + case OCALLMETH, OCALLFUNC, OCALLINTER: + if src.Escretval != nil { + if Debug['m'] > 1 { + fmt.Printf("%v:[%d] dst %v escwalk replace src: %v with %v\n", + Ctxt.Line(int(lineno)), e.loopdepth, + Nconv(dst, obj.FmtShort), Nconv(src, obj.FmtShort), Nconv(src.Escretval.N, obj.FmtShort)) + } + src = src.Escretval.N + } + } + +recurse: + level = level.copy() + for ll := src.Escflowsrc; ll != nil; ll = ll.Next { + escwalk(e, level, dst, ll.N) + } + + e.pdepth-- +} + +func esctag(e *EscState, func_ *Node) { + func_.Esc = EscFuncTagged + + // External functions are assumed unsafe, + // unless //go:noescape is given before the declaration. + if func_.Nbody == nil { + if func_.Noescape { + for t := getinargx(func_.Type).Type; t != nil; t = t.Down { + if haspointers(t.Type) { + t.Note = mktag(EscNone) + } + } + } + + return + } + + savefn := Curfn + Curfn = func_ + + for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { + if ll.N.Op != ONAME { + continue + } + + switch ll.N.Esc & EscMask { + case EscNone, // not touched by escflood + EscReturn: + if haspointers(ll.N.Type) { // don't bother tagging for scalars + ll.N.Paramfld.Note = mktag(int(ll.N.Esc)) + } + + case EscHeap, // touched by escflood, moved to heap + EscScope: // touched by escflood, value leaves scope + break + } + } + + Curfn = savefn +} diff --git a/src/cmd/compile/internal/gc/export.go b/src/cmd/compile/internal/gc/export.go new file mode 100644 index 0000000000..614de4e2ce --- /dev/null +++ b/src/cmd/compile/internal/gc/export.go @@ -0,0 +1,560 @@ +// Copyright 2009 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 gc + +import ( + "cmd/internal/obj" + "fmt" + "sort" + "unicode" + "unicode/utf8" +) + +var asmlist *NodeList + +// Mark n's symbol as exported +func exportsym(n *Node) { + if n == nil || n.Sym == nil { + return + } + if n.Sym.Flags&(SymExport|SymPackage) != 0 { + if n.Sym.Flags&SymPackage != 0 { + Yyerror("export/package mismatch: %v", n.Sym) + } + return + } + + n.Sym.Flags |= SymExport + + if Debug['E'] != 0 { + fmt.Printf("export symbol %v\n", n.Sym) + } + exportlist = list(exportlist, n) +} + +func exportname(s string) bool { + if s[0] < utf8.RuneSelf { + return 'A' <= s[0] && s[0] <= 'Z' + } + r, _ := utf8.DecodeRuneInString(s) + return unicode.IsUpper(r) +} + +func initname(s string) bool { + return s == "init" +} + +// exportedsym reports whether a symbol will be visible +// to files that import our package. +func exportedsym(sym *Sym) bool { + // Builtins are visible everywhere. + if sym.Pkg == builtinpkg || sym.Origpkg == builtinpkg { + return true + } + + return sym.Pkg == localpkg && exportname(sym.Name) +} + +func autoexport(n *Node, ctxt uint8) { + if n == nil || n.Sym == nil { + return + } + if (ctxt != PEXTERN && ctxt != PFUNC) || dclcontext != PEXTERN { + return + } + if n.Param != nil && n.Ntype != nil && n.Ntype.Op == OTFUNC && n.Ntype.Left != nil { // method + return + } + + // -A is for cmd/gc/mkbuiltin script, so export everything + if Debug['A'] != 0 || exportname(n.Sym.Name) || initname(n.Sym.Name) { + exportsym(n) + } + if asmhdr != "" && n.Sym.Pkg == localpkg && n.Sym.Flags&SymAsm == 0 { + n.Sym.Flags |= SymAsm + asmlist = list(asmlist, n) + } +} + +func dumppkg(p *Pkg) { + if p == nil || p == localpkg || p.Exported != 0 || p == builtinpkg { + return + } + p.Exported = 1 + suffix := "" + if p.Direct == 0 { + suffix = " // indirect" + } + fmt.Fprintf(bout, "\timport %s %q%s\n", p.Name, p.Path, suffix) +} + +// Look for anything we need for the inline body +func reexportdeplist(ll *NodeList) { + for ; ll != nil; ll = ll.Next { + reexportdep(ll.N) + } +} + +func reexportdep(n *Node) { + if n == nil { + return + } + + //print("reexportdep %+hN\n", n); + switch n.Op { + case ONAME: + switch n.Class &^ PHEAP { + // methods will be printed along with their type + // nodes for T.Method expressions + case PFUNC: + if n.Left != nil && n.Left.Op == OTYPE { + break + } + + // nodes for method calls. + if n.Type == nil || n.Type.Thistuple > 0 { + break + } + fallthrough + + case PEXTERN: + if n.Sym != nil && !exportedsym(n.Sym) { + if Debug['E'] != 0 { + fmt.Printf("reexport name %v\n", n.Sym) + } + exportlist = list(exportlist, n) + } + } + + // Local variables in the bodies need their type. + case ODCL: + t := n.Left.Type + + if t != Types[t.Etype] && t != idealbool && t != idealstring { + if Isptr[t.Etype] { + t = t.Type + } + if t != nil && t.Sym != nil && t.Sym.Def != nil && !exportedsym(t.Sym) { + if Debug['E'] != 0 { + fmt.Printf("reexport type %v from declaration\n", t.Sym) + } + exportlist = list(exportlist, t.Sym.Def) + } + } + + case OLITERAL: + t := n.Type + if t != Types[n.Type.Etype] && t != idealbool && t != idealstring { + if Isptr[t.Etype] { + t = t.Type + } + if t != nil && t.Sym != nil && t.Sym.Def != nil && !exportedsym(t.Sym) { + if Debug['E'] != 0 { + fmt.Printf("reexport literal type %v\n", t.Sym) + } + exportlist = list(exportlist, t.Sym.Def) + } + } + fallthrough + + case OTYPE: + if n.Sym != nil && !exportedsym(n.Sym) { + if Debug['E'] != 0 { + fmt.Printf("reexport literal/type %v\n", n.Sym) + } + exportlist = list(exportlist, n) + } + + // for operations that need a type when rendered, put the type on the export list. + case OCONV, + OCONVIFACE, + OCONVNOP, + ORUNESTR, + OARRAYBYTESTR, + OARRAYRUNESTR, + OSTRARRAYBYTE, + OSTRARRAYRUNE, + ODOTTYPE, + ODOTTYPE2, + OSTRUCTLIT, + OARRAYLIT, + OPTRLIT, + OMAKEMAP, + OMAKESLICE, + OMAKECHAN: + t := n.Type + + if t.Sym == nil && t.Type != nil { + t = t.Type + } + if t != nil && t.Sym != nil && t.Sym.Def != nil && !exportedsym(t.Sym) { + if Debug['E'] != 0 { + fmt.Printf("reexport type for expression %v\n", t.Sym) + } + exportlist = list(exportlist, t.Sym.Def) + } + } + + reexportdep(n.Left) + reexportdep(n.Right) + reexportdeplist(n.List) + reexportdeplist(n.Rlist) + reexportdeplist(n.Ninit) + reexportdep(n.Ntest) + reexportdep(n.Nincr) + reexportdeplist(n.Nbody) + reexportdeplist(n.Nelse) +} + +func dumpexportconst(s *Sym) { + n := s.Def + typecheck(&n, Erv) + if n == nil || n.Op != OLITERAL { + Fatal("dumpexportconst: oconst nil: %v", s) + } + + t := n.Type // may or may not be specified + dumpexporttype(t) + + if t != nil && !isideal(t) { + fmt.Fprintf(bout, "\tconst %v %v = %v\n", Sconv(s, obj.FmtSharp), Tconv(t, obj.FmtSharp), Vconv(&n.Val, obj.FmtSharp)) + } else { + fmt.Fprintf(bout, "\tconst %v = %v\n", Sconv(s, obj.FmtSharp), Vconv(&n.Val, obj.FmtSharp)) + } +} + +func dumpexportvar(s *Sym) { + n := s.Def + typecheck(&n, Erv|Ecall) + if n == nil || n.Type == nil { + Yyerror("variable exported but not defined: %v", s) + return + } + + t := n.Type + dumpexporttype(t) + + if t.Etype == TFUNC && n.Class == PFUNC { + if n.Func != nil && n.Func.Inl != nil { + // when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet. + // currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package + if Debug['l'] < 2 { + typecheckinl(n) + } + + // NOTE: The space after %#S here is necessary for ld's export data parser. + fmt.Fprintf(bout, "\tfunc %v %v { %v }\n", Sconv(s, obj.FmtSharp), Tconv(t, obj.FmtShort|obj.FmtSharp), Hconv(n.Func.Inl, obj.FmtSharp)) + + reexportdeplist(n.Func.Inl) + } else { + fmt.Fprintf(bout, "\tfunc %v %v\n", Sconv(s, obj.FmtSharp), Tconv(t, obj.FmtShort|obj.FmtSharp)) + } + } else { + fmt.Fprintf(bout, "\tvar %v %v\n", Sconv(s, obj.FmtSharp), Tconv(t, obj.FmtSharp)) + } +} + +type methodbyname []*Type + +func (x methodbyname) Len() int { + return len(x) +} + +func (x methodbyname) Swap(i, j int) { + x[i], x[j] = x[j], x[i] +} + +func (x methodbyname) Less(i, j int) bool { + a := x[i] + b := x[j] + return stringsCompare(a.Sym.Name, b.Sym.Name) < 0 +} + +func dumpexporttype(t *Type) { + if t == nil { + return + } + if t.Printed != 0 || t == Types[t.Etype] || t == bytetype || t == runetype || t == errortype { + return + } + t.Printed = 1 + + if t.Sym != nil && t.Etype != TFIELD { + dumppkg(t.Sym.Pkg) + } + + dumpexporttype(t.Type) + dumpexporttype(t.Down) + + if t.Sym == nil || t.Etype == TFIELD { + return + } + + n := 0 + for f := t.Method; f != nil; f = f.Down { + dumpexporttype(f) + n++ + } + + m := make([]*Type, n) + i := 0 + for f := t.Method; f != nil; f = f.Down { + m[i] = f + i++ + } + sort.Sort(methodbyname(m[:n])) + + fmt.Fprintf(bout, "\ttype %v %v\n", Sconv(t.Sym, obj.FmtSharp), Tconv(t, obj.FmtSharp|obj.FmtLong)) + var f *Type + for i := 0; i < n; i++ { + f = m[i] + if f.Nointerface { + fmt.Fprintf(bout, "\t//go:nointerface\n") + } + if f.Type.Nname != nil && f.Type.Nname.Func.Inl != nil { // nname was set by caninl + + // when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet. + // currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package + if Debug['l'] < 2 { + typecheckinl(f.Type.Nname) + } + fmt.Fprintf(bout, "\tfunc (%v) %v %v { %v }\n", Tconv(getthisx(f.Type).Type, obj.FmtSharp), Sconv(f.Sym, obj.FmtShort|obj.FmtByte|obj.FmtSharp), Tconv(f.Type, obj.FmtShort|obj.FmtSharp), Hconv(f.Type.Nname.Func.Inl, obj.FmtSharp)) + reexportdeplist(f.Type.Nname.Func.Inl) + } else { + fmt.Fprintf(bout, "\tfunc (%v) %v %v\n", Tconv(getthisx(f.Type).Type, obj.FmtSharp), Sconv(f.Sym, obj.FmtShort|obj.FmtByte|obj.FmtSharp), Tconv(f.Type, obj.FmtShort|obj.FmtSharp)) + } + } +} + +func dumpsym(s *Sym) { + if s.Flags&SymExported != 0 { + return + } + s.Flags |= SymExported + + if s.Def == nil { + Yyerror("unknown export symbol: %v", s) + return + } + + // print("dumpsym %O %+S\n", s->def->op, s); + dumppkg(s.Pkg) + + switch s.Def.Op { + default: + Yyerror("unexpected export symbol: %v %v", Oconv(int(s.Def.Op), 0), s) + + case OLITERAL: + dumpexportconst(s) + + case OTYPE: + if s.Def.Type.Etype == TFORW { + Yyerror("export of incomplete type %v", s) + } else { + dumpexporttype(s.Def.Type) + } + + case ONAME: + dumpexportvar(s) + } +} + +func dumpexport() { + lno := lineno + + fmt.Fprintf(bout, "\n$$\npackage %s", localpkg.Name) + if safemode != 0 { + fmt.Fprintf(bout, " safe") + } + fmt.Fprintf(bout, "\n") + + for _, p := range pkgs { + if p.Direct != 0 { + dumppkg(p) + } + } + + for l := exportlist; l != nil; l = l.Next { + lineno = l.N.Lineno + dumpsym(l.N.Sym) + } + + fmt.Fprintf(bout, "\n$$\n") + lineno = lno +} + +/* + * import + */ + +/* + * return the sym for ss, which should match lexical + */ +func importsym(s *Sym, op int) *Sym { + if s.Def != nil && int(s.Def.Op) != op { + pkgstr := fmt.Sprintf("during import %q", importpkg.Path) + redeclare(s, pkgstr) + } + + // mark the symbol so it is not reexported + if s.Def == nil { + if exportname(s.Name) || initname(s.Name) { + s.Flags |= SymExport + } else { + s.Flags |= SymPackage // package scope + } + } + + return s +} + +/* + * return the type pkg.name, forward declaring if needed + */ +func pkgtype(s *Sym) *Type { + importsym(s, OTYPE) + if s.Def == nil || s.Def.Op != OTYPE { + t := typ(TFORW) + t.Sym = s + s.Def = typenod(t) + } + + if s.Def.Type == nil { + Yyerror("pkgtype %v", s) + } + return s.Def.Type +} + +var numImport = make(map[string]int) + +func importimport(s *Sym, path string) { + // Informational: record package name + // associated with import path, for use in + // human-readable messages. + + if isbadimport(path) { + errorexit() + } + p := mkpkg(path) + if p.Name == "" { + p.Name = s.Name + numImport[s.Name]++ + } else if p.Name != s.Name { + Yyerror("conflicting names %s and %s for package %q", p.Name, s.Name, p.Path) + } + + if incannedimport == 0 && myimportpath != "" && path == myimportpath { + Yyerror("import %q: package depends on %q (import cycle)", importpkg.Path, path) + errorexit() + } +} + +func importconst(s *Sym, t *Type, n *Node) { + importsym(s, OLITERAL) + Convlit(&n, t) + + if s.Def != nil { // TODO: check if already the same. + return + } + + if n.Op != OLITERAL { + Yyerror("expression must be a constant") + return + } + + if n.Sym != nil { + n1 := Nod(OXXX, nil, nil) + *n1 = *n + n = n1 + } + + n.Orig = newname(s) + n.Sym = s + declare(n, PEXTERN) + + if Debug['E'] != 0 { + fmt.Printf("import const %v\n", s) + } +} + +func importvar(s *Sym, t *Type) { + importsym(s, ONAME) + if s.Def != nil && s.Def.Op == ONAME { + if Eqtype(t, s.Def.Type) { + return + } + Yyerror("inconsistent definition for var %v during import\n\t%v (in %q)\n\t%v (in %q)", s, s.Def.Type, s.Importdef.Path, t, importpkg.Path) + } + + n := newname(s) + s.Importdef = importpkg + n.Type = t + declare(n, PEXTERN) + + if Debug['E'] != 0 { + fmt.Printf("import var %v %v\n", s, Tconv(t, obj.FmtLong)) + } +} + +func importtype(pt *Type, t *Type) { + // override declaration in unsafe.go for Pointer. + // there is no way in Go code to define unsafe.Pointer + // so we have to supply it. + if incannedimport != 0 && importpkg.Name == "unsafe" && pt.Nod.Sym.Name == "Pointer" { + t = Types[TUNSAFEPTR] + } + + if pt.Etype == TFORW { + n := pt.Nod + copytype(pt.Nod, t) + pt.Nod = n // unzero nod + pt.Sym.Importdef = importpkg + pt.Sym.Lastlineno = int32(parserline()) + declare(n, PEXTERN) + checkwidth(pt) + } else if !Eqtype(pt.Orig, t) { + Yyerror("inconsistent definition for type %v during import\n\t%v (in %q)\n\t%v (in %q)", pt.Sym, Tconv(pt, obj.FmtLong), pt.Sym.Importdef.Path, Tconv(t, obj.FmtLong), importpkg.Path) + } + + if Debug['E'] != 0 { + fmt.Printf("import type %v %v\n", pt, Tconv(t, obj.FmtLong)) + } +} + +func dumpasmhdr() { + var b *obj.Biobuf + + b, err := obj.Bopenw(asmhdr) + if err != nil { + Fatal("%v", err) + } + fmt.Fprintf(b, "// generated by %cg -asmhdr from package %s\n\n", Thearch.Thechar, localpkg.Name) + var n *Node + var t *Type + for l := asmlist; l != nil; l = l.Next { + n = l.N + if isblanksym(n.Sym) { + continue + } + switch n.Op { + case OLITERAL: + fmt.Fprintf(b, "#define const_%s %v\n", n.Sym.Name, Vconv(&n.Val, obj.FmtSharp)) + + case OTYPE: + t = n.Type + if t.Etype != TSTRUCT || t.Map != nil || t.Funarg != 0 { + break + } + fmt.Fprintf(b, "#define %s__size %d\n", t.Sym.Name, int(t.Width)) + for t = t.Type; t != nil; t = t.Down { + if !isblanksym(t.Sym) { + fmt.Fprintf(b, "#define %s_%s %d\n", n.Sym.Name, t.Sym.Name, int(t.Width)) + } + } + } + } + + obj.Bterm(b) +} diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go new file mode 100644 index 0000000000..9d8482bf76 --- /dev/null +++ b/src/cmd/compile/internal/gc/fmt.go @@ -0,0 +1,1736 @@ +// Copyright 2011 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 gc + +import ( + "bytes" + "cmd/internal/obj" + "fmt" + "strconv" + "strings" + "unicode/utf8" +) + +// +// Format conversions +// %L int Line numbers +// +// %E int etype values (aka 'Kind') +// +// %O int Node Opcodes +// Flags: "%#O": print go syntax. (automatic unless fmtmode == FDbg) +// +// %J Node* Node details +// Flags: "%hJ" suppresses things not relevant until walk. +// +// %V Val* Constant values +// +// %S Sym* Symbols +// Flags: +,- #: mode (see below) +// "%hS" unqualified identifier in any mode +// "%hhS" in export mode: unqualified identifier if exported, qualified if not +// +// %T Type* Types +// Flags: +,- #: mode (see below) +// 'l' definition instead of name. +// 'h' omit "func" and receiver in function types +// 'u' (only in -/Sym mode) print type identifiers wit package name instead of prefix. +// +// %N Node* Nodes +// Flags: +,- #: mode (see below) +// 'h' (only in +/debug mode) suppress recursion +// 'l' (only in Error mode) print "foo (type Bar)" +// +// %H NodeList* NodeLists +// Flags: those of %N +// ',' separate items with ',' instead of ';' +// +// In mparith1.c: +// %B Mpint* Big integers +// %F Mpflt* Big floats +// +// %S, %T and %N obey use the following flags to set the format mode: +const ( + FErr = iota + FDbg + FExp + FTypeId +) + +var fmtmode int = FErr + +var fmtpkgpfx int // %uT stickyness + +// +// E.g. for %S: %+S %#S %-S print an identifier properly qualified for debug/export/internal mode. +// +// The mode flags +, - and # are sticky, meaning they persist through +// recursions of %N, %T and %S, but not the h and l flags. The u flag is +// sticky only on %T recursions and only used in %-/Sym mode. + +// +// Useful format combinations: +// +// %+N %+H multiline recursive debug dump of node/nodelist +// %+hN %+hH non recursive debug dump +// +// %#N %#T export format +// %#lT type definition instead of name +// %#hT omit"func" and receiver in function signature +// +// %lN "foo (type Bar)" for error messages +// +// %-T type identifiers +// %-hT type identifiers without "func" and arg names in type signatures (methodsym) +// %-uT type identifiers with package name instead of prefix (typesym, dcommontype, typehash) +// + +func setfmode(flags *int) int { + fm := fmtmode + if *flags&obj.FmtSign != 0 { + fmtmode = FDbg + } else if *flags&obj.FmtSharp != 0 { + fmtmode = FExp + } else if *flags&obj.FmtLeft != 0 { + fmtmode = FTypeId + } + + *flags &^= (obj.FmtSharp | obj.FmtLeft | obj.FmtSign) + return fm +} + +// Fmt "%L": Linenumbers + +var goopnames = []string{ + OADDR: "&", + OADD: "+", + OADDSTR: "+", + OANDAND: "&&", + OANDNOT: "&^", + OAND: "&", + OAPPEND: "append", + OAS: "=", + OAS2: "=", + OBREAK: "break", + OCALL: "function call", // not actual syntax + OCAP: "cap", + OCASE: "case", + OCLOSE: "close", + OCOMPLEX: "complex", + OCOM: "^", + OCONTINUE: "continue", + OCOPY: "copy", + ODEC: "--", + ODELETE: "delete", + ODEFER: "defer", + ODIV: "/", + OEQ: "==", + OFALL: "fallthrough", + OFOR: "for", + OGE: ">=", + OGOTO: "goto", + OGT: ">", + OIF: "if", + OIMAG: "imag", + OINC: "++", + OIND: "*", + OLEN: "len", + OLE: "<=", + OLSH: "<<", + OLT: "<", + OMAKE: "make", + OMINUS: "-", + OMOD: "%", + OMUL: "*", + ONEW: "new", + ONE: "!=", + ONOT: "!", + OOROR: "||", + OOR: "|", + OPANIC: "panic", + OPLUS: "+", + OPRINTN: "println", + OPRINT: "print", + ORANGE: "range", + OREAL: "real", + ORECV: "<-", + ORECOVER: "recover", + ORETURN: "return", + ORSH: ">>", + OSELECT: "select", + OSEND: "<-", + OSUB: "-", + OSWITCH: "switch", + OXOR: "^", +} + +// Fmt "%O": Node opcodes +func Oconv(o int, flag int) string { + if (flag&obj.FmtSharp != 0) || fmtmode != FDbg { + if o >= 0 && o < len(goopnames) && goopnames[o] != "" { + return goopnames[o] + } + } + + if o >= 0 && o < len(opnames) && opnames[o] != "" { + return opnames[o] + } + + return fmt.Sprintf("O-%d", o) +} + +var classnames = []string{ + "Pxxx", + "PEXTERN", + "PAUTO", + "PPARAM", + "PPARAMOUT", + "PPARAMREF", + "PFUNC", +} + +// Fmt "%J": Node details. +func Jconv(n *Node, flag int) string { + var buf bytes.Buffer + + c := flag & obj.FmtShort + + if c == 0 && n.Ullman != 0 { + fmt.Fprintf(&buf, " u(%d)", n.Ullman) + } + + if c == 0 && n.Addable { + fmt.Fprintf(&buf, " a(%v)", n.Addable) + } + + if c == 0 && n.Vargen != 0 { + fmt.Fprintf(&buf, " g(%d)", n.Vargen) + } + + if n.Lineno != 0 { + fmt.Fprintf(&buf, " l(%d)", n.Lineno) + } + + if c == 0 && n.Xoffset != BADWIDTH { + fmt.Fprintf(&buf, " x(%d%+d)", n.Xoffset, n.Stkdelta) + } + + if n.Class != 0 { + s := "" + if n.Class&PHEAP != 0 { + s = ",heap" + } + if int(n.Class&^PHEAP) < len(classnames) { + fmt.Fprintf(&buf, " class(%s%s)", classnames[n.Class&^PHEAP], s) + } else { + fmt.Fprintf(&buf, " class(%d?%s)", n.Class&^PHEAP, s) + } + } + + if n.Colas { + fmt.Fprintf(&buf, " colas(%v)", n.Colas) + } + + if n.Funcdepth != 0 { + fmt.Fprintf(&buf, " f(%d)", n.Funcdepth) + } + + switch n.Esc { + case EscUnknown: + break + + case EscHeap: + buf.WriteString(" esc(h)") + + case EscScope: + buf.WriteString(" esc(s)") + + case EscNone: + buf.WriteString(" esc(no)") + + case EscNever: + if c == 0 { + buf.WriteString(" esc(N)") + } + + default: + fmt.Fprintf(&buf, " esc(%d)", n.Esc) + } + + if n.Escloopdepth != 0 { + fmt.Fprintf(&buf, " ld(%d)", n.Escloopdepth) + } + + if c == 0 && n.Typecheck != 0 { + fmt.Fprintf(&buf, " tc(%d)", n.Typecheck) + } + + if c == 0 && n.Dodata != 0 { + fmt.Fprintf(&buf, " dd(%d)", n.Dodata) + } + + if n.Isddd { + fmt.Fprintf(&buf, " isddd(%v)", n.Isddd) + } + + if n.Implicit { + fmt.Fprintf(&buf, " implicit(%v)", n.Implicit) + } + + if n.Embedded != 0 { + fmt.Fprintf(&buf, " embedded(%d)", n.Embedded) + } + + if n.Addrtaken { + buf.WriteString(" addrtaken") + } + + if n.Assigned { + buf.WriteString(" assigned") + } + + if c == 0 && n.Used { + fmt.Fprintf(&buf, " used(%v)", n.Used) + } + return buf.String() +} + +// Fmt "%V": Values +func Vconv(v *Val, flag int) string { + switch v.Ctype { + case CTINT: + if (flag&obj.FmtSharp != 0) || fmtmode == FExp { + return Bconv(v.U.(*Mpint), obj.FmtSharp) + } + return Bconv(v.U.(*Mpint), 0) + + case CTRUNE: + x := Mpgetfix(v.U.(*Mpint)) + if ' ' <= x && x < 0x80 && x != '\\' && x != '\'' { + return fmt.Sprintf("'%c'", int(x)) + } + if 0 <= x && x < 1<<16 { + return fmt.Sprintf("'\\u%04x'", uint(int(x))) + } + if 0 <= x && x <= utf8.MaxRune { + return fmt.Sprintf("'\\U%08x'", uint64(x)) + } + return fmt.Sprintf("('\\x00' + %v)", v.U.(*Mpint)) + + case CTFLT: + if (flag&obj.FmtSharp != 0) || fmtmode == FExp { + return Fconv(v.U.(*Mpflt), 0) + } + return Fconv(v.U.(*Mpflt), obj.FmtSharp) + + case CTCPLX: + if (flag&obj.FmtSharp != 0) || fmtmode == FExp { + return fmt.Sprintf("(%v+%vi)", &v.U.(*Mpcplx).Real, &v.U.(*Mpcplx).Imag) + } + if mpcmpfltc(&v.U.(*Mpcplx).Real, 0) == 0 { + return fmt.Sprintf("%vi", Fconv(&v.U.(*Mpcplx).Imag, obj.FmtSharp)) + } + if mpcmpfltc(&v.U.(*Mpcplx).Imag, 0) == 0 { + return Fconv(&v.U.(*Mpcplx).Real, 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.(*Mpcplx).Real, obj.FmtSharp), Fconv(&v.U.(*Mpcplx).Imag, obj.FmtSharp)) + + case CTSTR: + return strconv.Quote(v.U.(string)) + + case CTBOOL: + if v.U.(bool) { + return "true" + } + return "false" + + case CTNIL: + return "nil" + } + + return fmt.Sprintf("", v.Ctype) +} + +/* +s%,%,\n%g +s%\n+%\n%g +s%^[ ]*T%%g +s%,.*%%g +s%.+% [T&] = "&",%g +s%^ ........*\]%&~%g +s%~ %%g +*/ +var etnames = []string{ + TINT: "INT", + TUINT: "UINT", + TINT8: "INT8", + TUINT8: "UINT8", + TINT16: "INT16", + TUINT16: "UINT16", + TINT32: "INT32", + TUINT32: "UINT32", + TINT64: "INT64", + TUINT64: "UINT64", + TUINTPTR: "UINTPTR", + TFLOAT32: "FLOAT32", + TFLOAT64: "FLOAT64", + TCOMPLEX64: "COMPLEX64", + TCOMPLEX128: "COMPLEX128", + TBOOL: "BOOL", + TPTR32: "PTR32", + TPTR64: "PTR64", + TFUNC: "FUNC", + TARRAY: "ARRAY", + TSTRUCT: "STRUCT", + TCHAN: "CHAN", + TMAP: "MAP", + TINTER: "INTER", + TFORW: "FORW", + TFIELD: "FIELD", + TSTRING: "STRING", + TANY: "ANY", +} + +// Fmt "%E": etype +func Econv(et int, flag int) string { + if et >= 0 && et < len(etnames) && etnames[et] != "" { + return etnames[et] + } + return fmt.Sprintf("E-%d", et) +} + +// Fmt "%S": syms +func symfmt(s *Sym, flag int) string { + if s.Pkg != nil && flag&obj.FmtShort == 0 { + switch fmtmode { + case FErr: // This is for the user + if s.Pkg == localpkg { + return s.Name + } + + // If the name was used by multiple packages, display the full path, + if s.Pkg.Name != "" && numImport[s.Pkg.Name] > 1 { + return fmt.Sprintf("%q.%s", s.Pkg.Path, s.Name) + } + return fmt.Sprintf("%s.%s", s.Pkg.Name, s.Name) + + case FDbg: + return fmt.Sprintf("%s.%s", s.Pkg.Name, s.Name) + + case FTypeId: + if flag&obj.FmtUnsigned != 0 { + return fmt.Sprintf("%s.%s", s.Pkg.Name, s.Name) // dcommontype, typehash + } + return fmt.Sprintf("%s.%s", s.Pkg.Prefix, s.Name) // (methodsym), typesym, weaksym + + case FExp: + if s.Name != "" && s.Name[0] == '.' { + Fatal("exporting synthetic symbol %s", s.Name) + } + if s.Pkg != builtinpkg { + return fmt.Sprintf("@%q.%s", s.Pkg.Path, s.Name) + } + } + } + + if flag&obj.FmtByte != 0 { + // FmtByte (hh) implies FmtShort (h) + // skip leading "type." in method name + p := s.Name + if i := strings.LastIndex(s.Name, "."); i >= 0 { + p = s.Name[i+1:] + } + + // exportname needs to see the name without the prefix too. + if (fmtmode == FExp && !exportname(p)) || fmtmode == FDbg { + return fmt.Sprintf("@%q.%s", s.Pkg.Path, p) + } + + return p + } + + return s.Name +} + +var basicnames = []string{ + TINT: "int", + TUINT: "uint", + TINT8: "int8", + TUINT8: "uint8", + TINT16: "int16", + TUINT16: "uint16", + TINT32: "int32", + TUINT32: "uint32", + TINT64: "int64", + TUINT64: "uint64", + TUINTPTR: "uintptr", + TFLOAT32: "float32", + TFLOAT64: "float64", + TCOMPLEX64: "complex64", + TCOMPLEX128: "complex128", + TBOOL: "bool", + TANY: "any", + TSTRING: "string", + TNIL: "nil", + TIDEAL: "untyped number", + TBLANK: "blank", +} + +func typefmt(t *Type, flag int) string { + if t == nil { + return "" + } + + if t == bytetype || t == runetype { + // in %-T mode collapse rune and byte with their originals. + if fmtmode != FTypeId { + return Sconv(t.Sym, obj.FmtShort) + } + t = Types[t.Etype] + } + + if t == errortype { + return "error" + } + + // Unless the 'l' flag was specified, if the type has a name, just print that name. + if flag&obj.FmtLong == 0 && t.Sym != nil && t.Etype != TFIELD && t != Types[t.Etype] { + switch fmtmode { + case FTypeId: + if flag&obj.FmtShort != 0 { + if t.Vargen != 0 { + return fmt.Sprintf("%v·%d", Sconv(t.Sym, obj.FmtShort), t.Vargen) + } + return Sconv(t.Sym, obj.FmtShort) + } + + if flag&obj.FmtUnsigned != 0 { + return Sconv(t.Sym, obj.FmtUnsigned) + } + fallthrough + + case FExp: + if t.Sym.Pkg == localpkg && t.Vargen != 0 { + return fmt.Sprintf("%v·%d", t.Sym, t.Vargen) + } + } + + return Sconv(t.Sym, 0) + } + + if int(t.Etype) < len(basicnames) && basicnames[t.Etype] != "" { + prefix := "" + if fmtmode == FErr && (t == idealbool || t == idealstring) { + prefix = "untyped " + } + return prefix + basicnames[t.Etype] + } + + if fmtmode == FDbg { + fmtmode = 0 + str := Econv(int(t.Etype), 0) + "-" + typefmt(t, flag) + fmtmode = FDbg + return str + } + + switch t.Etype { + case TPTR32, TPTR64: + if fmtmode == FTypeId && (flag&obj.FmtShort != 0) { + return fmt.Sprintf("*%v", Tconv(t.Type, obj.FmtShort)) + } + return fmt.Sprintf("*%v", t.Type) + + case TARRAY: + if t.Bound >= 0 { + return fmt.Sprintf("[%d]%v", t.Bound, t.Type) + } + if t.Bound == -100 { + return fmt.Sprintf("[...]%v", t.Type) + } + return fmt.Sprintf("[]%v", t.Type) + + case TCHAN: + switch t.Chan { + case Crecv: + return fmt.Sprintf("<-chan %v", t.Type) + + case Csend: + return fmt.Sprintf("chan<- %v", t.Type) + } + + if t.Type != nil && t.Type.Etype == TCHAN && t.Type.Sym == nil && t.Type.Chan == Crecv { + return fmt.Sprintf("chan (%v)", t.Type) + } + return fmt.Sprintf("chan %v", t.Type) + + case TMAP: + return fmt.Sprintf("map[%v]%v", t.Down, t.Type) + + case TINTER: + var buf bytes.Buffer + buf.WriteString("interface {") + for t1 := t.Type; t1 != nil; t1 = t1.Down { + buf.WriteString(" ") + if exportname(t1.Sym.Name) { + buf.WriteString(Sconv(t1.Sym, obj.FmtShort)) + } else { + buf.WriteString(Sconv(t1.Sym, obj.FmtUnsigned)) + } + buf.WriteString(Tconv(t1.Type, obj.FmtShort)) + if t1.Down != nil { + buf.WriteString(";") + } + } + if t.Type != nil { + buf.WriteString(" ") + } + buf.WriteString("}") + return buf.String() + + case TFUNC: + var buf bytes.Buffer + if flag&obj.FmtShort != 0 { + // no leading func + } else { + if t.Thistuple != 0 { + buf.WriteString("method") + buf.WriteString(Tconv(getthisx(t), 0)) + buf.WriteString(" ") + } + buf.WriteString("func") + } + buf.WriteString(Tconv(getinargx(t), 0)) + + switch t.Outtuple { + case 0: + break + + case 1: + if fmtmode != FExp { + buf.WriteString(" ") + buf.WriteString(Tconv(getoutargx(t).Type.Type, 0)) // struct->field->field's type + break + } + fallthrough + + default: + buf.WriteString(" ") + buf.WriteString(Tconv(getoutargx(t), 0)) + } + return buf.String() + + case TSTRUCT: + if t.Map != nil { + // Format the bucket struct for map[x]y as map.bucket[x]y. + // This avoids a recursive print that generates very long names. + if t.Map.Bucket == t { + return fmt.Sprintf("map.bucket[%v]%v", t.Map.Down, t.Map.Type) + } + + if t.Map.Hmap == t { + return fmt.Sprintf("map.hdr[%v]%v", t.Map.Down, t.Map.Type) + } + + if t.Map.Hiter == t { + return fmt.Sprintf("map.iter[%v]%v", t.Map.Down, t.Map.Type) + } + + Yyerror("unknown internal map type") + } + + var buf bytes.Buffer + if t.Funarg != 0 { + buf.WriteString("(") + if fmtmode == FTypeId || fmtmode == FErr { // no argument names on function signature, and no "noescape"/"nosplit" tags + for t1 := t.Type; t1 != nil; t1 = t1.Down { + buf.WriteString(Tconv(t1, obj.FmtShort)) + if t1.Down != nil { + buf.WriteString(", ") + } + } + } else { + for t1 := t.Type; t1 != nil; t1 = t1.Down { + buf.WriteString(Tconv(t1, 0)) + if t1.Down != nil { + buf.WriteString(", ") + } + } + } + buf.WriteString(")") + } else { + buf.WriteString("struct {") + for t1 := t.Type; t1 != nil; t1 = t1.Down { + buf.WriteString(" ") + buf.WriteString(Tconv(t1, obj.FmtLong)) + if t1.Down != nil { + buf.WriteString(";") + } + } + if t.Type != nil { + buf.WriteString(" ") + } + buf.WriteString("}") + } + return buf.String() + + case TFIELD: + var name string + if flag&obj.FmtShort == 0 { + s := t.Sym + + // Take the name from the original, lest we substituted it with ~r%d or ~b%d. + // ~r%d is a (formerly) unnamed result. + if (fmtmode == FErr || fmtmode == FExp) && t.Nname != nil { + if t.Nname.Orig != nil { + s = t.Nname.Orig.Sym + if s != nil && s.Name[0] == '~' { + if s.Name[1] == 'r' { // originally an unnamed result + s = nil + } else if s.Name[1] == 'b' { // originally the blank identifier _ + s = Lookup("_") + } + } + } else { + s = nil + } + } + + if s != nil && t.Embedded == 0 { + if t.Funarg != 0 { + name = Nconv(t.Nname, 0) + } else if flag&obj.FmtLong != 0 { + name = Sconv(s, obj.FmtShort|obj.FmtByte) // qualify non-exported names (used on structs, not on funarg) + } else { + name = Sconv(s, 0) + } + } else if fmtmode == FExp { + // TODO(rsc) this breaks on the eliding of unused arguments in the backend + // when this is fixed, the special case in dcl.c checkarglist can go. + //if(t->funarg) + // fmtstrcpy(fp, "_ "); + //else + if t.Embedded != 0 && s.Pkg != nil && len(s.Pkg.Path) > 0 { + name = fmt.Sprintf("@%q.?", s.Pkg.Path) + } else { + name = "?" + } + } + } + + var typ string + if t.Isddd { + typ = "..." + Tconv(t.Type.Type, 0) + } else { + typ = Tconv(t.Type, 0) + } + + str := typ + if name != "" { + str = name + " " + typ + } + if flag&obj.FmtShort == 0 && t.Note != nil { + str += " " + strconv.Quote(*t.Note) + } + return str + + case TFORW: + if t.Sym != nil { + return fmt.Sprintf("undefined %v", t.Sym) + } + return "undefined" + + case TUNSAFEPTR: + if fmtmode == FExp { + return "@\"unsafe\".Pointer" + } + return "unsafe.Pointer" + } + + if fmtmode == FExp { + Fatal("missing %v case during export", Econv(int(t.Etype), 0)) + } + + // Don't know how to handle - fall back to detailed prints. + return fmt.Sprintf("%v <%v> %v", Econv(int(t.Etype), 0), t.Sym, t.Type) +} + +// Statements which may be rendered with a simplestmt as init. +func stmtwithinit(op int) bool { + switch op { + case OIF, OFOR, OSWITCH: + return true + } + + return false +} + +func stmtfmt(n *Node) string { + var f string + + // some statements allow for an init, but at most one, + // but we may have an arbitrary number added, eg by typecheck + // and inlining. If it doesn't fit the syntax, emit an enclosing + // block starting with the init statements. + + // if we can just say "for" n->ninit; ... then do so + simpleinit := n.Ninit != nil && n.Ninit.Next == nil && n.Ninit.N.Ninit == nil && stmtwithinit(int(n.Op)) + + // otherwise, print the inits as separate statements + complexinit := n.Ninit != nil && !simpleinit && (fmtmode != FErr) + + // but if it was for if/for/switch, put in an extra surrounding block to limit the scope + extrablock := complexinit && stmtwithinit(int(n.Op)) + + if extrablock { + f += "{" + } + + if complexinit { + f += fmt.Sprintf(" %v; ", n.Ninit) + } + + switch n.Op { + case ODCL: + if fmtmode == FExp { + switch n.Left.Class &^ PHEAP { + case PPARAM, PPARAMOUT, PAUTO: + f += fmt.Sprintf("var %v %v", n.Left, n.Left.Type) + goto ret + } + } + + f += fmt.Sprintf("var %v %v", n.Left.Sym, n.Left.Type) + + case ODCLFIELD: + if n.Left != nil { + f += fmt.Sprintf("%v %v", n.Left, n.Right) + } else { + f += Nconv(n.Right, 0) + } + + // Don't export "v = " initializing statements, hope they're always + // preceded by the DCL which will be re-parsed and typecheck to reproduce + // the "v = " again. + case OAS, OASWB: + if fmtmode == FExp && n.Right == nil { + break + } + + if n.Colas && !complexinit { + f += fmt.Sprintf("%v := %v", n.Left, n.Right) + } else { + f += fmt.Sprintf("%v = %v", n.Left, n.Right) + } + + case OASOP: + if n.Implicit { + if n.Etype == OADD { + f += fmt.Sprintf("%v++", n.Left) + } else { + f += fmt.Sprintf("%v--", n.Left) + } + break + } + + f += fmt.Sprintf("%v %v= %v", n.Left, Oconv(int(n.Etype), obj.FmtSharp), n.Right) + + case OAS2: + if n.Colas && !complexinit { + f += fmt.Sprintf("%v := %v", Hconv(n.List, obj.FmtComma), Hconv(n.Rlist, obj.FmtComma)) + break + } + fallthrough + + case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV: + f += fmt.Sprintf("%v = %v", Hconv(n.List, obj.FmtComma), Hconv(n.Rlist, obj.FmtComma)) + + case ORETURN: + f += fmt.Sprintf("return %v", Hconv(n.List, obj.FmtComma)) + + case ORETJMP: + f += fmt.Sprintf("retjmp %v", n.Sym) + + case OPROC: + f += fmt.Sprintf("go %v", n.Left) + + case ODEFER: + f += fmt.Sprintf("defer %v", n.Left) + + case OIF: + if simpleinit { + f += fmt.Sprintf("if %v; %v { %v }", n.Ninit.N, n.Ntest, n.Nbody) + } else { + f += fmt.Sprintf("if %v { %v }", n.Ntest, n.Nbody) + } + if n.Nelse != nil { + f += fmt.Sprintf(" else { %v }", n.Nelse) + } + + case OFOR: + if fmtmode == FErr { // TODO maybe only if FmtShort, same below + f += "for loop" + break + } + + f += "for" + if simpleinit { + f += fmt.Sprintf(" %v;", n.Ninit.N) + } else if n.Nincr != nil { + f += " ;" + } + + if n.Ntest != nil { + f += fmt.Sprintf(" %v", n.Ntest) + } + + if n.Nincr != nil { + f += fmt.Sprintf("; %v", n.Nincr) + } else if simpleinit { + f += ";" + } + + f += fmt.Sprintf(" { %v }", n.Nbody) + + case ORANGE: + if fmtmode == FErr { + f += "for loop" + break + } + + if n.List == nil { + f += fmt.Sprintf("for range %v { %v }", n.Right, n.Nbody) + break + } + + f += fmt.Sprintf("for %v = range %v { %v }", Hconv(n.List, obj.FmtComma), n.Right, n.Nbody) + + case OSELECT, OSWITCH: + if fmtmode == FErr { + f += fmt.Sprintf("%v statement", Oconv(int(n.Op), 0)) + break + } + + f += Oconv(int(n.Op), obj.FmtSharp) + if simpleinit { + f += fmt.Sprintf(" %v;", n.Ninit.N) + } + if n.Ntest != nil { + f += Nconv(n.Ntest, 0) + } + + f += fmt.Sprintf(" { %v }", n.List) + + case OCASE, OXCASE: + if n.List != nil { + f += fmt.Sprintf("case %v: %v", Hconv(n.List, obj.FmtComma), n.Nbody) + } else { + f += fmt.Sprintf("default: %v", n.Nbody) + } + + case OBREAK, + OCONTINUE, + OGOTO, + OFALL, + OXFALL: + if n.Left != nil { + f += fmt.Sprintf("%v %v", Oconv(int(n.Op), obj.FmtSharp), n.Left) + } else { + f += Oconv(int(n.Op), obj.FmtSharp) + } + + case OEMPTY: + break + + case OLABEL: + f += fmt.Sprintf("%v: ", n.Left) + } + +ret: + if extrablock { + f += "}" + } + + return f +} + +var opprec = []int{ + OAPPEND: 8, + OARRAYBYTESTR: 8, + OARRAYLIT: 8, + OARRAYRUNESTR: 8, + OCALLFUNC: 8, + OCALLINTER: 8, + OCALLMETH: 8, + OCALL: 8, + OCAP: 8, + OCLOSE: 8, + OCONVIFACE: 8, + OCONVNOP: 8, + OCONV: 8, + OCOPY: 8, + ODELETE: 8, + OGETG: 8, + OLEN: 8, + OLITERAL: 8, + OMAKESLICE: 8, + OMAKE: 8, + OMAPLIT: 8, + ONAME: 8, + ONEW: 8, + ONONAME: 8, + OPACK: 8, + OPANIC: 8, + OPAREN: 8, + OPRINTN: 8, + OPRINT: 8, + ORUNESTR: 8, + OSTRARRAYBYTE: 8, + OSTRARRAYRUNE: 8, + OSTRUCTLIT: 8, + OTARRAY: 8, + OTCHAN: 8, + OTFUNC: 8, + OTINTER: 8, + OTMAP: 8, + OTSTRUCT: 8, + OINDEXMAP: 8, + OINDEX: 8, + OSLICE: 8, + OSLICESTR: 8, + OSLICEARR: 8, + OSLICE3: 8, + OSLICE3ARR: 8, + ODOTINTER: 8, + ODOTMETH: 8, + ODOTPTR: 8, + ODOTTYPE2: 8, + ODOTTYPE: 8, + ODOT: 8, + OXDOT: 8, + OCALLPART: 8, + OPLUS: 7, + ONOT: 7, + OCOM: 7, + OMINUS: 7, + OADDR: 7, + OIND: 7, + ORECV: 7, + OMUL: 6, + ODIV: 6, + OMOD: 6, + OLSH: 6, + ORSH: 6, + OAND: 6, + OANDNOT: 6, + OADD: 5, + OSUB: 5, + OOR: 5, + OXOR: 5, + OEQ: 4, + OLT: 4, + OLE: 4, + OGE: 4, + OGT: 4, + ONE: 4, + OCMPSTR: 4, + OCMPIFACE: 4, + OSEND: 3, + OANDAND: 2, + OOROR: 1, + // Statements handled by stmtfmt + OAS: -1, + OAS2: -1, + OAS2DOTTYPE: -1, + OAS2FUNC: -1, + OAS2MAPR: -1, + OAS2RECV: -1, + OASOP: -1, + OBREAK: -1, + OCASE: -1, + OCONTINUE: -1, + ODCL: -1, + ODCLFIELD: -1, + ODEFER: -1, + OEMPTY: -1, + OFALL: -1, + OFOR: -1, + OGOTO: -1, + OIF: -1, + OLABEL: -1, + OPROC: -1, + ORANGE: -1, + ORETURN: -1, + OSELECT: -1, + OSWITCH: -1, + OXCASE: -1, + OXFALL: -1, + OEND: 0, +} + +func exprfmt(n *Node, prec int) string { + for n != nil && n.Implicit && (n.Op == OIND || n.Op == OADDR) { + n = n.Left + } + + if n == nil { + return "" + } + + nprec := opprec[n.Op] + if n.Op == OTYPE && n.Sym != nil { + nprec = 8 + } + + if prec > nprec { + return fmt.Sprintf("(%v)", n) + } + + switch n.Op { + case OPAREN: + return fmt.Sprintf("(%v)", n.Left) + + case ODDDARG: + return "... argument" + + case OREGISTER: + return obj.Rconv(int(n.Reg)) + + case OLITERAL: // this is a bit of a mess + if fmtmode == FErr { + if n.Orig != nil && n.Orig != n { + return exprfmt(n.Orig, prec) + } + if n.Sym != nil { + return Sconv(n.Sym, 0) + } + } + if n.Val.Ctype == CTNIL && n.Orig != nil && n.Orig != n { + return exprfmt(n.Orig, prec) + } + if n.Type != nil && n.Type != Types[n.Type.Etype] && n.Type != idealbool && n.Type != idealstring { + // Need parens when type begins with what might + // be misinterpreted as a unary operator: * or <-. + if Isptr[n.Type.Etype] || (n.Type.Etype == TCHAN && n.Type.Chan == Crecv) { + return fmt.Sprintf("(%v)(%v)", n.Type, Vconv(&n.Val, 0)) + } else { + return fmt.Sprintf("%v(%v)", n.Type, Vconv(&n.Val, 0)) + } + } + + return Vconv(&n.Val, 0) + + // Special case: name used as local variable in export. + // _ becomes ~b%d internally; print as _ for export + case ONAME: + 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 { + return fmt.Sprintf("%v·%d", n.Sym, n.Vargen) + } + + // Special case: explicit name of func (*T) method(...) is turned into pkg.(*T).method, + // but for export, this should be rendered as (*pkg.T).meth. + // These nodes have the special property that they are names with a left OTYPE and a right ONAME. + if fmtmode == FExp && n.Left != nil && n.Left.Op == OTYPE && n.Right != nil && n.Right.Op == ONAME { + if Isptr[n.Left.Type.Etype] { + return fmt.Sprintf("(%v).%v", n.Left.Type, Sconv(n.Right.Sym, obj.FmtShort|obj.FmtByte)) + } else { + return fmt.Sprintf("%v.%v", n.Left.Type, Sconv(n.Right.Sym, obj.FmtShort|obj.FmtByte)) + } + } + fallthrough + + //fallthrough + case OPACK, ONONAME: + return Sconv(n.Sym, 0) + + case OTYPE: + if n.Type == nil && n.Sym != nil { + return Sconv(n.Sym, 0) + } + return Tconv(n.Type, 0) + + case OTARRAY: + if n.Left != nil { + return fmt.Sprintf("[]%v", n.Left) + } + var f string + f += fmt.Sprintf("[]%v", n.Right) + return f // happens before typecheck + + case OTMAP: + return fmt.Sprintf("map[%v]%v", n.Left, n.Right) + + case OTCHAN: + switch n.Etype { + case Crecv: + return fmt.Sprintf("<-chan %v", n.Left) + + case Csend: + return fmt.Sprintf("chan<- %v", n.Left) + + default: + if n.Left != nil && n.Left.Op == OTCHAN && n.Left.Sym == nil && n.Left.Etype == Crecv { + return fmt.Sprintf("chan (%v)", n.Left) + } else { + return fmt.Sprintf("chan %v", n.Left) + } + } + + case OTSTRUCT: + return "" + + case OTINTER: + return "" + + case OTFUNC: + return "" + + case OCLOSURE: + if fmtmode == FErr { + return "func literal" + } + if n.Nbody != nil { + return fmt.Sprintf("%v { %v }", n.Type, n.Nbody) + } + return fmt.Sprintf("%v { %v }", n.Type, n.Closure.Nbody) + + case OCOMPLIT: + ptrlit := n.Right != nil && n.Right.Implicit && n.Right.Type != nil && Isptr[n.Right.Type.Etype] + if fmtmode == FErr { + if n.Right != nil && n.Right.Type != nil && !n.Implicit { + if ptrlit { + return fmt.Sprintf("&%v literal", n.Right.Type.Type) + } else { + return fmt.Sprintf("%v literal", n.Right.Type) + } + } + + return "composite literal" + } + + if fmtmode == FExp && ptrlit { + // typecheck has overwritten OIND by OTYPE with pointer type. + return fmt.Sprintf("(&%v{ %v })", n.Right.Type.Type, Hconv(n.List, obj.FmtComma)) + } + + return fmt.Sprintf("(%v{ %v })", n.Right, Hconv(n.List, obj.FmtComma)) + + case OPTRLIT: + if fmtmode == FExp && n.Left.Implicit { + return Nconv(n.Left, 0) + } + return fmt.Sprintf("&%v", n.Left) + + case OSTRUCTLIT: + if fmtmode == FExp { // requires special handling of field names + var f string + if n.Implicit { + f += "{" + } else { + f += fmt.Sprintf("(%v{", n.Type) + } + for l := n.List; l != nil; l = l.Next { + f += fmt.Sprintf(" %v:%v", Sconv(l.N.Left.Sym, obj.FmtShort|obj.FmtByte), l.N.Right) + + if l.Next != nil { + f += "," + } else { + f += " " + } + } + + if !n.Implicit { + f += "})" + return f + } + f += "}" + return f + } + fallthrough + + case OARRAYLIT, OMAPLIT: + if fmtmode == FErr { + return fmt.Sprintf("%v literal", n.Type) + } + if fmtmode == FExp && n.Implicit { + return fmt.Sprintf("{ %v }", Hconv(n.List, obj.FmtComma)) + } + return fmt.Sprintf("(%v{ %v })", n.Type, Hconv(n.List, obj.FmtComma)) + + case OKEY: + if n.Left != nil && n.Right != nil { + if fmtmode == FExp && n.Left.Type != nil && n.Left.Type.Etype == TFIELD { + // requires special handling of field names + return fmt.Sprintf("%v:%v", Sconv(n.Left.Sym, obj.FmtShort|obj.FmtByte), n.Right) + } else { + return fmt.Sprintf("%v:%v", n.Left, n.Right) + } + } + + if n.Left == nil && n.Right != nil { + return fmt.Sprintf(":%v", n.Right) + } + if n.Left != nil && n.Right == nil { + return fmt.Sprintf("%v:", n.Left) + } + return ":" + + case OXDOT, + ODOT, + ODOTPTR, + ODOTINTER, + ODOTMETH, + OCALLPART: + var f string + f += exprfmt(n.Left, nprec) + if n.Right == nil || n.Right.Sym == nil { + f += "." + return f + } + f += fmt.Sprintf(".%v", Sconv(n.Right.Sym, obj.FmtShort|obj.FmtByte)) + return f + + case ODOTTYPE, ODOTTYPE2: + var f string + f += exprfmt(n.Left, nprec) + if n.Right != nil { + f += fmt.Sprintf(".(%v)", n.Right) + return f + } + f += fmt.Sprintf(".(%v)", n.Type) + return f + + case OINDEX, + OINDEXMAP, + OSLICE, + OSLICESTR, + OSLICEARR, + OSLICE3, + OSLICE3ARR: + var f string + f += exprfmt(n.Left, nprec) + f += fmt.Sprintf("[%v]", n.Right) + return f + + case OCOPY, OCOMPLEX: + return fmt.Sprintf("%v(%v, %v)", Oconv(int(n.Op), obj.FmtSharp), n.Left, n.Right) + + case OCONV, + OCONVIFACE, + OCONVNOP, + OARRAYBYTESTR, + OARRAYRUNESTR, + OSTRARRAYBYTE, + OSTRARRAYRUNE, + ORUNESTR: + if n.Type == nil || n.Type.Sym == nil { + return fmt.Sprintf("(%v)(%v)", n.Type, n.Left) + } + if n.Left != nil { + return fmt.Sprintf("%v(%v)", n.Type, n.Left) + } + return fmt.Sprintf("%v(%v)", n.Type, Hconv(n.List, obj.FmtComma)) + + case OREAL, + OIMAG, + OAPPEND, + OCAP, + OCLOSE, + ODELETE, + OLEN, + OMAKE, + ONEW, + OPANIC, + ORECOVER, + OPRINT, + OPRINTN: + if n.Left != nil { + return fmt.Sprintf("%v(%v)", Oconv(int(n.Op), obj.FmtSharp), n.Left) + } + if n.Isddd { + return fmt.Sprintf("%v(%v...)", Oconv(int(n.Op), obj.FmtSharp), Hconv(n.List, obj.FmtComma)) + } + return fmt.Sprintf("%v(%v)", Oconv(int(n.Op), obj.FmtSharp), Hconv(n.List, obj.FmtComma)) + + case OCALL, OCALLFUNC, OCALLINTER, OCALLMETH, OGETG: + var f string + f += exprfmt(n.Left, nprec) + if n.Isddd { + f += fmt.Sprintf("(%v...)", Hconv(n.List, obj.FmtComma)) + return f + } + f += fmt.Sprintf("(%v)", Hconv(n.List, obj.FmtComma)) + return f + + case OMAKEMAP, OMAKECHAN, OMAKESLICE: + if n.List != nil { // pre-typecheck + return fmt.Sprintf("make(%v, %v)", n.Type, Hconv(n.List, obj.FmtComma)) + } + if n.Right != nil { + return fmt.Sprintf("make(%v, %v, %v)", n.Type, n.Left, n.Right) + } + if n.Left != nil && (n.Op == OMAKESLICE || !isideal(n.Left.Type)) { + return fmt.Sprintf("make(%v, %v)", n.Type, n.Left) + } + return fmt.Sprintf("make(%v)", n.Type) + + // Unary + case OPLUS, + OMINUS, + OADDR, + OCOM, + OIND, + ONOT, + ORECV: + var f string + if n.Left.Op == n.Op { + f += fmt.Sprintf("%v ", Oconv(int(n.Op), obj.FmtSharp)) + } else { + f += Oconv(int(n.Op), obj.FmtSharp) + } + f += exprfmt(n.Left, nprec+1) + return f + + // Binary + case OADD, + OAND, + OANDAND, + OANDNOT, + ODIV, + OEQ, + OGE, + OGT, + OLE, + OLT, + OLSH, + OMOD, + OMUL, + ONE, + OOR, + OOROR, + ORSH, + OSEND, + OSUB, + OXOR: + var f string + f += exprfmt(n.Left, nprec) + + f += fmt.Sprintf(" %v ", Oconv(int(n.Op), obj.FmtSharp)) + f += exprfmt(n.Right, nprec+1) + return f + + case OADDSTR: + var f string + for l := n.List; l != nil; l = l.Next { + if l != n.List { + f += " + " + } + f += exprfmt(l.N, nprec) + } + + return f + + case OCMPSTR, OCMPIFACE: + var f string + f += exprfmt(n.Left, nprec) + f += fmt.Sprintf(" %v ", Oconv(int(n.Etype), obj.FmtSharp)) + f += exprfmt(n.Right, nprec+1) + return f + } + + return fmt.Sprintf("", Oconv(int(n.Op), 0)) +} + +func nodefmt(n *Node, flag int) string { + t := n.Type + + // we almost always want the original, except in export mode for literals + // this saves the importer some work, and avoids us having to redo some + // special casing for package unsafe + if (fmtmode != FExp || n.Op != OLITERAL) && n.Orig != nil { + n = n.Orig + } + + if flag&obj.FmtLong != 0 && t != nil { + if t.Etype == TNIL { + return "nil" + } else { + return fmt.Sprintf("%v (type %v)", n, t) + } + } + + // TODO inlining produces expressions with ninits. we can't print these yet. + + if opprec[n.Op] < 0 { + return stmtfmt(n) + } + + return exprfmt(n, 0) +} + +var dumpdepth int + +func indent(buf *bytes.Buffer) { + buf.WriteString("\n") + for i := 0; i < dumpdepth; i++ { + buf.WriteString(". ") + } +} + +func nodedump(n *Node, flag int) string { + if n == nil { + return "" + } + + recur := flag&obj.FmtShort == 0 + + var buf bytes.Buffer + if recur { + indent(&buf) + if dumpdepth > 10 { + buf.WriteString("...") + return buf.String() + } + + if n.Ninit != nil { + fmt.Fprintf(&buf, "%v-init%v", Oconv(int(n.Op), 0), n.Ninit) + indent(&buf) + } + } + + switch n.Op { + default: + fmt.Fprintf(&buf, "%v%v", Oconv(int(n.Op), 0), Jconv(n, 0)) + + case OREGISTER, OINDREG: + fmt.Fprintf(&buf, "%v-%v%v", Oconv(int(n.Op), 0), obj.Rconv(int(n.Reg)), Jconv(n, 0)) + + case OLITERAL: + fmt.Fprintf(&buf, "%v-%v%v", Oconv(int(n.Op), 0), Vconv(&n.Val, 0), Jconv(n, 0)) + + case ONAME, ONONAME: + if n.Sym != nil { + fmt.Fprintf(&buf, "%v-%v%v", Oconv(int(n.Op), 0), n.Sym, Jconv(n, 0)) + } else { + fmt.Fprintf(&buf, "%v%v", Oconv(int(n.Op), 0), Jconv(n, 0)) + } + if recur && n.Type == nil && n.Ntype != nil { + indent(&buf) + fmt.Fprintf(&buf, "%v-ntype%v", Oconv(int(n.Op), 0), n.Ntype) + } + + case OASOP: + fmt.Fprintf(&buf, "%v-%v%v", Oconv(int(n.Op), 0), Oconv(int(n.Etype), 0), Jconv(n, 0)) + + 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 { + indent(&buf) + fmt.Fprintf(&buf, "%v-ntype%v", Oconv(int(n.Op), 0), n.Ntype) + } + } + + if n.Sym != nil && n.Op != ONAME { + fmt.Fprintf(&buf, " %v G%d", n.Sym, n.Vargen) + } + + if n.Type != nil { + fmt.Fprintf(&buf, " %v", n.Type) + } + + if recur { + if n.Left != nil { + buf.WriteString(Nconv(n.Left, 0)) + } + if n.Right != nil { + buf.WriteString(Nconv(n.Right, 0)) + } + if n.List != nil { + indent(&buf) + fmt.Fprintf(&buf, "%v-list%v", Oconv(int(n.Op), 0), n.List) + } + + if n.Rlist != nil { + indent(&buf) + fmt.Fprintf(&buf, "%v-rlist%v", Oconv(int(n.Op), 0), n.Rlist) + } + + if n.Ntest != nil { + indent(&buf) + fmt.Fprintf(&buf, "%v-test%v", Oconv(int(n.Op), 0), n.Ntest) + } + + if n.Nbody != nil { + indent(&buf) + fmt.Fprintf(&buf, "%v-body%v", Oconv(int(n.Op), 0), n.Nbody) + } + + if n.Nelse != nil { + indent(&buf) + fmt.Fprintf(&buf, "%v-else%v", Oconv(int(n.Op), 0), n.Nelse) + } + + if n.Nincr != nil { + indent(&buf) + fmt.Fprintf(&buf, "%v-incr%v", Oconv(int(n.Op), 0), n.Nincr) + } + } + + return buf.String() +} + +func (s *Sym) String() string { + return Sconv(s, 0) +} + +// Fmt "%S": syms +// Flags: "%hS" suppresses qualifying with package +func Sconv(s *Sym, flag int) string { + if flag&obj.FmtLong != 0 { + panic("linksymfmt") + } + + if s == nil { + return "" + } + + if s.Name == "_" { + return "_" + } + + sf := flag + sm := setfmode(&flag) + var r int + _ = r + str := symfmt(s, flag) + flag = sf + fmtmode = sm + return str +} + +func (t *Type) String() string { + return Tconv(t, 0) +} + +// Fmt "%T": types. +// Flags: 'l' print definition, not name +// 'h' omit 'func' and receiver from function types, short type names +// 'u' package name, not prefix (FTypeId mode, sticky) +func Tconv(t *Type, flag int) string { + if t == nil { + return "" + } + + if t.Trecur > 4 { + return "<...>" + } + + t.Trecur++ + sf := flag + sm := setfmode(&flag) + + if fmtmode == FTypeId && (sf&obj.FmtUnsigned != 0) { + fmtpkgpfx++ + } + if fmtpkgpfx != 0 { + flag |= obj.FmtUnsigned + } + + var r int + _ = r + str := typefmt(t, flag) + + if fmtmode == FTypeId && (sf&obj.FmtUnsigned != 0) { + fmtpkgpfx-- + } + + flag = sf + fmtmode = sm + t.Trecur-- + return str +} + +func (n *Node) String() string { + return Nconv(n, 0) +} + +// Fmt '%N': Nodes. +// Flags: 'l' suffix with "(type %T)" where possible +// '+h' in debug mode, don't recurse, no multiline output +func Nconv(n *Node, flag int) string { + if n == nil { + return "" + } + sf := flag + sm := setfmode(&flag) + + var r int + _ = r + var str string + switch fmtmode { + case FErr, FExp: + str = nodefmt(n, flag) + + case FDbg: + dumpdepth++ + str = nodedump(n, flag) + dumpdepth-- + + default: + Fatal("unhandled %%N mode") + } + + flag = sf + fmtmode = sm + return str +} + +func (l *NodeList) String() string { + return Hconv(l, 0) +} + +// Fmt '%H': NodeList. +// Flags: all those of %N plus ',': separate with comma's instead of semicolons. +func Hconv(l *NodeList, flag int) string { + if l == nil && fmtmode == FDbg { + return "" + } + + sf := flag + sm := setfmode(&flag) + var r int + _ = r + sep := "; " + if fmtmode == FDbg { + sep = "\n" + } else if flag&obj.FmtComma != 0 { + sep = ", " + } + + var buf bytes.Buffer + for ; l != nil; l = l.Next { + buf.WriteString(Nconv(l.N, 0)) + if l.Next != nil { + buf.WriteString(sep) + } + } + + flag = sf + fmtmode = sm + return buf.String() +} + +func dumplist(s string, l *NodeList) { + fmt.Printf("%s%v\n", s, Hconv(l, obj.FmtSign)) +} + +func Dump(s string, n *Node) { + fmt.Printf("%s [%p]%v\n", s, n, Nconv(n, obj.FmtSign)) +} diff --git a/src/cmd/compile/internal/gc/gen.go b/src/cmd/compile/internal/gc/gen.go new file mode 100644 index 0000000000..d3c6387a4e --- /dev/null +++ b/src/cmd/compile/internal/gc/gen.go @@ -0,0 +1,1279 @@ +// Copyright 2009 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 gc + +import ( + "cmd/internal/obj" + "fmt" +) + +/* + * portable half of code generator. + * mainly statements and control flow. + */ +var labellist *Label + +var lastlabel *Label + +func Sysfunc(name string) *Node { + n := newname(Pkglookup(name, Runtimepkg)) + n.Class = PFUNC + return n +} + +// addrescapes tags node n as having had its address taken +// by "increasing" the "value" of n.Esc to EscHeap. +// Storage is allocated as necessary to allow the address +// to be taken. +func addrescapes(n *Node) { + switch n.Op { + // probably a type error already. + // dump("addrescapes", n); + default: + break + + case ONAME: + if n == nodfp { + break + } + + // if this is a tmpname (PAUTO), it was tagged by tmpname as not escaping. + // on PPARAM it means something different. + if n.Class == PAUTO && n.Esc == EscNever { + break + } + + switch n.Class { + case PPARAMREF: + addrescapes(n.Defn) + + // if func param, need separate temporary + // to hold heap pointer. + // the function type has already been checked + // (we're in the function body) + // so the param already has a valid xoffset. + + // expression to refer to stack copy + case PPARAM, PPARAMOUT: + n.Stackparam = Nod(OPARAM, n, nil) + + n.Stackparam.Type = n.Type + n.Stackparam.Addable = true + if n.Xoffset == BADWIDTH { + Fatal("addrescapes before param assignment") + } + n.Stackparam.Xoffset = n.Xoffset + fallthrough + + case PAUTO: + n.Class |= PHEAP + + n.Addable = false + n.Ullman = 2 + n.Xoffset = 0 + + // create stack variable to hold pointer to heap + oldfn := Curfn + + Curfn = n.Curfn + n.Name.Heapaddr = temp(Ptrto(n.Type)) + buf := fmt.Sprintf("&%v", n.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) + } + Curfn = oldfn + } + + case OIND, ODOTPTR: + break + + // ODOTPTR has already been introduced, + // so these are the non-pointer ODOT and OINDEX. + // In &x[0], if x is a slice, then x does not + // escape--the pointer inside x does, but that + // is always a heap pointer anyway. + case ODOT, OINDEX, OPAREN, OCONVNOP: + if !Isslice(n.Left.Type) { + addrescapes(n.Left) + } + } +} + +func clearlabels() { + for l := labellist; l != nil; l = l.Link { + l.Sym.Label = nil + } + + labellist = nil + lastlabel = nil +} + +func newlab(n *Node) *Label { + s := n.Left.Sym + lab := s.Label + if lab == nil { + lab = new(Label) + if lastlabel == nil { + labellist = lab + } else { + lastlabel.Link = lab + } + lastlabel = lab + lab.Sym = s + s.Label = lab + } + + if n.Op == OLABEL { + if lab.Def != nil { + Yyerror("label %v already defined at %v", s, lab.Def.Line()) + } else { + lab.Def = n + } + } else { + lab.Use = list(lab.Use, n) + } + + return lab +} + +func checkgoto(from *Node, to *Node) { + if from.Sym == to.Sym { + return + } + + nf := 0 + for fs := from.Sym; fs != nil; fs = fs.Link { + nf++ + } + nt := 0 + for fs := to.Sym; fs != nil; fs = fs.Link { + nt++ + } + fs := from.Sym + for ; nf > nt; nf-- { + fs = fs.Link + } + if fs != to.Sym { + lno := int(lineno) + setlineno(from) + + // decide what to complain about. + // prefer to complain about 'into block' over declarations, + // so scan backward to find most recent block or else dcl. + var block *Sym + + var dcl *Sym + ts := to.Sym + for ; nt > nf; nt-- { + if ts.Pkg == nil { + block = ts + } else { + dcl = ts + } + ts = ts.Link + } + + for ts != fs { + if ts.Pkg == nil { + block = ts + } else { + dcl = ts + } + ts = ts.Link + fs = fs.Link + } + + if block != nil { + Yyerror("goto %v jumps into block starting at %v", from.Left.Sym, Ctxt.Line(int(block.Lastlineno))) + } else { + Yyerror("goto %v jumps over declaration of %v at %v", from.Left.Sym, dcl, Ctxt.Line(int(dcl.Lastlineno))) + } + lineno = int32(lno) + } +} + +func stmtlabel(n *Node) *Label { + if n.Sym != nil { + lab := n.Sym.Label + if lab != nil { + if lab.Def != nil { + if lab.Def.Defn == n { + return lab + } + } + } + } + return nil +} + +/* + * compile statements + */ +func Genlist(l *NodeList) { + for ; l != nil; l = l.Next { + gen(l.N) + } +} + +/* + * generate code to start new proc running call n. + */ +func cgen_proc(n *Node, proc int) { + switch n.Left.Op { + default: + Fatal("cgen_proc: unknown call %v", Oconv(int(n.Left.Op), 0)) + + case OCALLMETH: + cgen_callmeth(n.Left, proc) + + case OCALLINTER: + cgen_callinter(n.Left, nil, proc) + + case OCALLFUNC: + cgen_call(n.Left, proc) + } +} + +/* + * generate declaration. + * have to allocate heap copy + * for escaped variables. + */ +func cgen_dcl(n *Node) { + if Debug['g'] != 0 { + Dump("\ncgen-dcl", n) + } + if n.Op != ONAME { + Dump("cgen_dcl", n) + Fatal("cgen_dcl") + } + + if n.Class&PHEAP == 0 { + return + } + if compiling_runtime != 0 { + Yyerror("%v escapes to heap, not allowed in runtime.", n) + } + if n.Alloc == nil { + n.Alloc = callnew(n.Type) + } + Cgen_as(n.Name.Heapaddr, n.Alloc) +} + +/* + * generate discard of value + */ +func cgen_discard(nr *Node) { + if nr == nil { + return + } + + switch nr.Op { + case ONAME: + if nr.Class&PHEAP == 0 && nr.Class != PEXTERN && nr.Class != PFUNC && nr.Class != PPARAMREF { + gused(nr) + } + + // unary + case OADD, + OAND, + ODIV, + OEQ, + OGE, + OGT, + OLE, + OLSH, + OLT, + OMOD, + OMUL, + ONE, + OOR, + ORSH, + OSUB, + OXOR: + cgen_discard(nr.Left) + + cgen_discard(nr.Right) + + // binary + case OCAP, + OCOM, + OLEN, + OMINUS, + ONOT, + OPLUS: + cgen_discard(nr.Left) + + case OIND: + Cgen_checknil(nr.Left) + + // special enough to just evaluate + default: + var tmp Node + Tempname(&tmp, nr.Type) + + Cgen_as(&tmp, nr) + gused(&tmp) + } +} + +/* + * clearslim generates code to zero a slim node. + */ +func Clearslim(n *Node) { + var z Node + z.Op = OLITERAL + z.Type = n.Type + z.Addable = true + + switch Simtype[n.Type.Etype] { + case TCOMPLEX64, TCOMPLEX128: + 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 = &zero + + case TPTR32, TPTR64, TCHAN, TMAP: + z.Val.Ctype = CTNIL + + case TBOOL: + z.Val.Ctype = CTBOOL + z.Val.U = false + + case TINT8, + TINT16, + TINT32, + TINT64, + TUINT8, + TUINT16, + TUINT32, + TUINT64: + z.Val.Ctype = CTINT + z.Val.U = new(Mpint) + Mpmovecfix(z.Val.U.(*Mpint), 0) + + default: + Fatal("clearslim called on type %v", n.Type) + } + + ullmancalc(&z) + Cgen(&z, n) +} + +/* + * generate: + * res = iface{typ, data} + * n->left is typ + * n->right is data + */ +func Cgen_eface(n *Node, res *Node) { + /* + * the right node of an eface may contain function calls that uses res as an argument, + * so it's important that it is done first + */ + + tmp := temp(Types[Tptr]) + Cgen(n.Right, tmp) + + Gvardef(res) + + dst := *res + dst.Type = Types[Tptr] + dst.Xoffset += int64(Widthptr) + Cgen(tmp, &dst) + + dst.Xoffset -= int64(Widthptr) + Cgen(n.Left, &dst) +} + +/* + * generate one of: + * res, resok = x.(T) + * res = x.(T) (when resok == nil) + * n.Left is x + * n.Type is T + */ +func cgen_dottype(n *Node, res, resok *Node, wb bool) { + if Debug_typeassert > 0 { + Warn("type assertion inlined") + } + // iface := n.Left + // r1 := iword(iface) + // if n.Left is non-empty interface { + // r1 = *r1 + // } + // if r1 == T { + // res = idata(iface) + // resok = true + // } else { + // assert[EI]2T(x, T, nil) // (when resok == nil; does not return) + // resok = false // (when resok != nil) + // } + // + var iface Node + Igen(n.Left, &iface, res) + var r1, r2 Node + byteptr := Ptrto(Types[TUINT8]) // type used in runtime prototypes for runtime type (*byte) + Regalloc(&r1, byteptr, nil) + iface.Type = byteptr + Cgen(&iface, &r1) + if !isnilinter(n.Left.Type) { + // Holding itab, want concrete type in second word. + p := Thearch.Ginscmp(OEQ, byteptr, &r1, Nodintconst(0), -1) + r2 = r1 + r2.Op = OINDREG + r2.Xoffset = int64(Widthptr) + Cgen(&r2, &r1) + Patch(p, Pc) + } + Regalloc(&r2, byteptr, nil) + Cgen(typename(n.Type), &r2) + 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) + Regfree(&iface) + + if resok == nil { + r1.Type = res.Type + cgen_wb(&r1, res, wb) + q := Gbranch(obj.AJMP, nil, 0) + Patch(p, Pc) + Regrealloc(&r2) // reclaim from above, for this failure path + fn := syslook("panicdottype", 0) + dowidth(fn.Type) + call := Nod(OCALLFUNC, fn, nil) + r1.Type = byteptr + r2.Type = byteptr + call.List = list(list(list1(&r1), &r2), typename(n.Left.Type)) + call.List = ascompatte(OCALLFUNC, call, false, getinarg(fn.Type), call.List, 0, nil) + gen(call) + Regfree(&r1) + Regfree(&r2) + Thearch.Gins(obj.AUNDEF, nil, nil) + Patch(q, Pc) + } else { + // This half is handling the res, resok = x.(T) case, + // which is called from gen, not cgen, and is consequently fussier + // about blank assignments. We have to avoid calling cgen for those. + r1.Type = res.Type + if !isblank(res) { + cgen_wb(&r1, res, wb) + } + Regfree(&r1) + if !isblank(resok) { + Cgen(Nodbool(true), resok) + } + q := Gbranch(obj.AJMP, nil, 0) + Patch(p, Pc) + if !isblank(res) { + n := nodnil() + n.Type = res.Type + Cgen(n, res) + } + if !isblank(resok) { + Cgen(Nodbool(false), resok) + } + Patch(q, Pc) + } +} + +/* + * generate: + * res, resok = x.(T) + * n.Left is x + * n.Type is T + */ +func Cgen_As2dottype(n, res, resok *Node) { + if Debug_typeassert > 0 { + Warn("type assertion inlined") + } + // iface := n.Left + // r1 := iword(iface) + // if n.Left is non-empty interface { + // r1 = *r1 + // } + // if r1 == T { + // res = idata(iface) + // resok = true + // } else { + // res = nil + // resok = false + // } + // + var iface Node + Igen(n.Left, &iface, nil) + var r1, r2 Node + byteptr := Ptrto(Types[TUINT8]) // type used in runtime prototypes for runtime type (*byte) + Regalloc(&r1, byteptr, res) + iface.Type = byteptr + Cgen(&iface, &r1) + if !isnilinter(n.Left.Type) { + // Holding itab, want concrete type in second word. + p := Thearch.Ginscmp(OEQ, byteptr, &r1, Nodintconst(0), -1) + r2 = r1 + r2.Op = OINDREG + r2.Xoffset = int64(Widthptr) + Cgen(&r2, &r1) + Patch(p, Pc) + } + Regalloc(&r2, byteptr, nil) + Cgen(typename(n.Type), &r2) + p := Thearch.Ginscmp(ONE, byteptr, &r1, &r2, -1) + iface.Type = n.Type + iface.Xoffset += int64(Widthptr) + Cgen(&iface, &r1) + if iface.Op != 0 { + Regfree(&iface) + } + Cgen(&r1, res) + q := Gbranch(obj.AJMP, nil, 0) + Patch(p, Pc) + + fn := syslook("panicdottype", 0) + dowidth(fn.Type) + call := Nod(OCALLFUNC, fn, nil) + call.List = list(list(list1(&r1), &r2), typename(n.Left.Type)) + call.List = ascompatte(OCALLFUNC, call, false, getinarg(fn.Type), call.List, 0, nil) + gen(call) + Regfree(&r1) + Regfree(&r2) + Thearch.Gins(obj.AUNDEF, nil, nil) + Patch(q, Pc) +} + +/* + * gather series of offsets + * >=0 is direct addressed field + * <0 is pointer to next field (+1) + */ +func Dotoffset(n *Node, oary []int64, nn **Node) int { + var i int + + switch n.Op { + case ODOT: + if n.Xoffset == BADWIDTH { + Dump("bad width in dotoffset", n) + Fatal("bad width in dotoffset") + } + + i = Dotoffset(n.Left, oary, nn) + if i > 0 { + if oary[i-1] >= 0 { + oary[i-1] += n.Xoffset + } else { + oary[i-1] -= n.Xoffset + } + break + } + + if i < 10 { + oary[i] = n.Xoffset + i++ + } + + case ODOTPTR: + if n.Xoffset == BADWIDTH { + Dump("bad width in dotoffset", n) + Fatal("bad width in dotoffset") + } + + i = Dotoffset(n.Left, oary, nn) + if i < 10 { + oary[i] = -(n.Xoffset + 1) + i++ + } + + default: + *nn = n + return 0 + } + + if i >= 10 { + *nn = nil + } + return i +} + +/* + * make a new off the books + */ +func Tempname(nn *Node, t *Type) { + if Curfn == nil { + Fatal("no curfn for tempname") + } + + if t == nil { + Yyerror("tempname called with nil type") + t = Types[TINT32] + } + + // give each tmp a different name so that there + // a chance to registerizer them + s := Lookupf("autotmp_%.4d", statuniqgen) + statuniqgen++ + n := Nod(ONAME, nil, nil) + n.Sym = s + s.Def = n + n.Type = t + n.Class = PAUTO + n.Addable = true + n.Ullman = 1 + n.Esc = EscNever + n.Curfn = Curfn + Curfn.Func.Dcl = list(Curfn.Func.Dcl, n) + + dowidth(t) + n.Xoffset = 0 + *nn = *n +} + +func temp(t *Type) *Node { + n := Nod(OXXX, nil, nil) + Tempname(n, t) + n.Sym.Def.Used = true + return n.Orig +} + +func gen(n *Node) { + //dump("gen", n); + + lno := setlineno(n) + + wasregalloc := Anyregalloc() + + if n == nil { + goto ret + } + + if n.Ninit != nil { + Genlist(n.Ninit) + } + + setlineno(n) + + switch n.Op { + default: + Fatal("gen: unknown op %v", Nconv(n, obj.FmtShort|obj.FmtSign)) + + case OCASE, + OFALL, + OXCASE, + OXFALL, + ODCLCONST, + ODCLFUNC, + ODCLTYPE: + break + + case OEMPTY: + break + + case OBLOCK: + Genlist(n.List) + + case OLABEL: + if isblanksym(n.Left.Sym) { + break + } + + lab := newlab(n) + + // if there are pending gotos, resolve them all to the current pc. + var p2 *obj.Prog + for p1 := lab.Gotopc; p1 != nil; p1 = p2 { + p2 = unpatch(p1) + Patch(p1, Pc) + } + + lab.Gotopc = nil + if lab.Labelpc == nil { + lab.Labelpc = Pc + } + + if n.Defn != nil { + switch n.Defn.Op { + // so stmtlabel can find the label + case OFOR, OSWITCH, OSELECT: + n.Defn.Sym = lab.Sym + } + } + + // if label is defined, emit jump to it. + // otherwise save list of pending gotos in lab->gotopc. + // the list is linked through the normal jump target field + // to avoid a second list. (the jumps are actually still + // valid code, since they're just going to another goto + // to the same label. we'll unwind it when we learn the pc + // of the label in the OLABEL case above.) + case OGOTO: + lab := newlab(n) + + if lab.Labelpc != nil { + gjmp(lab.Labelpc) + } else { + lab.Gotopc = gjmp(lab.Gotopc) + } + + case OBREAK: + if n.Left != nil { + lab := n.Left.Sym.Label + if lab == nil { + Yyerror("break label not defined: %v", n.Left.Sym) + break + } + + lab.Used = 1 + if lab.Breakpc == nil { + Yyerror("invalid break label %v", n.Left.Sym) + break + } + + gjmp(lab.Breakpc) + break + } + + if breakpc == nil { + Yyerror("break is not in a loop") + break + } + + gjmp(breakpc) + + case OCONTINUE: + if n.Left != nil { + lab := n.Left.Sym.Label + if lab == nil { + Yyerror("continue label not defined: %v", n.Left.Sym) + break + } + + lab.Used = 1 + if lab.Continpc == nil { + Yyerror("invalid continue label %v", n.Left.Sym) + break + } + + gjmp(lab.Continpc) + break + } + + if continpc == nil { + Yyerror("continue is not in a loop") + break + } + + gjmp(continpc) + + case OFOR: + sbreak := breakpc + p1 := gjmp(nil) // goto test + breakpc = gjmp(nil) // break: goto done + scontin := continpc + continpc = Pc + + // define break and continue labels + lab := stmtlabel(n) + if lab != nil { + lab.Breakpc = breakpc + lab.Continpc = continpc + } + + gen(n.Nincr) // contin: incr + Patch(p1, Pc) // test: + Bgen(n.Ntest, false, -1, breakpc) // if(!test) goto break + Genlist(n.Nbody) // body + gjmp(continpc) + Patch(breakpc, Pc) // done: + continpc = scontin + breakpc = sbreak + if lab != nil { + lab.Breakpc = nil + lab.Continpc = nil + } + + case OIF: + p1 := gjmp(nil) // goto test + p2 := gjmp(nil) // p2: goto else + Patch(p1, Pc) // test: + Bgen(n.Ntest, false, int(-n.Likely), p2) // if(!test) goto p2 + Genlist(n.Nbody) // then + p3 := gjmp(nil) // goto done + Patch(p2, Pc) // else: + Genlist(n.Nelse) // else + Patch(p3, Pc) // done: + + case OSWITCH: + sbreak := breakpc + p1 := gjmp(nil) // goto test + breakpc = gjmp(nil) // break: goto done + + // define break label + lab := stmtlabel(n) + if lab != nil { + lab.Breakpc = breakpc + } + + Patch(p1, Pc) // test: + Genlist(n.Nbody) // switch(test) body + Patch(breakpc, Pc) // done: + breakpc = sbreak + if lab != nil { + lab.Breakpc = nil + } + + case OSELECT: + sbreak := breakpc + p1 := gjmp(nil) // goto test + breakpc = gjmp(nil) // break: goto done + + // define break label + lab := stmtlabel(n) + if lab != nil { + lab.Breakpc = breakpc + } + + Patch(p1, Pc) // test: + Genlist(n.Nbody) // select() body + Patch(breakpc, Pc) // done: + breakpc = sbreak + if lab != nil { + lab.Breakpc = nil + } + + case ODCL: + cgen_dcl(n.Left) + + case OAS: + if gen_as_init(n) { + break + } + Cgen_as(n.Left, n.Right) + + case OASWB: + Cgen_as_wb(n.Left, n.Right, true) + + case OAS2DOTTYPE: + cgen_dottype(n.Rlist.N, n.List.N, n.List.Next.N, false) + + case OCALLMETH: + cgen_callmeth(n, 0) + + case OCALLINTER: + cgen_callinter(n, nil, 0) + + case OCALLFUNC: + cgen_call(n, 0) + + case OPROC: + cgen_proc(n, 1) + + case ODEFER: + cgen_proc(n, 2) + + case ORETURN, ORETJMP: + cgen_ret(n) + + // Function calls turned into compiler intrinsics. + // At top level, can just ignore the call and make sure to preserve side effects in the argument, if any. + case OGETG: + // nothing + case OSQRT: + cgen_discard(n.Left) + + case OCHECKNIL: + Cgen_checknil(n.Left) + + case OVARKILL: + gvarkill(n.Left) + } + +ret: + if Anyregalloc() != wasregalloc { + Dump("node", n) + Fatal("registers left allocated") + } + + lineno = lno +} + +func Cgen_as(nl, nr *Node) { + Cgen_as_wb(nl, nr, false) +} + +func Cgen_as_wb(nl, nr *Node, wb bool) { + if Debug['g'] != 0 { + op := "cgen_as" + if wb { + op = "cgen_as_wb" + } + Dump(op, nl) + Dump(op+" = ", nr) + } + + for nr != nil && nr.Op == OCONVNOP { + nr = nr.Left + } + + if nl == nil || isblank(nl) { + cgen_discard(nr) + return + } + + if nr == nil || iszero(nr) { + // heaps should already be clear + if nr == nil && (nl.Class&PHEAP != 0) { + return + } + + tl := nl.Type + if tl == nil { + return + } + if Isfat(tl) { + if nl.Op == ONAME { + Gvardef(nl) + } + Thearch.Clearfat(nl) + return + } + + Clearslim(nl) + return + } + + tl := nl.Type + if tl == nil { + return + } + + cgen_wb(nr, nl, wb) +} + +func cgen_callmeth(n *Node, proc int) { + // generate a rewrite in n2 for the method call + // (p.f)(...) goes to (f)(p,...) + + l := n.Left + + if l.Op != ODOTMETH { + Fatal("cgen_callmeth: not dotmethod: %v", l) + } + + n2 := *n + n2.Op = OCALLFUNC + n2.Left = l.Right + n2.Left.Type = l.Type + + if n2.Left.Op == ONAME { + n2.Left.Class = PFUNC + } + cgen_call(&n2, proc) +} + +// CgenTemp creates a temporary node, assigns n to it, and returns it. +func CgenTemp(n *Node) *Node { + var tmp Node + Tempname(&tmp, n.Type) + Cgen(n, &tmp) + return &tmp +} + +func checklabels() { + var l *NodeList + + for lab := labellist; lab != nil; lab = lab.Link { + if lab.Def == nil { + for l = lab.Use; l != nil; l = l.Next { + yyerrorl(int(l.N.Lineno), "label %v not defined", lab.Sym) + } + continue + } + + if lab.Use == nil && lab.Used == 0 { + yyerrorl(int(lab.Def.Lineno), "label %v defined and not used", lab.Sym) + continue + } + + if lab.Gotopc != nil { + Fatal("label %v never resolved", lab.Sym) + } + for l = lab.Use; l != nil; l = l.Next { + checkgoto(l.N, lab.Def) + } + } +} + +// Componentgen copies a composite value by moving its individual components. +// Slices, strings and interfaces are supported. Small structs or arrays with +// elements of basic type are also supported. +// nr is nil when assigning a zero value. +func Componentgen(nr, nl *Node) bool { + return componentgen_wb(nr, nl, false) +} + +// componentgen_wb is like componentgen but if wb==true emits write barriers for pointer updates. +func componentgen_wb(nr, nl *Node, wb bool) bool { + // Don't generate any code for complete copy of a variable into itself. + // It's useless, and the VARDEF will incorrectly mark the old value as dead. + // (This check assumes that the arguments passed to componentgen did not + // themselves come from Igen, or else we could have Op==ONAME but + // with a Type and Xoffset describing an individual field, not the entire + // variable.) + if nl.Op == ONAME && nl == nr { + return true + } + + // Count number of moves required to move components. + // If using write barrier, can only emit one pointer. + // TODO(rsc): Allow more pointers, for reflect.Value. + const maxMoves = 8 + n := 0 + numPtr := 0 + visitComponents(nl.Type, 0, func(t *Type, offset int64) bool { + n++ + if int(Simtype[t.Etype]) == Tptr && t != itable { + numPtr++ + } + return n <= maxMoves && (!wb || numPtr <= 1) + }) + if n > maxMoves || wb && numPtr > 1 { + return false + } + + // Must call emitVardef after evaluating rhs but before writing to lhs. + emitVardef := func() { + // Emit vardef if needed. + if nl.Op == ONAME { + switch nl.Type.Etype { + case TARRAY, TSTRING, TINTER, TSTRUCT: + Gvardef(nl) + } + } + } + + isConstString := Isconst(nr, CTSTR) + + if !cadable(nl) && nr != nil && !cadable(nr) && !isConstString { + return false + } + + var nodl Node + if cadable(nl) { + nodl = *nl + } else { + if nr != nil && !cadable(nr) && !isConstString { + return false + } + if nr == nil || isConstString || nl.Ullman >= nr.Ullman { + Igen(nl, &nodl, nil) + defer Regfree(&nodl) + } + } + lbase := nodl.Xoffset + + // Special case: zeroing. + var nodr Node + if nr == nil { + // When zeroing, prepare a register containing zero. + // TODO(rsc): Check that this is actually generating the best code. + if Thearch.REGZERO != 0 { + // cpu has a dedicated zero register + Nodreg(&nodr, Types[TUINT], Thearch.REGZERO) + } else { + // no dedicated zero register + var zero Node + Nodconst(&zero, nl.Type, 0) + Regalloc(&nodr, Types[TUINT], nil) + Thearch.Gmove(&zero, &nodr) + defer Regfree(&nodr) + } + + emitVardef() + visitComponents(nl.Type, 0, func(t *Type, offset int64) bool { + nodl.Type = t + nodl.Xoffset = lbase + offset + nodr.Type = t + if Isfloat[t.Etype] { + // TODO(rsc): Cache zero register like we do for integers? + Clearslim(&nodl) + } else { + Thearch.Gmove(&nodr, &nodl) + } + return true + }) + return true + } + + // Special case: assignment of string constant. + if isConstString { + emitVardef() + + // base + nodl.Type = Ptrto(Types[TUINT8]) + Regalloc(&nodr, Types[Tptr], nil) + p := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), nil, &nodr) + Datastring(nr.Val.U.(string), &p.From) + p.From.Type = obj.TYPE_ADDR + Thearch.Gmove(&nodr, &nodl) + Regfree(&nodr) + + // length + nodl.Type = Types[Simtype[TUINT]] + nodl.Xoffset += int64(Array_nel) - int64(Array_array) + Nodconst(&nodr, nodl.Type, int64(len(nr.Val.U.(string)))) + Thearch.Gmove(&nodr, &nodl) + return true + } + + // General case: copy nl = nr. + nodr = *nr + if !cadable(nr) { + if nr.Ullman >= UINF && nodl.Op == OINDREG { + Fatal("miscompile") + } + Igen(nr, &nodr, nil) + defer Regfree(&nodr) + } + rbase := nodr.Xoffset + + if nodl.Op == 0 { + Igen(nl, &nodl, nil) + defer Regfree(&nodl) + lbase = nodl.Xoffset + } + + emitVardef() + var ( + ptrType *Type + ptrOffset int64 + ) + visitComponents(nl.Type, 0, func(t *Type, offset int64) bool { + if wb && int(Simtype[t.Etype]) == Tptr && t != itable { + if ptrType != nil { + Fatal("componentgen_wb %v", Tconv(nl.Type, 0)) + } + ptrType = t + ptrOffset = offset + return true + } + nodl.Type = t + nodl.Xoffset = lbase + offset + nodr.Type = t + nodr.Xoffset = rbase + offset + Thearch.Gmove(&nodr, &nodl) + return true + }) + if ptrType != nil { + nodl.Type = ptrType + nodl.Xoffset = lbase + ptrOffset + nodr.Type = ptrType + nodr.Xoffset = rbase + ptrOffset + cgen_wbptr(&nodr, &nodl) + } + return true +} + +// visitComponents walks the individual components of the type t, +// walking into array elements, struct fields, the real and imaginary +// parts of complex numbers, and on 32-bit systems the high and +// low halves of 64-bit integers. +// It calls f for each such component, passing the component (aka element) +// type and memory offset, assuming t starts at startOffset. +// If f ever returns false, visitComponents returns false without any more +// calls to f. Otherwise visitComponents returns true. +func visitComponents(t *Type, startOffset int64, f func(elem *Type, elemOffset int64) bool) bool { + switch t.Etype { + case TINT64: + if Widthreg == 8 { + break + } + // NOTE: Assuming little endian (signed top half at offset 4). + // We don't have any 32-bit big-endian systems. + if Thearch.Thechar != '5' && Thearch.Thechar != '8' { + Fatal("unknown 32-bit architecture") + } + return f(Types[TUINT32], startOffset) && + f(Types[TINT32], startOffset+4) + + case TUINT64: + if Widthreg == 8 { + break + } + return f(Types[TUINT32], startOffset) && + f(Types[TUINT32], startOffset+4) + + case TCOMPLEX64: + return f(Types[TFLOAT32], startOffset) && + f(Types[TFLOAT32], startOffset+4) + + case TCOMPLEX128: + return f(Types[TFLOAT64], startOffset) && + f(Types[TFLOAT64], startOffset+8) + + case TINTER: + return f(itable, startOffset) && + f(Ptrto(Types[TUINT8]), startOffset+int64(Widthptr)) + return true + + case TSTRING: + return f(Ptrto(Types[TUINT8]), startOffset) && + f(Types[Simtype[TUINT]], startOffset+int64(Widthptr)) + + case TARRAY: + if Isslice(t) { + return f(Ptrto(t.Type), startOffset+int64(Array_array)) && + f(Types[Simtype[TUINT]], startOffset+int64(Array_nel)) && + f(Types[Simtype[TUINT]], startOffset+int64(Array_cap)) + } + + // Short-circuit [1e6]struct{}. + if t.Type.Width == 0 { + return true + } + + for i := int64(0); i < t.Bound; i++ { + if !visitComponents(t.Type, startOffset+i*t.Type.Width, f) { + return false + } + } + return true + + case TSTRUCT: + if t.Type != nil && t.Type.Width != 0 { + // NOTE(rsc): If this happens, the right thing to do is to say + // startOffset -= t.Type.Width + // but I want to see if it does. + // The old version of componentgen handled this, + // in code introduced in CL 6932045 to fix issue #4518. + // But the test case in issue 4518 does not trigger this anymore, + // so maybe this complication is no longer needed. + Fatal("struct not at offset 0") + } + + for field := t.Type; field != nil; field = field.Down { + if field.Etype != TFIELD { + Fatal("bad struct") + } + if !visitComponents(field.Type, startOffset+field.Width, f) { + return false + } + } + return true + } + return f(t, startOffset) +} + +func cadable(n *Node) bool { + // Note: Not sure why you can have n.Op == ONAME without n.Addable, but you can. + return n.Addable && n.Op == ONAME +} diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go new file mode 100644 index 0000000000..dc33f62ba4 --- /dev/null +++ b/src/cmd/compile/internal/gc/go.go @@ -0,0 +1,841 @@ +// Copyright 2009 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 gc + +import ( + "bytes" + "cmd/compile/internal/big" + "cmd/internal/obj" +) + +// avoid + +// The parser's maximum stack size. +// We have to use a #define macro here since yacc +// or bison will check for its definition and use +// a potentially smaller value if it is undefined. +const ( + NHUNK = 50000 + BUFSIZ = 8192 + NSYMB = 500 + NHASH = 1024 + MAXALIGN = 7 + UINF = 100 + PRIME1 = 3 + BADWIDTH = -1000000000 + MaxStackVarSize = 10 * 1024 * 1024 +) + +const ( + // These values are known by runtime. + // The MEMx and NOEQx values must run in parallel. See algtype. + AMEM = iota + AMEM0 + AMEM8 + AMEM16 + AMEM32 + AMEM64 + AMEM128 + ANOEQ + ANOEQ0 + ANOEQ8 + ANOEQ16 + ANOEQ32 + ANOEQ64 + ANOEQ128 + ASTRING + AINTER + ANILINTER + ASLICE + AFLOAT32 + AFLOAT64 + ACPLX64 + ACPLX128 + AUNK = 100 +) + +const ( + // Maximum size in bits for Mpints before signalling + // overflow and also mantissa precision for Mpflts. + Mpprec = 512 + // Turn on for constant arithmetic debugging output. + Mpdebug = false +) + +// Mpint represents an integer constant. +type Mpint struct { + Val big.Int + Ovf bool // set if Val overflowed compiler limit (sticky) +} + +// Mpflt represents a floating-point constant. +type Mpflt struct { + Val big.Float +} + +// Mpcplx represents a complex constant. +type Mpcplx struct { + Real Mpflt + Imag Mpflt +} + +type Val struct { + Ctype int16 + // 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 { + Name string // package name + Path string // string literal used in import statement + Pathsym *Sym + Prefix string // escaped path for use in symbol table + Imported uint8 // export data of this package was parsed + Exported int8 // import line written in export data + Direct int8 // imported directly + Safe bool // whether the package is marked as safe + Syms map[string]*Sym +} + +type Sym struct { + Lexical uint16 + Flags uint8 + Link *Sym + Uniqgen uint32 + Importdef *Pkg // where imported definition was found + Linkname string // link name + + // saved and restored by dcopy + Pkg *Pkg + Name string // variable name + Def *Node // definition: ONAME OTYPE OPACK or OLITERAL + Label *Label // corresponding label (ephemeral) + Block int32 // blocknumber to catch redeclaration + Lastlineno int32 // last declaration for diagnostic + Origpkg *Pkg // original package for . import + Lsym *obj.LSym + Fsym *Sym // funcsym +} + +type Type struct { + Etype uint8 + Nointerface bool + Noalg uint8 + Chan uint8 + Trecur uint8 // to detect loops + Printed uint8 + Embedded uint8 // TFIELD embedded type + Siggen uint8 + Funarg uint8 // on TSTRUCT and TFIELD + Copyany uint8 + Local bool // created in this file + Deferwidth uint8 + Broke uint8 // broken type definition. + Isddd bool // TFIELD is ... argument + Align uint8 + Haspointers uint8 // 0 unknown, 1 no, 2 yes + + Nod *Node // canonical OTYPE node + Orig *Type // original type (type literal or predefined type) + Lineno int + + // TFUNC + Thistuple int + Outtuple int + Intuple int + Outnamed uint8 + + Method *Type + Xmethod *Type + + Sym *Sym + Vargen int32 // unique name for OTYPE/ONAME + + Nname *Node + Argwid int64 + + // most nodes + Type *Type // actual type for TFIELD, element type for TARRAY, TCHAN, TMAP, TPTRxx + Width int64 // offset in TFIELD, width in all others + + // TFIELD + Down *Type // next struct field, also key type in TMAP + Outer *Type // outer struct + Note *string // literal string annotation + + // TARRAY + Bound int64 // negative is dynamic array + + // TMAP + Bucket *Type // internal type representing a hash bucket + Hmap *Type // internal type representing a Hmap (map header object) + Hiter *Type // internal type representing hash iterator state + Map *Type // link from the above 3 internal types back to the map type. + + Maplineno int32 // first use of TFORW as map key + Embedlineno int32 // first use of TFORW as embedded type + + // for TFORW, where to copy the eventual value to + Copyto *NodeList + + Lastfn *Node // for usefield +} + +type Label struct { + Used uint8 + Sym *Sym + Def *Node + Use *NodeList + Link *Label + + // for use during gen + Gotopc *obj.Prog // pointer to unresolved gotos + Labelpc *obj.Prog // pointer to code + Breakpc *obj.Prog // pointer to code + Continpc *obj.Prog // pointer to code +} + +type InitEntry struct { + Xoffset int64 // struct, array only + Expr *Node // bytes of run-time computed expressions +} + +type InitPlan struct { + Lit int64 + Zero int64 + Expr int64 + E []InitEntry +} + +const ( + SymExport = 1 << 0 // to be exported + SymPackage = 1 << 1 + SymExported = 1 << 2 // already written out by export + SymUniq = 1 << 3 + SymSiggen = 1 << 4 + SymAsm = 1 << 5 + SymAlgGen = 1 << 6 +) + +var dclstack *Sym + +type Iter struct { + Done int + Tfunc *Type + T *Type +} + +const ( + Txxx = iota + + TINT8 + TUINT8 + TINT16 + TUINT16 + TINT32 + TUINT32 + TINT64 + TUINT64 + TINT + TUINT + TUINTPTR + + TCOMPLEX64 + TCOMPLEX128 + + TFLOAT32 + TFLOAT64 + + TBOOL + + TPTR32 + TPTR64 + + TFUNC + TARRAY + T_old_DARRAY + TSTRUCT + TCHAN + TMAP + TINTER + TFORW + TFIELD + TANY + TSTRING + TUNSAFEPTR + + // pseudo-types for literals + TIDEAL + TNIL + TBLANK + + // pseudo-type for frame layout + TFUNCARGS + TCHANARGS + TINTERMETH + + NTYPE +) + +const ( + CTxxx = iota + + CTINT + CTRUNE + CTFLT + CTCPLX + CTSTR + CTBOOL + CTNIL +) + +const ( + /* types of channel */ + /* must match ../../pkg/nreflect/type.go:/Chandir */ + Cxxx = 0 + Crecv = 1 << 0 + Csend = 1 << 1 + Cboth = Crecv | Csend +) + +// declaration context +const ( + Pxxx = uint8(iota) + PEXTERN // global variable + PAUTO // local variables + PPARAM // input arguments + PPARAMOUT // output results + PPARAMREF // closure variable reference + PFUNC // global function + + PDISCARD // discard during parse of duplicate import + + PHEAP = uint8(1 << 7) // an extra bit to identify an escaped variable +) + +const ( + Etop = 1 << 1 // evaluated at statement level + Erv = 1 << 2 // evaluated in value context + Etype = 1 << 3 + Ecall = 1 << 4 // call-only expressions are ok + Efnstruct = 1 << 5 // multivalue function returns are ok + Eiota = 1 << 6 // iota is ok + Easgn = 1 << 7 // assigning to expression + Eindir = 1 << 8 // indirecting through expression + Eaddr = 1 << 9 // taking address of expression + Eproc = 1 << 10 // inside a go statement + Ecomplit = 1 << 11 // type in composite literal +) + +type Typedef struct { + Name string + Etype int + Sameas int +} + +type Sig struct { + name string + pkg *Pkg + isym *Sym + tsym *Sym + type_ *Type + mtype *Type + offset int32 + link *Sig +} + +type Io struct { + infile string + bin *obj.Biobuf + nlsemi int + eofnl int + last int + peekc int + peekc1 int // second peekc for ... + cp string // used for content when bin==nil + importsafe bool +} + +type Dlist struct { + field *Type +} + +type Idir struct { + link *Idir + dir string +} + +/* + * argument passing to/from + * smagic and umagic + */ +type Magic struct { + W int // input for both - width + S int // output for both - shift + Bad int // output for both - unexpected failure + + // magic multiplier for signed literal divisors + Sd int64 // input - literal divisor + Sm int64 // output - multiplier + + // magic multiplier for unsigned literal divisors + Ud uint64 // input - literal divisor + Um uint64 // output - multiplier + Ua int // output - adder +} + +/* + * note this is the runtime representation + * of the compilers arrays. + * + * typedef struct + * { // must not move anything + * uchar array[8]; // pointer to data + * uchar nel[4]; // number of elements + * uchar cap[4]; // allocated number of elements + * } Array; + */ +var Array_array int // runtime offsetof(Array,array) - same for String + +var Array_nel int // runtime offsetof(Array,nel) - same for String + +var Array_cap int // runtime offsetof(Array,cap) + +var sizeof_Array int // runtime sizeof(Array) + +/* + * note this is the runtime representation + * of the compilers strings. + * + * typedef struct + * { // must not move anything + * uchar array[8]; // pointer to data + * uchar nel[4]; // number of elements + * } String; + */ +var sizeof_String int // runtime sizeof(String) + +var dotlist [10]Dlist // size is max depth of embeddeds + +var curio Io + +var pushedio Io + +var lexlineno int32 + +var lineno int32 + +var prevlineno int32 + +var pragcgobuf string + +var infile string + +var outfile string + +var bout *obj.Biobuf + +var nerrors int + +var nsavederrors int + +var nsyntaxerrors int + +var decldepth int32 + +var safemode int + +var nolocalimports int + +var lexbuf bytes.Buffer +var strbuf bytes.Buffer + +var litbuf string + +var Debug [256]int + +var debugstr string + +var Debug_checknil int +var Debug_typeassert int + +var importmyname *Sym // my name for package + +var localpkg *Pkg // package being compiled + +var importpkg *Pkg // package being imported + +var structpkg *Pkg // package that declared struct, during import + +var builtinpkg *Pkg // fake package for builtins + +var gostringpkg *Pkg // fake pkg for Go strings + +var itabpkg *Pkg // fake pkg for itab cache + +var Runtimepkg *Pkg // package runtime + +var racepkg *Pkg // package runtime/race + +var typepkg *Pkg // fake package for runtime type info (headers) + +var typelinkpkg *Pkg // fake package for runtime type info (data) + +var weaktypepkg *Pkg // weak references to runtime type info + +var unsafepkg *Pkg // package unsafe + +var trackpkg *Pkg // fake package for field tracking + +var Tptr int // either TPTR32 or TPTR64 + +var myimportpath string + +var idirs *Idir + +var localimport string + +var asmhdr string + +var Types [NTYPE]*Type + +var idealstring *Type + +var idealbool *Type + +var bytetype *Type + +var runetype *Type + +var errortype *Type + +var Simtype [NTYPE]uint8 + +var ( + Isptr [NTYPE]bool + isforw [NTYPE]bool + Isint [NTYPE]bool + Isfloat [NTYPE]bool + Iscomplex [NTYPE]bool + Issigned [NTYPE]bool + issimple [NTYPE]bool +) + +var ( + okforeq [NTYPE]bool + okforadd [NTYPE]bool + okforand [NTYPE]bool + okfornone [NTYPE]bool + okforcmp [NTYPE]bool + okforbool [NTYPE]bool + okforcap [NTYPE]bool + okforlen [NTYPE]bool + okforarith [NTYPE]bool + okforconst [NTYPE]bool +) + +var ( + okfor [OEND][]bool + iscmp [OEND]bool +) + +var Minintval [NTYPE]*Mpint + +var Maxintval [NTYPE]*Mpint + +var minfltval [NTYPE]*Mpflt + +var maxfltval [NTYPE]*Mpflt + +var xtop *NodeList + +var externdcl *NodeList + +var exportlist *NodeList + +var importlist *NodeList // imported functions and methods with inlinable bodies + +var funcsyms *NodeList + +var dclcontext uint8 // PEXTERN/PAUTO + +var incannedimport int + +var statuniqgen int // name generator for static temps + +var loophack int + +var iota_ int32 + +var lastconst *NodeList + +var lasttype *Node + +var Maxarg int64 + +var Stksize int64 // stack size for current frame + +var stkptrsize int64 // prefix of stack containing pointers + +var blockgen int32 // max block number + +var block int32 // current block number + +var Hasdefer int // flag that curfn has defer statetment + +var Curfn *Node + +var Widthptr int + +var Widthint int + +var Widthreg int + +var typesw *Node + +var nblank *Node + +var hunk string + +var nhunk int32 + +var thunk int32 + +var Funcdepth int32 + +var typecheckok int + +var compiling_runtime int + +var compiling_wrappers int + +var inl_nonlocal int + +var use_writebarrier int + +var pure_go int + +var flag_installsuffix string + +var flag_race int + +var flag_largemodel int + +var noescape bool + +var nosplit bool + +var nowritebarrier bool + +var debuglive int + +var Ctxt *obj.Link + +var nointerface bool + +var writearchive int + +var bstdout obj.Biobuf + +var Nacl bool + +var continpc *obj.Prog + +var breakpc *obj.Prog + +var Pc *obj.Prog + +var nodfp *Node + +var Disable_checknil int + +var zerosize int64 + +type Flow struct { + Prog *obj.Prog // actual instruction + P1 *Flow // predecessors of this instruction: p1, + P2 *Flow // and then p2 linked though p2link. + P2link *Flow + S1 *Flow // successors of this instruction (at most two: s1 and s2). + S2 *Flow + Link *Flow // next instruction in function code + + Active int32 // usable by client + + Id int32 // sequence number in flow graph + Rpo int32 // reverse post ordering + Loop uint16 // x5 for every loop + Refset uint8 // diagnostic generated + + Data interface{} // for use by client +} + +type Graph struct { + Start *Flow + Num int + + // After calling flowrpo, rpo lists the flow nodes in reverse postorder, + // and each non-dead Flow node f has g->rpo[f->rpo] == f. + Rpo []*Flow +} + +/* + * interface to back end + */ + +const ( + // Pseudo-op, like TEXT, GLOBL, TYPE, PCDATA, FUNCDATA. + Pseudo = 1 << 1 + + // There's nothing to say about the instruction, + // but it's still okay to see. + OK = 1 << 2 + + // Size of right-side write, or right-side read if no write. + SizeB = 1 << 3 + SizeW = 1 << 4 + SizeL = 1 << 5 + SizeQ = 1 << 6 + SizeF = 1 << 7 + SizeD = 1 << 8 + + // Left side (Prog.from): address taken, read, write. + LeftAddr = 1 << 9 + LeftRead = 1 << 10 + LeftWrite = 1 << 11 + + // Register in middle (Prog.reg); only ever read. (arm, ppc64) + RegRead = 1 << 12 + CanRegRead = 1 << 13 + + // Right side (Prog.to): address taken, read, write. + RightAddr = 1 << 14 + RightRead = 1 << 15 + RightWrite = 1 << 16 + + // Instruction kinds + Move = 1 << 17 // straight move + Conv = 1 << 18 // size conversion + Cjmp = 1 << 19 // conditional jump + Break = 1 << 20 // breaks control flow (no fallthrough) + Call = 1 << 21 // function call + Jump = 1 << 22 // jump + Skip = 1 << 23 // data instruction + + // Set, use, or kill of carry bit. + // Kill means we never look at the carry bit after this kind of instruction. + SetCarry = 1 << 24 + UseCarry = 1 << 25 + KillCarry = 1 << 26 + + // Special cases for register use. (amd64, 386) + ShiftCX = 1 << 27 // possible shift by CX + ImulAXDX = 1 << 28 // possible multiply into DX:AX + + // Instruction updates whichever of from/to is type D_OREG. (ppc64) + PostInc = 1 << 29 +) + +type Arch struct { + Thechar int + Thestring string + Thelinkarch *obj.LinkArch + Typedefs []Typedef + REGSP int + REGCTXT int + REGCALLX int // BX + REGCALLX2 int // AX + REGRETURN int // AX + REGMIN int + REGMAX int + REGZERO int // architectural zero register, if available + FREGMIN int + FREGMAX int + MAXWIDTH int64 + ReservedRegs []int + + AddIndex func(*Node, int64, *Node) bool // optional + Betypeinit func() + Bgen_float func(*Node, bool, int, *obj.Prog) // optional + Cgen64 func(*Node, *Node) // only on 32-bit systems + Cgenindex func(*Node, *Node, bool) *obj.Prog + Cgen_bmul func(int, *Node, *Node, *Node) bool + Cgen_float func(*Node, *Node) // optional + Cgen_hmul func(*Node, *Node, *Node) + Cgen_shift func(int, bool, *Node, *Node, *Node) + Clearfat func(*Node) + Cmp64 func(*Node, *Node, int, int, *obj.Prog) // only on 32-bit systems + Defframe func(*obj.Prog) + Dodiv func(int, *Node, *Node, *Node) + Excise func(*Flow) + 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, 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 + // 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) + + Ginscon func(int, int64, *Node) + Ginsnop func() + Gmove func(*Node, *Node) + Igenindex func(*Node, *Node, bool) *obj.Prog + Linkarchinit func() + Peep func(*obj.Prog) + Proginfo func(*obj.Prog) // fills in Prog.Info + Regtyp func(*obj.Addr) bool + Sameaddr func(*obj.Addr, *obj.Addr) bool + Smallindir func(*obj.Addr, *obj.Addr) bool + Stackaddr func(*obj.Addr) bool + Blockcopy func(*Node, *Node, int64, int64, int64) + Sudoaddable func(int, *Node, *obj.Addr) bool + Sudoclean func() + Excludedregs func() uint64 + RtoB func(int) uint64 + FtoB func(int) uint64 + BtoR func(uint64) int + BtoF func(uint64) int + Optoas func(int, *Type) int + Doregbits func(int) uint64 + Regnames func(*int) []string + Use387 bool // should 8g use 387 FP instructions instead of sse2. +} + +var pcloc int32 + +var Thearch Arch + +var Newproc *Node + +var Deferproc *Node + +var Deferreturn *Node + +var Panicindex *Node + +var panicslice *Node + +var throwreturn *Node diff --git a/src/cmd/compile/internal/gc/go.y b/src/cmd/compile/internal/gc/go.y new file mode 100644 index 0000000000..7d523ae7c0 --- /dev/null +++ b/src/cmd/compile/internal/gc/go.y @@ -0,0 +1,2312 @@ +// Copyright 2009 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. + +/* + * Go language grammar. + * + * The Go semicolon rules are: + * + * 1. all statements and declarations are terminated by semicolons. + * 2. semicolons can be omitted before a closing ) or }. + * 3. semicolons are inserted by the lexer before a newline + * following a specific list of tokens. + * + * Rules #1 and #2 are accomplished by writing the lists as + * semicolon-separated lists with an optional trailing semicolon. + * Rule #3 is implemented in yylex. + */ + +%{ +package gc + +import ( + "fmt" + "strings" +) +%} +%union { + node *Node + list *NodeList + typ *Type + sym *Sym + val Val + i int +} + +// |sed 's/.* //' |9 fmt -l1 |sort |9 fmt -l50 | sed 's/^/%xxx /' + +%token LLITERAL +%token LASOP LCOLAS +%token LBREAK LCASE LCHAN LCONST LCONTINUE LDDD +%token LDEFAULT LDEFER LELSE LFALL LFOR LFUNC LGO LGOTO +%token LIF LIMPORT LINTERFACE LMAP LNAME +%token LPACKAGE LRANGE LRETURN LSELECT LSTRUCT LSWITCH +%token LTYPE LVAR + +%token LANDAND LANDNOT LBODY LCOMM LDEC LEQ LGE LGT +%token LIGNORE LINC LLE LLSH LLT LNE LOROR LRSH + +%type lbrace import_here +%type sym packname +%type oliteral + +%type stmt ntype +%type arg_type +%type case caseblock +%type compound_stmt dotname embed expr complitexpr bare_complitexpr +%type expr_or_type +%type fndcl hidden_fndcl fnliteral +%type for_body for_header for_stmt if_header if_stmt non_dcl_stmt +%type interfacedcl keyval labelname name +%type name_or_type non_expr_type +%type new_name dcl_name oexpr typedclname +%type onew_name +%type osimple_stmt pexpr pexpr_no_paren +%type pseudocall range_stmt select_stmt +%type simple_stmt +%type switch_stmt uexpr +%type xfndcl typedcl start_complit + +%type xdcl fnbody fnres loop_body dcl_name_list +%type new_name_list expr_list keyval_list braced_keyval_list expr_or_type_list xdcl_list +%type oexpr_list caseblock_list elseif elseif_list else stmt_list oarg_type_list_ocomma arg_type_list +%type interfacedcl_list vardcl vardcl_list structdcl structdcl_list +%type common_dcl constdcl constdcl1 constdcl_list typedcl_list + +%type convtype comptype dotdotdot +%type indcl interfacetype structtype ptrtype +%type recvchantype non_recvchantype othertype fnret_type fntype + +%type hidden_importsym hidden_pkg_importsym + +%type hidden_constant hidden_literal hidden_funarg +%type hidden_interfacedcl hidden_structdcl + +%type hidden_funres +%type ohidden_funres +%type hidden_funarg_list ohidden_funarg_list +%type hidden_interfacedcl_list ohidden_interfacedcl_list +%type hidden_structdcl_list ohidden_structdcl_list + +%type hidden_type hidden_type_misc hidden_pkgtype +%type hidden_type_func +%type hidden_type_recv_chan hidden_type_non_recv_chan + +%left LCOMM /* outside the usual hierarchy; here for good error messages */ + +%left LOROR +%left LANDAND +%left LEQ LNE LLE LGE LLT LGT +%left '+' '-' '|' '^' +%left '*' '/' '%' '&' LLSH LRSH LANDNOT + +/* + * manual override of shift/reduce conflicts. + * the general form is that we assign a precedence + * to the token being shifted and then introduce + * NotToken with lower precedence or PreferToToken with higher + * and annotate the reducing rule accordingly. + */ +%left NotPackage +%left LPACKAGE + +%left NotParen +%left '(' + +%left ')' +%left PreferToRightParen + +%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: + loadsys + package + imports + xdcl_list + { + xtop = concat(xtop, $4); + } + +package: + %prec NotPackage + { + prevlineno = lineno; + Yyerror("package statement must be first"); + errorexit(); + } +| LPACKAGE sym ';' + { + mkpackage($2.Name); + } + +/* + * this loads the definitions for the low-level runtime functions, + * so that the compiler can generate calls to them, + * but does not make the name "runtime" visible as a package. + */ +loadsys: + { + importpkg = Runtimepkg; + + if Debug['A'] != 0 { + cannedimports("runtime.Builtin", "package runtime\n\n$$\n\n"); + } else { + cannedimports("runtime.Builtin", runtimeimport); + } + curio.importsafe = true + } + import_package + import_there + { + importpkg = nil; + } + +imports: +| imports import ';' + +import: + LIMPORT import_stmt +| LIMPORT '(' import_stmt_list osemi ')' +| LIMPORT '(' ')' + +import_stmt: + import_here import_package import_there + { + ipkg := importpkg; + my := importmyname; + importpkg = nil; + importmyname = nil; + + if my == nil { + my = Lookup(ipkg.Name); + } + + pack := Nod(OPACK, nil, nil); + pack.Sym = my; + pack.Pkg = ipkg; + pack.Lineno = int32($1); + + if strings.HasPrefix(my.Name, ".") { + importdot(ipkg, pack); + break; + } + if my.Name == "init" { + Yyerror("cannot import package as init - init must be a func"); + break; + } + if my.Name == "_" { + break; + } + if my.Def != nil { + lineno = int32($1); + redeclare(my, "as imported package name"); + } + my.Def = pack; + my.Lastlineno = int32($1); + my.Block = 1; // at top level + } +| import_here import_there + { + // When an invalid import path is passed to importfile, + // it calls Yyerror and then sets up a fake import with + // no package statement. This allows us to test more + // than one invalid import statement in a single file. + if nerrors == 0 { + Fatal("phase error in import"); + } + } + +import_stmt_list: + import_stmt +| import_stmt_list ';' import_stmt + +import_here: + LLITERAL + { + // import with original name + $$ = parserline(); + importmyname = nil; + importfile(&$1, $$); + } +| sym LLITERAL + { + // import with given name + $$ = parserline(); + importmyname = $1; + importfile(&$2, $$); + } +| '.' LLITERAL + { + // import into my name space + $$ = parserline(); + importmyname = Lookup("."); + importfile(&$2, $$); + } + +import_package: + LPACKAGE LNAME import_safety ';' + { + if importpkg.Name == "" { + importpkg.Name = $2.Name; + numImport[$2.Name]++ + } else if importpkg.Name != $2.Name { + Yyerror("conflicting names %s and %s for package %q", importpkg.Name, $2.Name, importpkg.Path); + } + importpkg.Direct = 1; + importpkg.Safe = curio.importsafe + + if safemode != 0 && !curio.importsafe { + Yyerror("cannot import unsafe package %q", importpkg.Path); + } + } + +import_safety: +| LNAME + { + if $1.Name == "safe" { + curio.importsafe = true + } + } + +import_there: + { + defercheckwidth(); + } + hidden_import_list '$' '$' + { + resumecheckwidth(); + unimportfile(); + } + +/* + * declarations + */ +xdcl: + { + Yyerror("empty top-level declaration"); + $$ = nil; + } +| common_dcl +| xfndcl + { + $$ = list1($1); + } +| non_dcl_stmt + { + Yyerror("non-declaration statement outside function body"); + $$ = nil; + } +| error + { + $$ = nil; + } + +common_dcl: + LVAR vardcl + { + $$ = $2; + } +| LVAR '(' vardcl_list osemi ')' + { + $$ = $3; + } +| LVAR '(' ')' + { + $$ = nil; + } +| lconst constdcl + { + $$ = $2; + iota_ = -100000; + lastconst = nil; + } +| lconst '(' constdcl osemi ')' + { + $$ = $3; + iota_ = -100000; + lastconst = nil; + } +| lconst '(' constdcl ';' constdcl_list osemi ')' + { + $$ = concat($3, $5); + iota_ = -100000; + lastconst = nil; + } +| lconst '(' ')' + { + $$ = nil; + iota_ = -100000; + } +| LTYPE typedcl + { + $$ = list1($2); + } +| LTYPE '(' typedcl_list osemi ')' + { + $$ = $3; + } +| LTYPE '(' ')' + { + $$ = nil; + } + +lconst: + LCONST + { + iota_ = 0; + } + +vardcl: + dcl_name_list ntype + { + $$ = variter($1, $2, nil); + } +| dcl_name_list ntype '=' expr_list + { + $$ = variter($1, $2, $4); + } +| dcl_name_list '=' expr_list + { + $$ = variter($1, nil, $3); + } + +constdcl: + dcl_name_list ntype '=' expr_list + { + $$ = constiter($1, $2, $4); + } +| dcl_name_list '=' expr_list + { + $$ = constiter($1, nil, $3); + } + +constdcl1: + constdcl +| dcl_name_list ntype + { + $$ = constiter($1, $2, nil); + } +| dcl_name_list + { + $$ = constiter($1, nil, nil); + } + +typedclname: + sym + { + // different from dclname because the name + // becomes visible right here, not at the end + // of the declaration. + $$ = typedcl0($1); + } + +typedcl: + typedclname ntype + { + $$ = typedcl1($1, $2, true); + } + +simple_stmt: + expr + { + $$ = $1; + + // These nodes do not carry line numbers. + // Since a bare name used as an expression is an error, + // introduce a wrapper node to give the correct line. + switch($$.Op) { + case ONAME, ONONAME, OTYPE, OPACK, OLITERAL: + $$ = Nod(OPAREN, $$, nil); + $$.Implicit = true; + break; + } + } +| expr LASOP expr + { + $$ = Nod(OASOP, $1, $3); + $$.Etype = uint8($2); // rathole to pass opcode + } +| expr_list '=' expr_list + { + if $1.Next == nil && $3.Next == nil { + // simple + $$ = Nod(OAS, $1.N, $3.N); + break; + } + // multiple + $$ = Nod(OAS2, nil, nil); + $$.List = $1; + $$.Rlist = $3; + } +| expr_list LCOLAS expr_list + { + if $3.N.Op == OTYPESW { + $$ = Nod(OTYPESW, nil, $3.N.Right); + if $3.Next != nil { + Yyerror("expr.(type) must be alone in list"); + } + if $1.Next != nil { + Yyerror("argument count mismatch: %d = %d", count($1), 1); + } else if ($1.N.Op != ONAME && $1.N.Op != OTYPE && $1.N.Op != ONONAME) || isblank($1.N) { + Yyerror("invalid variable name %s in type switch", $1.N); + } else { + $$.Left = dclname($1.N.Sym); + } // it's a colas, so must not re-use an oldname. + break; + } + $$ = colas($1, $3, int32($2)); + } +| expr LINC + { + $$ = Nod(OASOP, $1, Nodintconst(1)); + $$.Implicit = true; + $$.Etype = OADD; + } +| expr LDEC + { + $$ = Nod(OASOP, $1, Nodintconst(1)); + $$.Implicit = true; + $$.Etype = OSUB; + } + +case: + LCASE expr_or_type_list ':' + { + var n, nn *Node + + // will be converted to OCASE + // right will point to next case + // done in casebody() + markdcl(); + $$ = Nod(OXCASE, nil, nil); + $$.List = $2; + if typesw != nil && typesw.Right != nil { + n = typesw.Right.Left + if n != nil { + // type switch - declare variable + nn = newname(n.Sym); + declare(nn, dclcontext); + $$.Nname = nn; + + // keep track of the instances for reporting unused + nn.Defn = typesw.Right; + } + } + } +| LCASE expr_or_type_list '=' expr ':' + { + var n *Node + + // will be converted to OCASE + // right will point to next case + // done in casebody() + markdcl(); + $$ = Nod(OXCASE, nil, nil); + if $2.Next == nil { + n = Nod(OAS, $2.N, $4); + } else { + n = Nod(OAS2, nil, nil); + n.List = $2; + n.Rlist = list1($4); + } + $$.List = list1(n); + } +| LCASE expr_or_type_list LCOLAS expr ':' + { + // will be converted to OCASE + // right will point to next case + // done in casebody() + markdcl(); + $$ = Nod(OXCASE, nil, nil); + $$.List = list1(colas($2, list1($4), int32($3))); + } +| LDEFAULT ':' + { + var n, nn *Node + + markdcl(); + $$ = Nod(OXCASE, nil, nil); + if typesw != nil && typesw.Right != nil { + n = typesw.Right.Left + if n != nil { + // type switch - declare variable + nn = newname(n.Sym); + declare(nn, dclcontext); + $$.Nname = nn; + + // keep track of the instances for reporting unused + nn.Defn = typesw.Right; + } + } + } + +compound_stmt: + '{' + { + markdcl(); + } + stmt_list '}' + { + if $3 == nil { + $$ = Nod(OEMPTY, nil, nil); + } else { + $$ = liststmt($3); + } + popdcl(); + } + +caseblock: + case + { + // If the last token read by the lexer was consumed + // as part of the case, clear it (parser has cleared yychar). + // If the last token read by the lexer was the lookahead + // leave it alone (parser has it cached in yychar). + // This is so that the stmt_list action doesn't look at + // the case tokens if the stmt_list is empty. + yylast = yychar; + $1.Xoffset = int64(block); + } + stmt_list + { + // This is the only place in the language where a statement + // list is not allowed to drop the final semicolon, because + // it's the only place where a statement list is not followed + // by a closing brace. Handle the error for pedantry. + + // Find the final token of the statement list. + // yylast is lookahead; yyprev is last of stmt_list + last := yyprev; + + if last > 0 && last != ';' && yychar != '}' { + Yyerror("missing statement after label"); + } + $$ = $1; + $$.Nbody = $3; + popdcl(); + } + +caseblock_list: + { + $$ = nil; + } +| caseblock_list caseblock + { + $$ = list($1, $2); + } + +loop_body: + LBODY + { + markdcl(); + } + stmt_list '}' + { + $$ = $3; + popdcl(); + } + +range_stmt: + expr_list '=' LRANGE expr + { + $$ = Nod(ORANGE, nil, $4); + $$.List = $1; + $$.Etype = 0; // := flag + } +| expr_list LCOLAS LRANGE expr + { + $$ = Nod(ORANGE, nil, $4); + $$.List = $1; + $$.Colas = true; + colasdefn($1, $$); + } +| LRANGE expr + { + $$ = Nod(ORANGE, nil, $2); + $$.Etype = 0; // := flag + } + +for_header: + osimple_stmt ';' osimple_stmt ';' osimple_stmt + { + // init ; test ; incr + if $5 != nil && $5.Colas { + Yyerror("cannot declare in the for-increment"); + } + $$ = Nod(OFOR, nil, nil); + if $1 != nil { + $$.Ninit = list1($1); + } + $$.Ntest = $3; + $$.Nincr = $5; + } +| osimple_stmt + { + // normal test + $$ = Nod(OFOR, nil, nil); + $$.Ntest = $1; + } +| range_stmt + +for_body: + for_header loop_body + { + $$ = $1; + $$.Nbody = concat($$.Nbody, $2); + } + +for_stmt: + LFOR + { + markdcl(); + } + for_body + { + $$ = $3; + popdcl(); + } + +if_header: + osimple_stmt + { + // test + $$ = Nod(OIF, nil, nil); + $$.Ntest = $1; + } +| osimple_stmt ';' osimple_stmt + { + // init ; test + $$ = Nod(OIF, nil, nil); + if $1 != nil { + $$.Ninit = list1($1); + } + $$.Ntest = $3; + } + +/* IF cond body (ELSE IF cond body)* (ELSE block)? */ +if_stmt: + LIF + { + markdcl(); + } + if_header + { + if $3.Ntest == nil { + Yyerror("missing condition in if statement"); + } + } + loop_body + { + $3.Nbody = $5; + } + elseif_list else + { + var n *Node + var nn *NodeList + + $$ = $3; + n = $3; + popdcl(); + for nn = concat($7, $8); nn != nil; nn = nn.Next { + if nn.N.Op == OIF { + popdcl(); + } + n.Nelse = list1(nn.N); + n = nn.N; + } + } + +elseif: + LELSE LIF + { + markdcl(); + } + if_header loop_body + { + if $4.Ntest == nil { + Yyerror("missing condition in if statement"); + } + $4.Nbody = $5; + $$ = list1($4); + } + +elseif_list: + { + $$ = nil; + } +| elseif_list elseif + { + $$ = concat($1, $2); + } + +else: + { + $$ = nil; + } +| LELSE compound_stmt + { + l := &NodeList{N: $2} + l.End = l + $$ = l; + } + +switch_stmt: + LSWITCH + { + markdcl(); + } + if_header + { + var n *Node + n = $3.Ntest; + if n != nil && n.Op != OTYPESW { + n = nil; + } + typesw = Nod(OXXX, typesw, n); + } + LBODY caseblock_list '}' + { + $$ = $3; + $$.Op = OSWITCH; + $$.List = $6; + typesw = typesw.Left; + popdcl(); + } + +select_stmt: + LSELECT + { + typesw = Nod(OXXX, typesw, nil); + } + LBODY caseblock_list '}' + { + $$ = Nod(OSELECT, nil, nil); + $$.Lineno = typesw.Lineno; + $$.List = $4; + typesw = typesw.Left; + } + +/* + * expressions + */ +expr: + uexpr +| expr LOROR expr + { + $$ = Nod(OOROR, $1, $3); + } +| expr LANDAND expr + { + $$ = Nod(OANDAND, $1, $3); + } +| expr LEQ expr + { + $$ = Nod(OEQ, $1, $3); + } +| expr LNE expr + { + $$ = Nod(ONE, $1, $3); + } +| expr LLT expr + { + $$ = Nod(OLT, $1, $3); + } +| expr LLE expr + { + $$ = Nod(OLE, $1, $3); + } +| expr LGE expr + { + $$ = Nod(OGE, $1, $3); + } +| expr LGT expr + { + $$ = Nod(OGT, $1, $3); + } +| expr '+' expr + { + $$ = Nod(OADD, $1, $3); + } +| expr '-' expr + { + $$ = Nod(OSUB, $1, $3); + } +| expr '|' expr + { + $$ = Nod(OOR, $1, $3); + } +| expr '^' expr + { + $$ = Nod(OXOR, $1, $3); + } +| expr '*' expr + { + $$ = Nod(OMUL, $1, $3); + } +| expr '/' expr + { + $$ = Nod(ODIV, $1, $3); + } +| expr '%' expr + { + $$ = Nod(OMOD, $1, $3); + } +| expr '&' expr + { + $$ = Nod(OAND, $1, $3); + } +| expr LANDNOT expr + { + $$ = Nod(OANDNOT, $1, $3); + } +| expr LLSH expr + { + $$ = Nod(OLSH, $1, $3); + } +| expr LRSH expr + { + $$ = Nod(ORSH, $1, $3); + } + /* not an expression anymore, but left in so we can give a good error */ +| expr LCOMM expr + { + $$ = Nod(OSEND, $1, $3); + } + +uexpr: + pexpr +| '*' uexpr + { + $$ = Nod(OIND, $2, nil); + } +| '&' uexpr + { + if $2.Op == OCOMPLIT { + // Special case for &T{...}: turn into (*T){...}. + $$ = $2; + $$.Right = Nod(OIND, $$.Right, nil); + $$.Right.Implicit = true; + } else { + $$ = Nod(OADDR, $2, nil); + } + } +| '+' uexpr + { + $$ = Nod(OPLUS, $2, nil); + } +| '-' uexpr + { + $$ = Nod(OMINUS, $2, nil); + } +| '!' uexpr + { + $$ = Nod(ONOT, $2, nil); + } +| '~' uexpr + { + Yyerror("the bitwise complement operator is ^"); + $$ = Nod(OCOM, $2, nil); + } +| '^' uexpr + { + $$ = Nod(OCOM, $2, nil); + } +| LCOMM uexpr + { + $$ = Nod(ORECV, $2, nil); + } + +/* + * call-like statements that + * can be preceded by 'defer' and 'go' + */ +pseudocall: + pexpr '(' ')' + { + $$ = Nod(OCALL, $1, nil); + } +| pexpr '(' expr_or_type_list ocomma ')' + { + $$ = Nod(OCALL, $1, nil); + $$.List = $3; + } +| pexpr '(' expr_or_type_list LDDD ocomma ')' + { + $$ = Nod(OCALL, $1, nil); + $$.List = $3; + $$.Isddd = true; + } + +pexpr_no_paren: + LLITERAL + { + $$ = nodlit($1); + } +| name +| pexpr '.' sym + { + if $1.Op == OPACK { + var s *Sym + s = restrictlookup($3.Name, $1.Pkg); + $1.Used = true; + $$ = oldname(s); + break; + } + $$ = Nod(OXDOT, $1, newname($3)); + } +| pexpr '.' '(' expr_or_type ')' + { + $$ = Nod(ODOTTYPE, $1, $4); + } +| pexpr '.' '(' LTYPE ')' + { + $$ = Nod(OTYPESW, nil, $1); + } +| pexpr '[' expr ']' + { + $$ = Nod(OINDEX, $1, $3); + } +| pexpr '[' oexpr ':' oexpr ']' + { + $$ = Nod(OSLICE, $1, Nod(OKEY, $3, $5)); + } +| pexpr '[' oexpr ':' oexpr ':' oexpr ']' + { + if $5 == nil { + Yyerror("middle index required in 3-index slice"); + } + if $7 == nil { + Yyerror("final index required in 3-index slice"); + } + $$ = Nod(OSLICE3, $1, Nod(OKEY, $3, Nod(OKEY, $5, $7))); + } +| pseudocall +| convtype '(' expr ocomma ')' + { + // conversion + $$ = Nod(OCALL, $1, nil); + $$.List = list1($3); + } +| comptype lbrace start_complit braced_keyval_list '}' + { + $$ = $3; + $$.Right = $1; + $$.List = $4; + fixlbrace($2); + } +| pexpr_no_paren '{' start_complit braced_keyval_list '}' + { + $$ = $3; + $$.Right = $1; + $$.List = $4; + } +| '(' expr_or_type ')' '{' start_complit braced_keyval_list '}' + { + Yyerror("cannot parenthesize type in composite literal"); + $$ = $5; + $$.Right = $2; + $$.List = $6; + } +| fnliteral + +start_complit: + { + // composite expression. + // make node early so we get the right line number. + $$ = Nod(OCOMPLIT, nil, nil); + } + +keyval: + expr ':' complitexpr + { + $$ = Nod(OKEY, $1, $3); + } + +bare_complitexpr: + expr + { + // These nodes do not carry line numbers. + // Since a composite literal commonly spans several lines, + // the line number on errors may be misleading. + // Introduce a wrapper node to give the correct line. + $$ = $1; + switch($$.Op) { + case ONAME, ONONAME, OTYPE, OPACK, OLITERAL: + $$ = Nod(OPAREN, $$, nil); + $$.Implicit = true; + } + } +| '{' start_complit braced_keyval_list '}' + { + $$ = $2; + $$.List = $3; + } + +complitexpr: + expr +| '{' start_complit braced_keyval_list '}' + { + $$ = $2; + $$.List = $3; + } + +pexpr: + pexpr_no_paren +| '(' expr_or_type ')' + { + $$ = $2; + + // Need to know on lhs of := whether there are ( ). + // Don't bother with the OPAREN in other cases: + // it's just a waste of memory and time. + switch($$.Op) { + case ONAME, ONONAME, OPACK, OTYPE, OLITERAL, OTYPESW: + $$ = Nod(OPAREN, $$, nil); + } + } + +expr_or_type: + expr +| non_expr_type %prec PreferToRightParen + +name_or_type: + ntype + +lbrace: + LBODY + { + $$ = LBODY; + } +| '{' + { + $$ = '{'; + } + +/* + * names and types + * newname is used before declared + * oldname is used after declared + */ +new_name: + sym + { + if $1 == nil { + $$ = nil; + } else { + $$ = newname($1); + } + } + +dcl_name: + sym + { + $$ = dclname($1); + } + +onew_name: + { + $$ = nil; + } +| new_name + +sym: + LNAME + { + $$ = $1; + // during imports, unqualified non-exported identifiers are from builtinpkg + if importpkg != nil && !exportname($1.Name) { + $$ = Pkglookup($1.Name, builtinpkg); + } + } +| hidden_importsym +| '?' + { + $$ = nil; + } + +hidden_importsym: + '@' LLITERAL '.' LNAME + { + var p *Pkg + + if $2.U.(string) == "" { + p = importpkg; + } else { + if isbadimport($2.U.(string)) { + errorexit(); + } + p = mkpkg($2.U.(string)); + } + $$ = Pkglookup($4.Name, p); + } +| '@' LLITERAL '.' '?' + { + var p *Pkg + + if $2.U.(string) == "" { + p = importpkg; + } else { + if isbadimport($2.U.(string)) { + errorexit(); + } + p = mkpkg($2.U.(string)); + } + $$ = Pkglookup("?", p); + } + +name: + sym %prec NotParen + { + $$ = oldname($1); + if $$.Pack != nil { + $$.Pack.Used = true; + } + } + +labelname: + new_name + +/* + * to avoid parsing conflicts, type is split into + * channel types + * function types + * parenthesized types + * any other type + * the type system makes additional restrictions, + * but those are not implemented in the grammar. + */ +dotdotdot: + LDDD + { + Yyerror("final argument in variadic function missing type"); + $$ = Nod(ODDD, typenod(typ(TINTER)), nil); + } +| LDDD ntype + { + $$ = Nod(ODDD, $2, nil); + } + +ntype: + recvchantype +| fntype +| othertype +| ptrtype +| dotname +| '(' ntype ')' + { + $$ = $2; + } + +non_expr_type: + recvchantype +| fntype +| othertype +| '*' non_expr_type + { + $$ = Nod(OIND, $2, nil); + } + +non_recvchantype: + fntype +| othertype +| ptrtype +| dotname +| '(' ntype ')' + { + $$ = $2; + } + +convtype: + fntype +| othertype + +comptype: + othertype + +fnret_type: + recvchantype +| fntype +| othertype +| ptrtype +| dotname + +dotname: + name +| name '.' sym + { + if $1.Op == OPACK { + var s *Sym + s = restrictlookup($3.Name, $1.Pkg); + $1.Used = true; + $$ = oldname(s); + break; + } + $$ = Nod(OXDOT, $1, newname($3)); + } + +othertype: + '[' oexpr ']' ntype + { + $$ = Nod(OTARRAY, $2, $4); + } +| '[' LDDD ']' ntype + { + // array literal of nelem + $$ = Nod(OTARRAY, Nod(ODDD, nil, nil), $4); + } +| LCHAN non_recvchantype + { + $$ = Nod(OTCHAN, $2, nil); + $$.Etype = Cboth; + } +| LCHAN LCOMM ntype + { + $$ = Nod(OTCHAN, $3, nil); + $$.Etype = Csend; + } +| LMAP '[' ntype ']' ntype + { + $$ = Nod(OTMAP, $3, $5); + } +| structtype +| interfacetype + +ptrtype: + '*' ntype + { + $$ = Nod(OIND, $2, nil); + } + +recvchantype: + LCOMM LCHAN ntype + { + $$ = Nod(OTCHAN, $3, nil); + $$.Etype = Crecv; + } + +structtype: + LSTRUCT lbrace structdcl_list osemi '}' + { + $$ = Nod(OTSTRUCT, nil, nil); + $$.List = $3; + fixlbrace($2); + } +| LSTRUCT lbrace '}' + { + $$ = Nod(OTSTRUCT, nil, nil); + fixlbrace($2); + } + +interfacetype: + LINTERFACE lbrace interfacedcl_list osemi '}' + { + $$ = Nod(OTINTER, nil, nil); + $$.List = $3; + fixlbrace($2); + } +| LINTERFACE lbrace '}' + { + $$ = Nod(OTINTER, nil, nil); + fixlbrace($2); + } + +/* + * function stuff + * all in one place to show how crappy it all is + */ +xfndcl: + LFUNC fndcl fnbody + { + $$ = $2; + if $$ == nil { + break; + } + if noescape && $3 != nil { + Yyerror("can only use //go:noescape with external func implementations"); + } + $$.Nbody = $3; + $$.Func.Endlineno = lineno; + $$.Noescape = noescape; + $$.Func.Nosplit = nosplit; + $$.Func.Nowritebarrier = nowritebarrier; + funcbody($$); + } + +fndcl: + sym '(' oarg_type_list_ocomma ')' fnres + { + var t *Node + + $$ = nil; + $3 = checkarglist($3, 1); + + if $1.Name == "init" { + $1 = renameinit(); + if $3 != nil || $5 != nil { + Yyerror("func init must have no arguments and no return values"); + } + } + if localpkg.Name == "main" && $1.Name == "main" { + if $3 != nil || $5 != nil { + Yyerror("func main must have no arguments and no return values"); + } + } + + t = Nod(OTFUNC, nil, nil); + t.List = $3; + t.Rlist = $5; + + $$ = Nod(ODCLFUNC, nil, nil); + $$.Nname = newfuncname($1); + $$.Nname.Defn = $$; + $$.Nname.Ntype = t; // TODO: check if nname already has an ntype + declare($$.Nname, PFUNC); + + funchdr($$); + } +| '(' oarg_type_list_ocomma ')' sym '(' oarg_type_list_ocomma ')' fnres + { + var rcvr, t *Node + + $$ = nil; + $2 = checkarglist($2, 0); + $6 = checkarglist($6, 1); + + if $2 == nil { + Yyerror("method has no receiver"); + break; + } + if $2.Next != nil { + Yyerror("method has multiple receivers"); + break; + } + rcvr = $2.N; + if rcvr.Op != ODCLFIELD { + Yyerror("bad receiver in method"); + break; + } + + t = Nod(OTFUNC, rcvr, nil); + t.List = $6; + t.Rlist = $8; + + $$ = Nod(ODCLFUNC, nil, nil); + $$.Func.Shortname = newfuncname($4); + $$.Nname = methodname1($$.Func.Shortname, rcvr.Right); + $$.Nname.Defn = $$; + $$.Nname.Ntype = t; + $$.Nname.Nointerface = nointerface; + declare($$.Nname, PFUNC); + + funchdr($$); + } + +hidden_fndcl: + hidden_pkg_importsym '(' ohidden_funarg_list ')' ohidden_funres + { + var s *Sym + var t *Type + + $$ = nil; + + s = $1; + t = functype(nil, $3, $5); + + importsym(s, ONAME); + if s.Def != nil && s.Def.Op == ONAME { + if Eqtype(t, s.Def.Type) { + dclcontext = PDISCARD; // since we skip funchdr below + break; + } + Yyerror("inconsistent definition for func %v during import\n\t%v\n\t%v", s, s.Def.Type, t); + } + + $$ = newfuncname(s); + $$.Type = t; + declare($$, PFUNC); + + funchdr($$); + } +| '(' hidden_funarg_list ')' sym '(' ohidden_funarg_list ')' ohidden_funres + { + $$ = methodname1(newname($4), $2.N.Right); + $$.Type = functype($2.N, $6, $8); + + checkwidth($$.Type); + addmethod($4, $$.Type, false, nointerface); + nointerface = false + funchdr($$); + + // inl.C's inlnode in on a dotmeth node expects to find the inlineable body as + // (dotmeth's type).Nname.Inl, and dotmeth's type has been pulled + // out by typecheck's lookdot as this $$.ttype. So by providing + // this back link here we avoid special casing there. + $$.Type.Nname = $$; + } + +fntype: + LFUNC '(' oarg_type_list_ocomma ')' fnres + { + $3 = checkarglist($3, 1); + $$ = Nod(OTFUNC, nil, nil); + $$.List = $3; + $$.Rlist = $5; + } + +fnbody: + { + $$ = nil; + } +| '{' stmt_list '}' + { + $$ = $2; + if $$ == nil { + $$ = list1(Nod(OEMPTY, nil, nil)); + } + } + +fnres: + %prec NotParen + { + $$ = nil; + } +| fnret_type + { + $$ = list1(Nod(ODCLFIELD, nil, $1)); + } +| '(' oarg_type_list_ocomma ')' + { + $2 = checkarglist($2, 0); + $$ = $2; + } + +fnlitdcl: + fntype + { + closurehdr($1); + } + +fnliteral: + fnlitdcl lbrace stmt_list '}' + { + $$ = closurebody($3); + fixlbrace($2); + } +| fnlitdcl error + { + $$ = closurebody(nil); + } + +/* + * lists of things + * note that they are left recursive + * to conserve yacc stack. they need to + * be reversed to interpret correctly + */ +xdcl_list: + { + $$ = nil; + } +| xdcl_list xdcl ';' + { + $$ = concat($1, $2); + if nsyntaxerrors == 0 { + testdclstack(); + } + nointerface = false + noescape = false + nosplit = false + nowritebarrier = false + } + +vardcl_list: + vardcl +| vardcl_list ';' vardcl + { + $$ = concat($1, $3); + } + +constdcl_list: + constdcl1 +| constdcl_list ';' constdcl1 + { + $$ = concat($1, $3); + } + +typedcl_list: + typedcl + { + $$ = list1($1); + } +| typedcl_list ';' typedcl + { + $$ = list($1, $3); + } + +structdcl_list: + structdcl +| structdcl_list ';' structdcl + { + $$ = concat($1, $3); + } + +interfacedcl_list: + interfacedcl + { + $$ = list1($1); + } +| interfacedcl_list ';' interfacedcl + { + $$ = list($1, $3); + } + +structdcl: + new_name_list ntype oliteral + { + var l *NodeList + + var n *Node + l = $1; + if l == nil { + // ? symbol, during import (list1(nil) == nil) + n = $2; + if n.Op == OIND { + n = n.Left; + } + n = embedded(n.Sym, importpkg); + n.Right = $2; + n.Val = $3; + $$ = list1(n); + break; + } + + for l=$1; l != nil; l=l.Next { + l.N = Nod(ODCLFIELD, l.N, $2); + l.N.Val = $3; + } + } +| embed oliteral + { + $1.Val = $2; + $$ = list1($1); + } +| '(' embed ')' oliteral + { + $2.Val = $4; + $$ = list1($2); + Yyerror("cannot parenthesize embedded type"); + } +| '*' embed oliteral + { + $2.Right = Nod(OIND, $2.Right, nil); + $2.Val = $3; + $$ = list1($2); + } +| '(' '*' embed ')' oliteral + { + $3.Right = Nod(OIND, $3.Right, nil); + $3.Val = $5; + $$ = list1($3); + Yyerror("cannot parenthesize embedded type"); + } +| '*' '(' embed ')' oliteral + { + $3.Right = Nod(OIND, $3.Right, nil); + $3.Val = $5; + $$ = list1($3); + Yyerror("cannot parenthesize embedded type"); + } + +packname: + LNAME + { + var n *Node + + $$ = $1; + n = oldname($1); + if n.Pack != nil { + n.Pack.Used = true; + } + } +| LNAME '.' sym + { + var pkg *Pkg + + if $1.Def == nil || $1.Def.Op != OPACK { + Yyerror("%v is not a package", $1); + pkg = localpkg; + } else { + $1.Def.Used = true; + pkg = $1.Def.Pkg; + } + $$ = restrictlookup($3.Name, pkg); + } + +embed: + packname + { + $$ = embedded($1, localpkg); + } + +interfacedcl: + new_name indcl + { + $$ = Nod(ODCLFIELD, $1, $2); + ifacedcl($$); + } +| packname + { + $$ = Nod(ODCLFIELD, nil, oldname($1)); + } +| '(' packname ')' + { + $$ = Nod(ODCLFIELD, nil, oldname($2)); + Yyerror("cannot parenthesize embedded type"); + } + +indcl: + '(' oarg_type_list_ocomma ')' fnres + { + // without func keyword + $2 = checkarglist($2, 1); + $$ = Nod(OTFUNC, fakethis(), nil); + $$.List = $2; + $$.Rlist = $4; + } + +/* + * function arguments. + */ +arg_type: + name_or_type +| sym name_or_type + { + $$ = Nod(ONONAME, nil, nil); + $$.Sym = $1; + $$ = Nod(OKEY, $$, $2); + } +| sym dotdotdot + { + $$ = Nod(ONONAME, nil, nil); + $$.Sym = $1; + $$ = Nod(OKEY, $$, $2); + } +| dotdotdot + +arg_type_list: + arg_type + { + $$ = list1($1); + } +| arg_type_list ',' arg_type + { + $$ = list($1, $3); + } + +oarg_type_list_ocomma: + { + $$ = nil; + } +| arg_type_list ocomma + { + $$ = $1; + } + +/* + * statement + */ +stmt: + { + $$ = nil; + } +| compound_stmt +| common_dcl + { + $$ = liststmt($1); + } +| non_dcl_stmt +| error + { + $$ = nil; + } + +non_dcl_stmt: + simple_stmt +| for_stmt +| switch_stmt +| select_stmt +| if_stmt +| labelname ':' + { + $1 = Nod(OLABEL, $1, nil); + $1.Sym = dclstack; // context, for goto restrictions + } + stmt + { + var l *NodeList + + $1.Defn = $4; + l = list1($1); + if $4 != nil { + l = list(l, $4); + } + $$ = liststmt(l); + } +| LFALL + { + // will be converted to OFALL + $$ = Nod(OXFALL, nil, nil); + $$.Xoffset = int64(block); + } +| LBREAK onew_name + { + $$ = Nod(OBREAK, $2, nil); + } +| LCONTINUE onew_name + { + $$ = Nod(OCONTINUE, $2, nil); + } +| LGO pseudocall + { + $$ = Nod(OPROC, $2, nil); + } +| LDEFER pseudocall + { + $$ = Nod(ODEFER, $2, nil); + } +| LGOTO new_name + { + $$ = Nod(OGOTO, $2, nil); + $$.Sym = dclstack; // context, for goto restrictions + } +| LRETURN oexpr_list + { + $$ = Nod(ORETURN, nil, nil); + $$.List = $2; + if $$.List == nil && Curfn != nil { + var l *NodeList + + for l=Curfn.Func.Dcl; l != nil; l=l.Next { + if l.N.Class == PPARAM { + continue; + } + if l.N.Class != PPARAMOUT { + break; + } + if l.N.Sym.Def != l.N { + Yyerror("%s is shadowed during return", l.N.Sym.Name); + } + } + } + } + +stmt_list: + stmt + { + $$ = nil; + if $1 != nil { + $$ = list1($1); + } + } +| stmt_list ';' stmt + { + $$ = $1; + if $3 != nil { + $$ = list($$, $3); + } + } + +new_name_list: + new_name + { + $$ = list1($1); + } +| new_name_list ',' new_name + { + $$ = list($1, $3); + } + +dcl_name_list: + dcl_name + { + $$ = list1($1); + } +| dcl_name_list ',' dcl_name + { + $$ = list($1, $3); + } + +expr_list: + expr + { + $$ = list1($1); + } +| expr_list ',' expr + { + $$ = list($1, $3); + } + +expr_or_type_list: + expr_or_type + { + $$ = list1($1); + } +| expr_or_type_list ',' expr_or_type + { + $$ = list($1, $3); + } + +/* + * list of combo of keyval and val + */ +keyval_list: + keyval + { + $$ = list1($1); + } +| bare_complitexpr + { + $$ = list1($1); + } +| keyval_list ',' keyval + { + $$ = list($1, $3); + } +| keyval_list ',' bare_complitexpr + { + $$ = list($1, $3); + } + +braced_keyval_list: + { + $$ = nil; + } +| keyval_list ocomma + { + $$ = $1; + } + +/* + * optional things + */ +osemi: +| ';' + +ocomma: +| ',' + +oexpr: + { + $$ = nil; + } +| expr + +oexpr_list: + { + $$ = nil; + } +| expr_list + +osimple_stmt: + { + $$ = nil; + } +| simple_stmt + +ohidden_funarg_list: + { + $$ = nil; + } +| hidden_funarg_list + +ohidden_structdcl_list: + { + $$ = nil; + } +| hidden_structdcl_list + +ohidden_interfacedcl_list: + { + $$ = nil; + } +| hidden_interfacedcl_list + +oliteral: + { + $$.Ctype = CTxxx; + } +| LLITERAL + +/* + * import syntax from package header + */ +hidden_import: + LIMPORT LNAME LLITERAL ';' + { + importimport($2, $3.U.(string)); + } +| LVAR hidden_pkg_importsym hidden_type ';' + { + importvar($2, $3); + } +| LCONST hidden_pkg_importsym '=' hidden_constant ';' + { + importconst($2, Types[TIDEAL], $4); + } +| LCONST hidden_pkg_importsym hidden_type '=' hidden_constant ';' + { + importconst($2, $3, $5); + } +| LTYPE hidden_pkgtype hidden_type ';' + { + importtype($2, $3); + } +| LFUNC hidden_fndcl fnbody ';' + { + if $2 == nil { + dclcontext = PEXTERN; // since we skip the funcbody below + break; + } + + $2.Func.Inl = $3; + + funcbody($2); + importlist = list(importlist, $2); + + if Debug['E'] > 0 { + fmt.Printf("import [%q] func %v \n", importpkg.Path, $2) + if Debug['m'] > 2 && $2.Func.Inl != nil { + fmt.Printf("inl body:%v\n", $2.Func.Inl) + } + } + } + +hidden_pkg_importsym: + hidden_importsym + { + $$ = $1; + structpkg = $$.Pkg; + } + +hidden_pkgtype: + hidden_pkg_importsym + { + $$ = pkgtype($1); + importsym($1, OTYPE); + } + +/* + * importing types + */ + +hidden_type: + hidden_type_misc +| hidden_type_recv_chan +| hidden_type_func + +hidden_type_non_recv_chan: + hidden_type_misc +| hidden_type_func + +hidden_type_misc: + hidden_importsym + { + $$ = pkgtype($1); + } +| LNAME + { + // predefined name like uint8 + $1 = Pkglookup($1.Name, builtinpkg); + if $1.Def == nil || $1.Def.Op != OTYPE { + Yyerror("%s is not a type", $1.Name); + $$ = nil; + } else { + $$ = $1.Def.Type; + } + } +| '[' ']' hidden_type + { + $$ = aindex(nil, $3); + } +| '[' LLITERAL ']' hidden_type + { + $$ = aindex(nodlit($2), $4); + } +| LMAP '[' hidden_type ']' hidden_type + { + $$ = maptype($3, $5); + } +| LSTRUCT '{' ohidden_structdcl_list '}' + { + $$ = tostruct($3); + } +| LINTERFACE '{' ohidden_interfacedcl_list '}' + { + $$ = tointerface($3); + } +| '*' hidden_type + { + $$ = Ptrto($2); + } +| LCHAN hidden_type_non_recv_chan + { + $$ = typ(TCHAN); + $$.Type = $2; + $$.Chan = Cboth; + } +| LCHAN '(' hidden_type_recv_chan ')' + { + $$ = typ(TCHAN); + $$.Type = $3; + $$.Chan = Cboth; + } +| LCHAN LCOMM hidden_type + { + $$ = typ(TCHAN); + $$.Type = $3; + $$.Chan = Csend; + } + +hidden_type_recv_chan: + LCOMM LCHAN hidden_type + { + $$ = typ(TCHAN); + $$.Type = $3; + $$.Chan = Crecv; + } + +hidden_type_func: + LFUNC '(' ohidden_funarg_list ')' ohidden_funres + { + $$ = functype(nil, $3, $5); + } + +hidden_funarg: + sym hidden_type oliteral + { + $$ = Nod(ODCLFIELD, nil, typenod($2)); + if $1 != nil { + $$.Left = newname($1); + } + $$.Val = $3; + } +| sym LDDD hidden_type oliteral + { + var t *Type + + t = typ(TARRAY); + t.Bound = -1; + t.Type = $3; + + $$ = Nod(ODCLFIELD, nil, typenod(t)); + if $1 != nil { + $$.Left = newname($1); + } + $$.Isddd = true; + $$.Val = $4; + } + +hidden_structdcl: + sym hidden_type oliteral + { + var s *Sym + var p *Pkg + + if $1 != nil && $1.Name != "?" { + $$ = Nod(ODCLFIELD, newname($1), typenod($2)); + $$.Val = $3; + } else { + s = $2.Sym; + if s == nil && Isptr[$2.Etype] { + s = $2.Type.Sym; + } + p = importpkg; + if $1 != nil { + p = $1.Pkg; + } + $$ = embedded(s, p); + $$.Right = typenod($2); + $$.Val = $3; + } + } + +hidden_interfacedcl: + sym '(' ohidden_funarg_list ')' ohidden_funres + { + $$ = Nod(ODCLFIELD, newname($1), typenod(functype(fakethis(), $3, $5))); + } +| hidden_type + { + $$ = Nod(ODCLFIELD, nil, typenod($1)); + } + +ohidden_funres: + { + $$ = nil; + } +| hidden_funres + +hidden_funres: + '(' ohidden_funarg_list ')' + { + $$ = $2; + } +| hidden_type + { + $$ = list1(Nod(ODCLFIELD, nil, typenod($1))); + } + +/* + * importing constants + */ + +hidden_literal: + LLITERAL + { + $$ = nodlit($1); + } +| '-' LLITERAL + { + $$ = nodlit($2); + switch($$.Val.Ctype){ + case CTINT, CTRUNE: + mpnegfix($$.Val.U.(*Mpint)); + break; + case CTFLT: + mpnegflt($$.Val.U.(*Mpflt)); + break; + case CTCPLX: + mpnegflt(&$$.Val.U.(*Mpcplx).Real); + mpnegflt(&$$.Val.U.(*Mpcplx).Imag); + break; + default: + Yyerror("bad negated constant"); + } + } +| sym + { + $$ = oldname(Pkglookup($1.Name, builtinpkg)); + if $$.Op != OLITERAL { + Yyerror("bad constant %v", $$.Sym); + } + } + +hidden_constant: + hidden_literal +| '(' hidden_literal '+' hidden_literal ')' + { + if $2.Val.Ctype == CTRUNE && $4.Val.Ctype == CTINT { + $$ = $2; + mpaddfixfix($2.Val.U.(*Mpint), $4.Val.U.(*Mpint), 0); + break; + } + $4.Val.U.(*Mpcplx).Real = $4.Val.U.(*Mpcplx).Imag; + Mpmovecflt(&$4.Val.U.(*Mpcplx).Imag, 0.0); + $$ = nodcplxlit($2.Val, $4.Val); + } + +hidden_import_list: +| hidden_import_list hidden_import + +hidden_funarg_list: + hidden_funarg + { + $$ = list1($1); + } +| hidden_funarg_list ',' hidden_funarg + { + $$ = list($1, $3); + } + +hidden_structdcl_list: + hidden_structdcl + { + $$ = list1($1); + } +| hidden_structdcl_list ';' hidden_structdcl + { + $$ = list($1, $3); + } + +hidden_interfacedcl_list: + hidden_interfacedcl + { + $$ = list1($1); + } +| hidden_interfacedcl_list ';' hidden_interfacedcl + { + $$ = list($1, $3); + } + +%% +func fixlbrace(lbr int) { + // If the opening brace was an LBODY, + // set up for another one now that we're done. + // See comment in lex.C about loophack. + if lbr == LBODY { + loophack = 1 + } +} diff --git a/src/cmd/compile/internal/gc/gsubr.go b/src/cmd/compile/internal/gc/gsubr.go new file mode 100644 index 0000000000..5ec4587e74 --- /dev/null +++ b/src/cmd/compile/internal/gc/gsubr.go @@ -0,0 +1,827 @@ +// Derived from Inferno utils/6c/txt.c +// http://code.google.com/p/inferno-os/source/browse/utils/6c/txt.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package gc + +import ( + "cmd/internal/obj" + "fmt" + "runtime" + "strings" +) + +var ddumped int + +var dfirst *obj.Prog + +var dpc *obj.Prog + +/* + * Is this node a memory operand? + */ +func Ismem(n *Node) bool { + switch n.Op { + case OITAB, + OSPTR, + OLEN, + OCAP, + OINDREG, + ONAME, + OPARAM, + OCLOSUREVAR: + return true + + case OADDR: + return Thearch.Thechar == '6' || Thearch.Thechar == '9' // because 6g uses PC-relative addressing; TODO(rsc): not sure why 9g too + } + + return false +} + +func Samereg(a *Node, b *Node) bool { + if a == nil || b == nil { + return false + } + if a.Op != OREGISTER { + return false + } + if b.Op != OREGISTER { + return false + } + if a.Reg != b.Reg { + return false + } + return true +} + +func Gbranch(as int, t *Type, likely int) *obj.Prog { + p := Prog(as) + p.To.Type = obj.TYPE_BRANCH + p.To.Val = nil + if as != obj.AJMP && likely != 0 && Thearch.Thechar != '9' && Thearch.Thechar != '7' { + p.From.Type = obj.TYPE_CONST + p.From.Offset = int64(obj.Bool2int(likely > 0)) + } + + if Debug['g'] != 0 { + fmt.Printf("%v\n", p) + } + + return p +} + +func Prog(as int) *obj.Prog { + var p *obj.Prog + + if as == obj.ADATA || as == obj.AGLOBL { + if ddumped != 0 { + Fatal("already dumped data") + } + if dpc == nil { + dpc = Ctxt.NewProg() + dfirst = dpc + } + + p = dpc + dpc = Ctxt.NewProg() + p.Link = dpc + } else { + p = Pc + Pc = Ctxt.NewProg() + Clearp(Pc) + p.Link = Pc + } + + if lineno == 0 { + if Debug['K'] != 0 { + Warn("prog: line 0") + } + } + + p.As = int16(as) + p.Lineno = lineno + return p +} + +func Nodreg(n *Node, t *Type, r int) { + if t == nil { + Fatal("nodreg: t nil") + } + + *n = Node{} + n.Op = OREGISTER + n.Addable = true + ullmancalc(n) + n.Reg = int16(r) + n.Type = t +} + +func Nodindreg(n *Node, t *Type, r int) { + Nodreg(n, t, r) + n.Op = OINDREG +} + +func Afunclit(a *obj.Addr, n *Node) { + if a.Type == obj.TYPE_ADDR && a.Name == obj.NAME_EXTERN { + a.Type = obj.TYPE_MEM + a.Sym = Linksym(n.Sym) + } +} + +func Clearp(p *obj.Prog) { + obj.Nopout(p) + p.As = obj.AEND + p.Pc = int64(pcloc) + pcloc++ +} + +func dumpdata() { + ddumped = 1 + if dfirst == nil { + return + } + newplist() + *Pc = *dfirst + Pc = dpc + Clearp(Pc) +} + +// Fixup instructions after allocauto (formerly compactframe) has moved all autos around. +func fixautoused(p *obj.Prog) { + for lp := &p; ; { + p = *lp + if p == nil { + break + } + if p.As == obj.ATYPE && p.From.Node != nil && p.From.Name == obj.NAME_AUTO && !((p.From.Node).(*Node)).Used { + *lp = p.Link + continue + } + + if (p.As == obj.AVARDEF || p.As == obj.AVARKILL) && p.To.Node != nil && !((p.To.Node).(*Node)).Used { + // Cannot remove VARDEF instruction, because - unlike TYPE handled above - + // VARDEFs are interspersed with other code, and a jump might be using the + // VARDEF as a target. Replace with a no-op instead. A later pass will remove + // the no-ops. + obj.Nopout(p) + + continue + } + + if p.From.Name == obj.NAME_AUTO && p.From.Node != nil { + p.From.Offset += ((p.From.Node).(*Node)).Stkdelta + } + + if p.To.Name == obj.NAME_AUTO && p.To.Node != nil { + p.To.Offset += ((p.To.Node).(*Node)).Stkdelta + } + + lp = &p.Link + } +} + +func ggloblnod(nam *Node) { + p := Thearch.Gins(obj.AGLOBL, nam, nil) + p.Lineno = nam.Lineno + p.From.Sym.Gotype = Linksym(ngotype(nam)) + p.To.Sym = nil + p.To.Type = obj.TYPE_CONST + p.To.Offset = nam.Type.Width + if nam.Name.Readonly { + p.From3.Offset = obj.RODATA + } + if nam.Type != nil && !haspointers(nam.Type) { + p.From3.Offset |= obj.NOPTR + } +} + +func ggloblsym(s *Sym, width int32, flags int16) { + p := Thearch.Gins(obj.AGLOBL, nil, nil) + p.From.Type = obj.TYPE_MEM + p.From.Name = obj.NAME_EXTERN + p.From.Sym = Linksym(s) + if flags&obj.LOCAL != 0 { + p.From.Sym.Local = true + flags &= ^obj.LOCAL + } + p.To.Type = obj.TYPE_CONST + p.To.Offset = int64(width) + p.From3.Offset = int64(flags) +} + +func gjmp(to *obj.Prog) *obj.Prog { + p := Gbranch(obj.AJMP, nil, 0) + if to != nil { + Patch(p, to) + } + return p +} + +func gtrack(s *Sym) { + p := Thearch.Gins(obj.AUSEFIELD, nil, nil) + p.From.Type = obj.TYPE_MEM + p.From.Name = obj.NAME_EXTERN + p.From.Sym = Linksym(s) +} + +func gused(n *Node) { + Thearch.Gins(obj.ANOP, n, nil) // used +} + +func Isfat(t *Type) bool { + if t != nil { + switch t.Etype { + case TSTRUCT, TARRAY, TSTRING, + TINTER: // maybe remove later + return true + } + } + + return false +} + +// Sweep the prog list to mark any used nodes. +func markautoused(p *obj.Prog) { + for ; p != nil; p = p.Link { + if p.As == obj.ATYPE || p.As == obj.AVARDEF || p.As == obj.AVARKILL { + continue + } + + if p.From.Node != nil { + ((p.From.Node).(*Node)).Used = true + } + + if p.To.Node != nil { + ((p.To.Node).(*Node)).Used = true + } + } +} + +// Naddr rewrites a to refer to n. +// It assumes that a is zeroed on entry. +func Naddr(a *obj.Addr, n *Node) { + if n == nil { + return + } + + if n.Type != nil && n.Type.Etype != TIDEAL { + // TODO(rsc): This is undone by the selective clearing of width below, + // to match architectures that were not as aggressive in setting width + // during naddr. Those widths must be cleared to avoid triggering + // failures in gins when it detects real but heretofore latent (and one + // hopes innocuous) type mismatches. + // The type mismatches should be fixed and the clearing below removed. + dowidth(n.Type) + + a.Width = n.Type.Width + } + + switch n.Op { + default: + a := a // copy to let escape into Ctxt.Dconv + Debug['h'] = 1 + Dump("naddr", n) + Fatal("naddr: bad %v %v", Oconv(int(n.Op), 0), Ctxt.Dconv(a)) + + case OREGISTER: + a.Type = obj.TYPE_REG + a.Reg = n.Reg + a.Sym = nil + if Thearch.Thechar == '8' { // TODO(rsc): Never clear a->width. + a.Width = 0 + } + + case OINDREG: + a.Type = obj.TYPE_MEM + a.Reg = n.Reg + a.Sym = Linksym(n.Sym) + a.Offset = n.Xoffset + if a.Offset != int64(int32(a.Offset)) { + Yyerror("offset %d too large for OINDREG", a.Offset) + } + if Thearch.Thechar == '8' { // TODO(rsc): Never clear a->width. + a.Width = 0 + } + + // n->left is PHEAP ONAME for stack parameter. + // compute address of actual parameter on stack. + case OPARAM: + a.Etype = Simtype[n.Left.Type.Etype] + + a.Width = n.Left.Type.Width + a.Offset = n.Xoffset + a.Sym = Linksym(n.Left.Sym) + a.Type = obj.TYPE_MEM + a.Name = obj.NAME_PARAM + a.Node = n.Left.Orig + + case OCLOSUREVAR: + if !Curfn.Func.Needctxt { + Fatal("closurevar without needctxt") + } + a.Type = obj.TYPE_MEM + a.Reg = int16(Thearch.REGCTXT) + a.Sym = nil + a.Offset = n.Xoffset + + case OCFUNC: + Naddr(a, n.Left) + a.Sym = Linksym(n.Left.Sym) + + case ONAME: + a.Etype = 0 + if n.Type != nil { + a.Etype = Simtype[n.Type.Etype] + } + a.Offset = n.Xoffset + s := n.Sym + a.Node = n.Orig + + //if(a->node >= (Node*)&n) + // fatal("stack node"); + if s == nil { + s = Lookup(".noname") + } + if n.Name.Method { + if n.Type != nil { + if n.Type.Sym != nil { + if n.Type.Sym.Pkg != nil { + s = Pkglookup(s.Name, n.Type.Sym.Pkg) + } + } + } + } + + a.Type = obj.TYPE_MEM + switch n.Class { + default: + Fatal("naddr: ONAME class %v %d\n", n.Sym, n.Class) + + case PEXTERN: + a.Name = obj.NAME_EXTERN + + case PAUTO: + a.Name = obj.NAME_AUTO + + case PPARAM, PPARAMOUT: + a.Name = obj.NAME_PARAM + + case PFUNC: + a.Name = obj.NAME_EXTERN + a.Type = obj.TYPE_ADDR + a.Width = int64(Widthptr) + s = funcsym(s) + } + + a.Sym = Linksym(s) + + case OLITERAL: + if Thearch.Thechar == '8' { + a.Width = 0 + } + switch n.Val.Ctype { + default: + Fatal("naddr: const %v", Tconv(n.Type, obj.FmtLong)) + + case CTFLT: + a.Type = obj.TYPE_FCONST + a.Val = mpgetflt(n.Val.U.(*Mpflt)) + + case CTINT, CTRUNE: + a.Sym = nil + a.Type = obj.TYPE_CONST + a.Offset = Mpgetfix(n.Val.U.(*Mpint)) + + case CTSTR: + datagostring(n.Val.U.(string), a) + + case CTBOOL: + a.Sym = nil + a.Type = obj.TYPE_CONST + a.Offset = int64(obj.Bool2int(n.Val.U.(bool))) + + case CTNIL: + a.Sym = nil + a.Type = obj.TYPE_CONST + a.Offset = 0 + } + + case OADDR: + Naddr(a, n.Left) + a.Etype = uint8(Tptr) + if Thearch.Thechar != '5' && Thearch.Thechar != '7' && Thearch.Thechar != '9' { // TODO(rsc): Do this even for arm, ppc64. + a.Width = int64(Widthptr) + } + if a.Type != obj.TYPE_MEM { + a := a // copy to let escape into Ctxt.Dconv + Fatal("naddr: OADDR %v (from %v)", Ctxt.Dconv(a), Oconv(int(n.Left.Op), 0)) + } + a.Type = obj.TYPE_ADDR + + // itable of interface value + case OITAB: + Naddr(a, n.Left) + + if a.Type == obj.TYPE_CONST && a.Offset == 0 { + break // itab(nil) + } + a.Etype = uint8(Tptr) + a.Width = int64(Widthptr) + + // pointer in a string or slice + case OSPTR: + Naddr(a, n.Left) + + if a.Type == obj.TYPE_CONST && a.Offset == 0 { + break // ptr(nil) + } + a.Etype = Simtype[Tptr] + a.Offset += int64(Array_array) + a.Width = int64(Widthptr) + + // len of string or slice + case OLEN: + Naddr(a, n.Left) + + if a.Type == obj.TYPE_CONST && a.Offset == 0 { + break // len(nil) + } + a.Etype = Simtype[TUINT] + a.Offset += int64(Array_nel) + if Thearch.Thechar != '5' { // TODO(rsc): Do this even on arm. + a.Width = int64(Widthint) + } + + // cap of string or slice + case OCAP: + Naddr(a, n.Left) + + if a.Type == obj.TYPE_CONST && a.Offset == 0 { + break // cap(nil) + } + a.Etype = Simtype[TUINT] + a.Offset += int64(Array_cap) + if Thearch.Thechar != '5' { // TODO(rsc): Do this even on arm. + a.Width = int64(Widthint) + } + } + return +} + +func newplist() *obj.Plist { + pl := obj.Linknewplist(Ctxt) + + Pc = Ctxt.NewProg() + Clearp(Pc) + pl.Firstpc = Pc + + return pl +} + +func nodarg(t *Type, fp int) *Node { + var n *Node + + // entire argument struct, not just one arg + if t.Etype == TSTRUCT && t.Funarg != 0 { + n = Nod(ONAME, nil, nil) + n.Sym = Lookup(".args") + n.Type = t + var savet Iter + first := Structfirst(&savet, &t) + if first == nil { + Fatal("nodarg: bad struct") + } + if first.Width == BADWIDTH { + Fatal("nodarg: offset not computed for %v", t) + } + n.Xoffset = first.Width + n.Addable = true + goto fp + } + + if t.Etype != TFIELD { + Fatal("nodarg: not field %v", t) + } + + if fp == 1 { + var n *Node + for l := Curfn.Func.Dcl; l != nil; l = l.Next { + n = l.N + if (n.Class == PPARAM || n.Class == PPARAMOUT) && !isblanksym(t.Sym) && n.Sym == t.Sym { + return n + } + } + } + + n = Nod(ONAME, nil, nil) + n.Type = t.Type + n.Sym = t.Sym + + if t.Width == BADWIDTH { + Fatal("nodarg: offset not computed for %v", t) + } + n.Xoffset = t.Width + n.Addable = true + n.Orig = t.Nname + + // Rewrite argument named _ to __, + // or else the assignment to _ will be + // discarded during code generation. +fp: + if isblank(n) { + n.Sym = Lookup("__") + } + + switch fp { + case 0: // output arg + n.Op = OINDREG + + n.Reg = int16(Thearch.REGSP) + if HasLinkRegister() { + n.Xoffset += int64(Ctxt.Arch.Ptrsize) + } + + case 1: // input arg + n.Class = PPARAM + + case 2: // offset output arg + Fatal("shouldn't be used") + } + + n.Typecheck = 1 + return n +} + +func Patch(p *obj.Prog, to *obj.Prog) { + if p.To.Type != obj.TYPE_BRANCH { + Fatal("patch: not a branch") + } + p.To.Val = to + p.To.Offset = to.Pc +} + +func unpatch(p *obj.Prog) *obj.Prog { + if p.To.Type != obj.TYPE_BRANCH { + Fatal("unpatch: not a branch") + } + q, _ := p.To.Val.(*obj.Prog) + p.To.Val = nil + p.To.Offset = 0 + return q +} + +var reg [100]int // count of references to reg +var regstk [100][]byte // allocation sites, when -v is given + +func ginit() { + for r := range reg { + reg[r] = 1 + } + + for r := Thearch.REGMIN; r <= Thearch.REGMAX; r++ { + reg[r-Thearch.REGMIN] = 0 + } + for r := Thearch.FREGMIN; r <= Thearch.FREGMAX; r++ { + reg[r-Thearch.REGMIN] = 0 + } + + for _, r := range Thearch.ReservedRegs { + reg[r-Thearch.REGMIN] = 1 + } +} + +func gclean() { + for _, r := range Thearch.ReservedRegs { + reg[r-Thearch.REGMIN]-- + } + + for r := Thearch.REGMIN; r <= Thearch.REGMAX; r++ { + n := reg[r-Thearch.REGMIN] + if n != 0 { + 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 { + if Debug['v'] != 0 { + Regdump() + } + Yyerror("reg %v left allocated", obj.Rconv(r)) + } + } +} + +func Anyregalloc() bool { + n := 0 + for r := Thearch.REGMIN; r <= Thearch.REGMAX; r++ { + if reg[r-Thearch.REGMIN] == 0 { + n++ + } + } + return n > len(Thearch.ReservedRegs) +} + +/* + * allocate register of type t, leave in n. + * if o != N, o may be reusable register. + * caller must Regfree(n). + */ +func Regalloc(n *Node, t *Type, o *Node) { + if t == nil { + Fatal("regalloc: t nil") + } + et := int(Simtype[t.Etype]) + if Ctxt.Arch.Regsize == 4 && (et == TINT64 || et == TUINT64) { + Fatal("regalloc 64bit") + } + + var i int +Switch: + switch et { + default: + Fatal("regalloc: unknown type %v", t) + + case TINT8, TUINT8, TINT16, TUINT16, TINT32, TUINT32, TINT64, TUINT64, TPTR32, TPTR64, TBOOL: + if o != nil && o.Op == OREGISTER { + i = int(o.Reg) + if Thearch.REGMIN <= i && i <= Thearch.REGMAX { + break Switch + } + } + for i = Thearch.REGMIN; i <= Thearch.REGMAX; i++ { + if reg[i-Thearch.REGMIN] == 0 { + break Switch + } + } + Flusherrors() + Regdump() + Fatal("out of fixed registers") + + case TFLOAT32, TFLOAT64: + if Thearch.Use387 { + i = Thearch.FREGMIN // x86.REG_F0 + break Switch + } + if o != nil && o.Op == OREGISTER { + i = int(o.Reg) + if Thearch.FREGMIN <= i && i <= Thearch.FREGMAX { + break Switch + } + } + for i = Thearch.FREGMIN; i <= Thearch.FREGMAX; i++ { + if reg[i-Thearch.REGMIN] == 0 { // note: REGMIN, not FREGMIN + break Switch + } + } + Flusherrors() + Regdump() + Fatal("out of floating registers") + + case TCOMPLEX64, TCOMPLEX128: + Tempname(n, t) + return + } + + ix := i - Thearch.REGMIN + if reg[ix] == 0 && Debug['v'] > 0 { + if regstk[ix] == nil { + regstk[ix] = make([]byte, 4096) + } + stk := regstk[ix] + n := runtime.Stack(stk[:cap(stk)], false) + regstk[ix] = stk[:n] + } + reg[ix]++ + Nodreg(n, t, i) +} + +func Regfree(n *Node) { + if n.Op == ONAME { + return + } + if n.Op != OREGISTER && n.Op != OINDREG { + Fatal("regfree: not a register") + } + i := int(n.Reg) + if i == Thearch.REGSP { + return + } + switch { + case Thearch.REGMIN <= i && i <= Thearch.REGMAX, + Thearch.FREGMIN <= i && i <= Thearch.FREGMAX: + // ok + default: + Fatal("regfree: reg out of range") + } + + i -= Thearch.REGMIN + if reg[i] <= 0 { + Fatal("regfree: reg not allocated") + } + reg[i]-- + if reg[i] == 0 { + regstk[i] = regstk[i][:0] + } +} + +// Reginuse reports whether r is in use. +func Reginuse(r int) bool { + switch { + case Thearch.REGMIN <= r && r <= Thearch.REGMAX, + Thearch.FREGMIN <= r && r <= Thearch.FREGMAX: + // ok + default: + Fatal("reginuse: reg out of range") + } + + return reg[r-Thearch.REGMIN] > 0 +} + +// Regrealloc(n) undoes the effect of Regfree(n), +// so that a register can be given up but then reclaimed. +func Regrealloc(n *Node) { + if n.Op != OREGISTER && n.Op != OINDREG { + Fatal("regrealloc: not a register") + } + i := int(n.Reg) + if i == Thearch.REGSP { + return + } + switch { + case Thearch.REGMIN <= i && i <= Thearch.REGMAX, + Thearch.FREGMIN <= i && i <= Thearch.FREGMAX: + // ok + default: + Fatal("regrealloc: reg out of range") + } + + i -= Thearch.REGMIN + if reg[i] == 0 && Debug['v'] > 0 { + if regstk[i] == nil { + regstk[i] = make([]byte, 4096) + } + stk := regstk[i] + n := runtime.Stack(stk[:cap(stk)], false) + regstk[i] = stk[:n] + } + reg[i]++ +} + +func Regdump() { + if Debug['v'] == 0 { + fmt.Printf("run compiler with -v for register allocation sites\n") + return + } + + dump := func(r int) { + stk := regstk[r-Thearch.REGMIN] + if len(stk) == 0 { + return + } + fmt.Printf("reg %v allocated at:\n", obj.Rconv(r)) + fmt.Printf("\t%s\n", strings.Replace(strings.TrimSpace(string(stk)), "\n", "\n\t", -1)) + } + + for r := Thearch.REGMIN; r <= Thearch.REGMAX; r++ { + if reg[r-Thearch.REGMIN] != 0 { + dump(r) + } + } + for r := Thearch.FREGMIN; r <= Thearch.FREGMAX; r++ { + if reg[r-Thearch.REGMIN] == 0 { + dump(r) + } + } +} diff --git a/src/cmd/compile/internal/gc/init.go b/src/cmd/compile/internal/gc/init.go new file mode 100644 index 0000000000..b5d1e505a5 --- /dev/null +++ b/src/cmd/compile/internal/gc/init.go @@ -0,0 +1,189 @@ +// Copyright 2009 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 gc + +// case OADD: +// if(n->right->op == OLITERAL) { +// v = n->right->vconst; +// naddr(n->left, a, canemitcode); +// } else +// if(n->left->op == OLITERAL) { +// v = n->left->vconst; +// naddr(n->right, a, canemitcode); +// } else +// goto bad; +// a->offset += v; +// break; + +/* + * a function named init is a special case. + * it is called by the initialization before + * main is run. to make it unique within a + * package and also uncallable, the name, + * normally "pkg.init", is altered to "pkg.init.1". + */ + +var renameinit_initgen int + +func renameinit() *Sym { + renameinit_initgen++ + return Lookupf("init.%d", renameinit_initgen) +} + +/* + * hand-craft the following initialization code + * var initdone· uint8 (1) + * func init() (2) + * if initdone· != 0 { (3) + * if initdone· == 2 (4) + * return + * throw(); (5) + * } + * initdone· = 1; (6) + * // over all matching imported symbols + * .init() (7) + * { } (8) + * init.() // if any (9) + * initdone· = 2; (10) + * return (11) + * } + */ +func anyinit(n *NodeList) bool { + // are there any interesting init statements + for l := n; l != nil; l = l.Next { + switch l.N.Op { + case ODCLFUNC, ODCLCONST, ODCLTYPE, OEMPTY: + break + + case OAS, OASWB: + if isblank(l.N.Left) && candiscard(l.N.Right) { + break + } + fallthrough + + // fall through + default: + return true + } + } + + // is this main + if localpkg.Name == "main" { + return true + } + + // is there an explicit init function + s := Lookup("init.1") + + if s.Def != nil { + return true + } + + // are there any imported init functions + for _, s := range initSyms { + if s.Def != nil { + return true + } + } + + // then none + return false +} + +func fninit(n *NodeList) { + if Debug['A'] != 0 { + // sys.go or unsafe.go during compiler build + return + } + + n = initfix(n) + if !anyinit(n) { + return + } + + var r *NodeList + + // (1) + gatevar := newname(Lookup("initdone·")) + addvar(gatevar, Types[TUINT8], PEXTERN) + + // (2) + Maxarg = 0 + + fn := Nod(ODCLFUNC, nil, nil) + initsym := Lookup("init") + fn.Nname = newname(initsym) + fn.Nname.Defn = fn + fn.Nname.Ntype = Nod(OTFUNC, nil, nil) + declare(fn.Nname, PFUNC) + funchdr(fn) + + // (3) + a := Nod(OIF, nil, nil) + + a.Ntest = Nod(ONE, gatevar, Nodintconst(0)) + r = list(r, a) + + // (4) + b := Nod(OIF, nil, nil) + + b.Ntest = Nod(OEQ, gatevar, Nodintconst(2)) + b.Nbody = list1(Nod(ORETURN, nil, nil)) + a.Nbody = list1(b) + + // (5) + b = syslook("throwinit", 0) + + b = Nod(OCALL, b, nil) + a.Nbody = list(a.Nbody, b) + + // (6) + a = Nod(OAS, gatevar, Nodintconst(1)) + + r = list(r, a) + + // (7) + for _, s := range initSyms { + if s.Def != nil && s != initsym { + // could check that it is fn of no args/returns + a = Nod(OCALL, s.Def, nil) + r = list(r, a) + } + } + + // (8) + r = concat(r, n) + + // (9) + // could check that it is fn of no args/returns + for i := 1; ; i++ { + s := Lookupf("init.%d", i) + if s.Def == nil { + break + } + a = Nod(OCALL, s.Def, nil) + r = list(r, a) + } + + // (10) + a = Nod(OAS, gatevar, Nodintconst(2)) + + r = list(r, a) + + // (11) + a = Nod(ORETURN, nil, nil) + + r = list(r, a) + exportsym(fn.Nname) + + fn.Nbody = r + funcbody(fn) + + Curfn = fn + typecheck(&fn, Etop) + typechecklist(r, Etop) + Curfn = nil + funccompile(fn) +} diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go new file mode 100644 index 0000000000..22a5d3d9fe --- /dev/null +++ b/src/cmd/compile/internal/gc/inl.go @@ -0,0 +1,1008 @@ +// Copyright 2011 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. +// +// The inlining facility makes 2 passes: first caninl determines which +// functions are suitable for inlining, and for those that are it +// saves a copy of the body. Then inlcalls walks each function body to +// expand calls to inlinable functions. +// +// The debug['l'] flag controls the agressiveness. Note that main() swaps level 0 and 1, +// making 1 the default and -l disable. -ll and more is useful to flush out bugs. +// These additional levels (beyond -l) may be buggy and are not supported. +// 0: disabled +// 1: 40-nodes leaf functions, oneliners, lazy typechecking (default) +// 2: early typechecking of all imported bodies +// 3: allow variadic functions +// 4: allow non-leaf functions , (breaks runtime.Caller) +// +// At some point this may get another default and become switch-offable with -N. +// +// The debug['m'] flag enables diagnostic output. a single -m is useful for verifying +// which calls get inlined or not, more is for debugging, and may go away at any point. +// +// TODO: +// - inline functions with ... args +// - handle T.meth(f()) with func f() (t T, arg, arg, ) + +package gc + +import ( + "cmd/internal/obj" + "fmt" +) + +// Used by caninl. + +// Used by inlcalls + +// Used during inlsubst[list] +var inlfn *Node // function currently being inlined + +var inlretlabel *Node // target of the goto substituted in place of a return + +var inlretvars *NodeList // temp out variables + +// Get the function's package. For ordinary functions it's on the ->sym, but for imported methods +// the ->sym can be re-used in the local package, so peel it off the receiver's type. +func fnpkg(fn *Node) *Pkg { + if fn.Type.Thistuple != 0 { + // method + rcvr := getthisx(fn.Type).Type.Type + + if Isptr[rcvr.Etype] { + rcvr = rcvr.Type + } + if rcvr.Sym == nil { + Fatal("receiver with no sym: [%v] %v (%v)", fn.Sym, Nconv(fn, obj.FmtLong), rcvr) + } + return rcvr.Sym.Pkg + } + + // non-method + return fn.Sym.Pkg +} + +// Lazy typechecking of imported bodies. For local functions, caninl will set ->typecheck +// because they're a copy of an already checked body. +func typecheckinl(fn *Node) { + lno := int(setlineno(fn)) + + // typecheckinl is only for imported functions; + // their bodies may refer to unsafe as long as the package + // was marked safe during import (which was checked then). + // the ->inl of a local function has been typechecked before caninl copied it. + pkg := fnpkg(fn) + + if pkg == localpkg || pkg == nil { + return // typecheckinl on local function + } + + if Debug['m'] > 2 { + fmt.Printf("typecheck import [%v] %v { %v }\n", fn.Sym, Nconv(fn, obj.FmtLong), Hconv(fn.Func.Inl, obj.FmtSharp)) + } + + save_safemode := safemode + safemode = 0 + + savefn := Curfn + Curfn = fn + typechecklist(fn.Func.Inl, Etop) + Curfn = savefn + + safemode = save_safemode + + lineno = int32(lno) +} + +// Caninl determines whether fn is inlineable. +// If so, caninl saves fn->nbody in fn->inl and substitutes it with a copy. +// fn and ->nbody will already have been typechecked. +func caninl(fn *Node) { + if fn.Op != ODCLFUNC { + Fatal("caninl %v", fn) + } + if fn.Nname == nil { + Fatal("caninl no nname %v", Nconv(fn, obj.FmtSign)) + } + + // If fn has no body (is defined outside of Go), cannot inline it. + if fn.Nbody == nil { + return + } + + if fn.Typecheck == 0 { + Fatal("caninl on non-typechecked function %v", fn) + } + + // can't handle ... args yet + if Debug['l'] < 3 { + for t := fn.Type.Type.Down.Down.Type; t != nil; t = t.Down { + if t.Isddd { + return + } + } + } + + // Runtime package must not be race instrumented. + // Racewalk skips runtime package. However, some runtime code can be + // inlined into other packages and instrumented there. To avoid this, + // we disable inlining of runtime functions in race mode. + // The example that we observed is inlining of LockOSThread, + // which lead to false race reports on m contents. + if flag_race != 0 && myimportpath == "runtime" { + return + } + + const maxBudget = 80 + budget := maxBudget // allowed hairyness + if ishairylist(fn.Nbody, &budget) || budget < 0 { + return + } + + savefn := Curfn + Curfn = fn + + fn.Nname.Func.Inl = fn.Nbody + fn.Nbody = inlcopylist(fn.Nname.Func.Inl) + fn.Nname.Func.Inldcl = inlcopylist(fn.Nname.Defn.Func.Dcl) + fn.Nname.Func.InlCost = int32(maxBudget - budget) + + // hack, TODO, check for better way to link method nodes back to the thing with the ->inl + // this is so export can find the body of a method + fn.Type.Nname = fn.Nname + + if Debug['m'] > 1 { + fmt.Printf("%v: can inline %v as: %v { %v }\n", fn.Line(), Nconv(fn.Nname, obj.FmtSharp), Tconv(fn.Type, obj.FmtSharp), Hconv(fn.Nname.Func.Inl, obj.FmtSharp)) + } else if Debug['m'] != 0 { + fmt.Printf("%v: can inline %v\n", fn.Line(), fn.Nname) + } + + Curfn = savefn +} + +// Look for anything we want to punt on. +func ishairylist(ll *NodeList, budget *int) bool { + for ; ll != nil; ll = ll.Next { + if ishairy(ll.N, budget) { + return true + } + } + return false +} + +func ishairy(n *Node, budget *int) bool { + if n == nil { + return false + } + + switch n.Op { + // Call is okay if inlinable and we have the budget for the body. + case OCALLFUNC: + if n.Left.Func != nil && n.Left.Func.Inl != nil { + *budget -= int(n.Left.Func.InlCost) + break + } + if n.Left.Op == ONAME && n.Left.Left != nil && n.Left.Left.Op == OTYPE && n.Left.Right != nil && n.Left.Right.Op == ONAME { // methods called as functions + if n.Left.Sym.Def != nil && n.Left.Sym.Def.Func.Inl != nil { + *budget -= int(n.Left.Sym.Def.Func.InlCost) + break + } + } + if Debug['l'] < 4 { + return true + } + + // Call is okay if inlinable and we have the budget for the body. + case OCALLMETH: + if n.Left.Type == nil { + Fatal("no function type for [%p] %v\n", n.Left, Nconv(n.Left, obj.FmtSign)) + } + if n.Left.Type.Nname == nil { + Fatal("no function definition for [%p] %v\n", n.Left.Type, Tconv(n.Left.Type, obj.FmtSign)) + } + if n.Left.Type.Nname.Func.Inl != nil { + *budget -= int(n.Left.Type.Nname.Func.InlCost) + break + } + if Debug['l'] < 4 { + return true + } + + // Things that are too hairy, irrespective of the budget + case OCALL, OCALLINTER, OPANIC, ORECOVER: + if Debug['l'] < 4 { + return true + } + + case OCLOSURE, + OCALLPART, + ORANGE, + OFOR, + OSELECT, + OSWITCH, + OPROC, + ODEFER, + ODCLTYPE, // can't print yet + ODCLCONST, // can't print yet + ORETJMP: + return true + } + + (*budget)-- + + return *budget < 0 || ishairy(n.Left, budget) || ishairy(n.Right, budget) || ishairylist(n.List, budget) || ishairylist(n.Rlist, budget) || ishairylist(n.Ninit, budget) || ishairy(n.Ntest, budget) || ishairy(n.Nincr, budget) || ishairylist(n.Nbody, budget) || ishairylist(n.Nelse, budget) +} + +// Inlcopy and inlcopylist recursively copy the body of a function. +// Any name-like node of non-local class is marked for re-export by adding it to +// the exportlist. +func inlcopylist(ll *NodeList) *NodeList { + var l *NodeList + for ; ll != nil; ll = ll.Next { + l = list(l, inlcopy(ll.N)) + } + return l +} + +func inlcopy(n *Node) *Node { + if n == nil { + return nil + } + + switch n.Op { + case ONAME, OTYPE, OLITERAL: + return n + } + + m := Nod(OXXX, nil, nil) + *m = *n + if m.Func != nil { + m.Func.Inl = nil + } + m.Left = inlcopy(n.Left) + m.Right = inlcopy(n.Right) + m.List = inlcopylist(n.List) + m.Rlist = inlcopylist(n.Rlist) + m.Ninit = inlcopylist(n.Ninit) + m.Ntest = inlcopy(n.Ntest) + m.Nincr = inlcopy(n.Nincr) + m.Nbody = inlcopylist(n.Nbody) + m.Nelse = inlcopylist(n.Nelse) + + return m +} + +// Inlcalls/nodelist/node walks fn's statements and expressions and substitutes any +// calls made to inlineable functions. This is the external entry point. +func inlcalls(fn *Node) { + savefn := Curfn + Curfn = fn + inlnode(&fn) + if fn != Curfn { + Fatal("inlnode replaced curfn") + } + Curfn = savefn +} + +// Turn an OINLCALL into a statement. +func inlconv2stmt(n *Node) { + n.Op = OBLOCK + + // n->ninit stays + n.List = n.Nbody + + n.Nbody = nil + n.Rlist = nil +} + +// Turn an OINLCALL into a single valued expression. +func inlconv2expr(np **Node) { + n := *np + r := n.Rlist.N + addinit(&r, concat(n.Ninit, n.Nbody)) + *np = r +} + +// Turn the rlist (with the return values) of the OINLCALL in +// n into an expression list lumping the ninit and body +// containing the inlined statements on the first list element so +// order will be preserved Used in return, oas2func and call +// statements. +func inlconv2list(n *Node) *NodeList { + if n.Op != OINLCALL || n.Rlist == nil { + Fatal("inlconv2list %v\n", Nconv(n, obj.FmtSign)) + } + + l := n.Rlist + addinit(&l.N, concat(n.Ninit, n.Nbody)) + return l +} + +func inlnodelist(l *NodeList) { + for ; l != nil; l = l.Next { + inlnode(&l.N) + } +} + +// inlnode recurses over the tree to find inlineable calls, which will +// be turned into OINLCALLs by mkinlcall. When the recursion comes +// back up will examine left, right, list, rlist, ninit, ntest, nincr, +// nbody and nelse and use one of the 4 inlconv/glue functions above +// to turn the OINLCALL into an expression, a statement, or patch it +// in to this nodes list or rlist as appropriate. +// NOTE it makes no sense to pass the glue functions down the +// recursion to the level where the OINLCALL gets created because they +// have to edit /this/ n, so you'd have to push that one down as well, +// but then you may as well do it here. so this is cleaner and +// shorter and less complicated. +func inlnode(np **Node) { + if *np == nil { + return + } + + n := *np + + switch n.Op { + // inhibit inlining of their argument + case ODEFER, OPROC: + switch n.Left.Op { + case OCALLFUNC, OCALLMETH: + n.Left.Etype = n.Op + } + fallthrough + + // TODO do them here (or earlier), + // so escape analysis can avoid more heapmoves. + case OCLOSURE: + return + } + + lno := int(setlineno(n)) + + inlnodelist(n.Ninit) + for l := n.Ninit; l != nil; l = l.Next { + if l.N.Op == OINLCALL { + inlconv2stmt(l.N) + } + } + + inlnode(&n.Left) + if n.Left != nil && n.Left.Op == OINLCALL { + inlconv2expr(&n.Left) + } + + inlnode(&n.Right) + if n.Right != nil && n.Right.Op == OINLCALL { + inlconv2expr(&n.Right) + } + + inlnodelist(n.List) + switch n.Op { + case OBLOCK: + for l := n.List; l != nil; l = l.Next { + if l.N.Op == OINLCALL { + inlconv2stmt(l.N) + } + } + + // if we just replaced arg in f(arg()) or return arg with an inlined call + // and arg returns multiple values, glue as list + case ORETURN, + OCALLFUNC, + OCALLMETH, + OCALLINTER, + OAPPEND, + OCOMPLEX: + if count(n.List) == 1 && n.List.N.Op == OINLCALL && count(n.List.N.Rlist) > 1 { + n.List = inlconv2list(n.List.N) + break + } + fallthrough + + default: + for l := n.List; l != nil; l = l.Next { + if l.N.Op == OINLCALL { + inlconv2expr(&l.N) + } + } + } + + inlnodelist(n.Rlist) + switch n.Op { + case OAS2FUNC: + if n.Rlist.N.Op == OINLCALL { + n.Rlist = inlconv2list(n.Rlist.N) + n.Op = OAS2 + n.Typecheck = 0 + typecheck(np, Etop) + break + } + fallthrough + + default: + for l := n.Rlist; l != nil; l = l.Next { + if l.N.Op == OINLCALL { + inlconv2expr(&l.N) + } + } + } + + inlnode(&n.Ntest) + if n.Ntest != nil && n.Ntest.Op == OINLCALL { + inlconv2expr(&n.Ntest) + } + + inlnode(&n.Nincr) + if n.Nincr != nil && n.Nincr.Op == OINLCALL { + inlconv2stmt(n.Nincr) + } + + inlnodelist(n.Nbody) + for l := n.Nbody; l != nil; l = l.Next { + if l.N.Op == OINLCALL { + inlconv2stmt(l.N) + } + } + + inlnodelist(n.Nelse) + for l := n.Nelse; l != nil; l = l.Next { + if l.N.Op == OINLCALL { + inlconv2stmt(l.N) + } + } + + // with all the branches out of the way, it is now time to + // transmogrify this node itself unless inhibited by the + // switch at the top of this function. + switch n.Op { + case OCALLFUNC, OCALLMETH: + if n.Etype == OPROC || n.Etype == ODEFER { + return + } + } + + switch n.Op { + case OCALLFUNC: + if Debug['m'] > 3 { + fmt.Printf("%v:call to func %v\n", n.Line(), Nconv(n.Left, obj.FmtSign)) + } + if n.Left.Func != nil && n.Left.Func.Inl != nil { // normal case + mkinlcall(np, n.Left, n.Isddd) + } else if n.Left.Op == ONAME && n.Left.Left != nil && n.Left.Left.Op == OTYPE && n.Left.Right != nil && n.Left.Right.Op == ONAME { // methods called as functions + if n.Left.Sym.Def != nil { + mkinlcall(np, n.Left.Sym.Def, n.Isddd) + } + } + + case OCALLMETH: + if Debug['m'] > 3 { + fmt.Printf("%v:call to meth %v\n", n.Line(), Nconv(n.Left.Right, obj.FmtLong)) + } + + // typecheck should have resolved ODOTMETH->type, whose nname points to the actual function. + if n.Left.Type == nil { + Fatal("no function type for [%p] %v\n", n.Left, Nconv(n.Left, obj.FmtSign)) + } + + if n.Left.Type.Nname == nil { + Fatal("no function definition for [%p] %v\n", n.Left.Type, Tconv(n.Left.Type, obj.FmtSign)) + } + + mkinlcall(np, n.Left.Type.Nname, n.Isddd) + } + + lineno = int32(lno) +} + +func mkinlcall(np **Node, fn *Node, isddd bool) { + save_safemode := safemode + + // imported functions may refer to unsafe as long as the + // package was marked safe during import (already checked). + pkg := fnpkg(fn) + + if pkg != localpkg && pkg != nil { + safemode = 0 + } + mkinlcall1(np, fn, isddd) + safemode = save_safemode +} + +func tinlvar(t *Type) *Node { + if t.Nname != nil && !isblank(t.Nname) { + if t.Nname.Name.Inlvar == nil { + Fatal("missing inlvar for %v\n", t.Nname) + } + return t.Nname.Name.Inlvar + } + + typecheck(&nblank, Erv|Easgn) + return nblank +} + +var inlgen int + +// if *np is a call, and fn is a function with an inlinable body, substitute *np with an OINLCALL. +// On return ninit has the parameter assignments, the nbody is the +// inlined function body and list, rlist contain the input, output +// parameters. +func mkinlcall1(np **Node, fn *Node, isddd bool) { + // For variadic fn. + if fn.Func.Inl == nil { + return + } + + if fn == Curfn || fn.Defn == Curfn { + return + } + + if Debug['l'] < 2 { + typecheckinl(fn) + } + + n := *np + + // Bingo, we have a function node, and it has an inlineable body + if Debug['m'] > 1 { + fmt.Printf("%v: inlining call to %v %v { %v }\n", n.Line(), fn.Sym, Tconv(fn.Type, obj.FmtSharp), Hconv(fn.Func.Inl, obj.FmtSharp)) + } else if Debug['m'] != 0 { + fmt.Printf("%v: inlining call to %v\n", n.Line(), fn) + } + + if Debug['m'] > 2 { + fmt.Printf("%v: Before inlining: %v\n", n.Line(), Nconv(n, obj.FmtSign)) + } + + saveinlfn := inlfn + inlfn = fn + + ninit := n.Ninit + + //dumplist("ninit pre", ninit); + + var dcl *NodeList + if fn.Defn != nil { // local function + dcl = fn.Func.Inldcl // imported function + } else { + dcl = fn.Func.Dcl + } + + inlretvars = nil + i := 0 + + // Make temp names to use instead of the originals + for ll := dcl; ll != nil; ll = ll.Next { + if ll.N.Class == PPARAMOUT { // return values handled below. + continue + } + if ll.N.Op == ONAME { + ll.N.Name.Inlvar = inlvar(ll.N) + + // Typecheck because inlvar is not necessarily a function parameter. + typecheck(&ll.N.Name.Inlvar, Erv) + + if ll.N.Class&^PHEAP != PAUTO { + ninit = list(ninit, Nod(ODCL, ll.N.Name.Inlvar, nil)) // otherwise gen won't emit the allocations for heapallocs + } + } + } + + // temporaries for return values. + var m *Node + for t := getoutargx(fn.Type).Type; t != nil; t = t.Down { + if t != nil && t.Nname != nil && !isblank(t.Nname) { + m = inlvar(t.Nname) + typecheck(&m, Erv) + t.Nname.Name.Inlvar = m + } else { + // anonymous return values, synthesize names for use in assignment that replaces return + m = retvar(t, i) + i++ + } + + ninit = list(ninit, Nod(ODCL, m, nil)) + inlretvars = list(inlretvars, m) + } + + // assign receiver. + var as *Node + if fn.Type.Thistuple != 0 && n.Left.Op == ODOTMETH { + // method call with a receiver. + t := getthisx(fn.Type).Type + + 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 { + Fatal("method call without receiver: %v", Nconv(n, obj.FmtSign)) + } + if t == nil { + Fatal("method call unknown receiver type: %v", Nconv(n, obj.FmtSign)) + } + as = Nod(OAS, tinlvar(t), n.Left.Left) + if as != nil { + typecheck(&as, Etop) + ninit = list(ninit, as) + } + } + + // check if inlined function is variadic. + variadic := false + + var varargtype *Type + varargcount := 0 + for t := fn.Type.Type.Down.Down.Type; t != nil; t = t.Down { + if t.Isddd { + variadic = true + varargtype = t.Type + } + } + + // but if argument is dotted too forget about variadicity. + if variadic && isddd { + variadic = false + } + + // check if argument is actually a returned tuple from call. + multiret := 0 + + if n.List != nil && n.List.Next == nil { + switch n.List.N.Op { + case OCALL, OCALLFUNC, OCALLINTER, OCALLMETH: + if n.List.N.Left.Type.Outtuple > 1 { + multiret = n.List.N.Left.Type.Outtuple - 1 + } + } + } + + if variadic { + varargcount = count(n.List) + multiret + if n.Left.Op != ODOTMETH { + varargcount -= fn.Type.Thistuple + } + varargcount -= fn.Type.Intuple - 1 + } + + // assign arguments to the parameters' temp names + as = Nod(OAS2, nil, nil) + + as.Rlist = n.List + ll := n.List + + // TODO: if len(nlist) == 1 but multiple args, check that n->list->n is a call? + if fn.Type.Thistuple != 0 && n.Left.Op != ODOTMETH { + // non-method call to method + if n.List == nil { + Fatal("non-method call to method without first arg: %v", Nconv(n, obj.FmtSign)) + } + + // append receiver inlvar to LHS. + t := getthisx(fn.Type).Type + + 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 { + Fatal("method call unknown receiver type: %v", Nconv(n, obj.FmtSign)) + } + as.List = list(as.List, tinlvar(t)) + ll = ll.Next // track argument count. + } + + // append ordinary arguments to LHS. + chkargcount := n.List != nil && n.List.Next != nil + + var vararg *Node // the slice argument to a variadic call + var varargs *NodeList // the list of LHS names to put in vararg. + if !chkargcount { + // 0 or 1 expression on RHS. + var i int + for t := getinargx(fn.Type).Type; t != nil; t = t.Down { + if variadic && t.Isddd { + vararg = tinlvar(t) + for i = 0; i < varargcount && ll != nil; i++ { + m = argvar(varargtype, i) + varargs = list(varargs, m) + as.List = list(as.List, m) + } + + break + } + + as.List = list(as.List, tinlvar(t)) + } + } else { + // match arguments except final variadic (unless the call is dotted itself) + var t *Type + for t = getinargx(fn.Type).Type; t != nil; { + if ll == nil { + break + } + if variadic && t.Isddd { + break + } + as.List = list(as.List, tinlvar(t)) + t = t.Down + ll = ll.Next + } + + // match varargcount arguments with variadic parameters. + if variadic && t != nil && t.Isddd { + vararg = tinlvar(t) + var i int + for i = 0; i < varargcount && ll != nil; i++ { + m = argvar(varargtype, i) + varargs = list(varargs, m) + as.List = list(as.List, m) + ll = ll.Next + } + + if i == varargcount { + t = t.Down + } + } + + if ll != nil || t != nil { + Fatal("arg count mismatch: %v vs %v\n", Tconv(getinargx(fn.Type), obj.FmtSharp), Hconv(n.List, obj.FmtComma)) + } + } + + if as.Rlist != nil { + typecheck(&as, Etop) + ninit = list(ninit, as) + } + + // turn the variadic args into a slice. + if variadic { + as = Nod(OAS, vararg, nil) + if varargcount == 0 { + as.Right = nodnil() + as.Right.Type = varargtype + } else { + vararrtype := typ(TARRAY) + vararrtype.Type = varargtype.Type + vararrtype.Bound = int64(varargcount) + + as.Right = Nod(OCOMPLIT, nil, typenod(varargtype)) + as.Right.List = varargs + as.Right = Nod(OSLICE, as.Right, Nod(OKEY, nil, nil)) + } + + typecheck(&as, Etop) + ninit = list(ninit, as) + } + + // zero the outparams + for ll := inlretvars; ll != nil; ll = ll.Next { + as = Nod(OAS, ll.N, nil) + typecheck(&as, Etop) + ninit = list(ninit, as) + } + + inlretlabel = newlabel_inl() + inlgen++ + body := inlsubstlist(fn.Func.Inl) + + body = list(body, Nod(OGOTO, inlretlabel, nil)) // avoid 'not used' when function doesnt have return + body = list(body, Nod(OLABEL, inlretlabel, nil)) + + typechecklist(body, Etop) + + //dumplist("ninit post", ninit); + + call := Nod(OINLCALL, nil, nil) + + call.Ninit = ninit + call.Nbody = body + call.Rlist = inlretvars + call.Type = n.Type + call.Typecheck = 1 + + setlno(call, int(n.Lineno)) + + //dumplist("call body", body); + + *np = call + + inlfn = saveinlfn + + // transitive inlining + // might be nice to do this before exporting the body, + // but can't emit the body with inlining expanded. + // instead we emit the things that the body needs + // and each use must redo the inlining. + // luckily these are small. + body = fn.Func.Inl + fn.Func.Inl = nil // prevent infinite recursion (shouldn't happen anyway) + inlnodelist(call.Nbody) + for ll := call.Nbody; ll != nil; ll = ll.Next { + if ll.N.Op == OINLCALL { + inlconv2stmt(ll.N) + } + } + fn.Func.Inl = body + + if Debug['m'] > 2 { + fmt.Printf("%v: After inlining %v\n\n", n.Line(), Nconv(*np, obj.FmtSign)) + } +} + +// Every time we expand a function we generate a new set of tmpnames, +// PAUTO's in the calling functions, and link them off of the +// PPARAM's, PAUTOS and PPARAMOUTs of the called function. +func inlvar(var_ *Node) *Node { + if Debug['m'] > 3 { + fmt.Printf("inlvar %v\n", Nconv(var_, obj.FmtSign)) + } + + n := newname(var_.Sym) + n.Type = var_.Type + n.Class = PAUTO + n.Used = true + n.Curfn = Curfn // the calling function, not the called one + n.Addrtaken = var_.Addrtaken + + // Esc pass wont run if we're inlining into a iface wrapper. + // Luckily, we can steal the results from the target func. + // If inlining a function defined in another package after + // escape analysis is done, treat all local vars as escaping. + // See issue 9537. + if var_.Esc == EscHeap || (inl_nonlocal != 0 && var_.Op == ONAME) { + addrescapes(n) + } + + Curfn.Func.Dcl = list(Curfn.Func.Dcl, n) + return n +} + +// Synthesize a variable to store the inlined function's results in. +func retvar(t *Type, i int) *Node { + n := newname(Lookupf("~r%d", i)) + n.Type = t.Type + n.Class = PAUTO + n.Used = true + n.Curfn = Curfn // the calling function, not the called one + Curfn.Func.Dcl = list(Curfn.Func.Dcl, n) + return n +} + +// Synthesize a variable to store the inlined function's arguments +// when they come from a multiple return call. +func argvar(t *Type, i int) *Node { + n := newname(Lookupf("~arg%d", i)) + n.Type = t.Type + n.Class = PAUTO + n.Used = true + n.Curfn = Curfn // the calling function, not the called one + Curfn.Func.Dcl = list(Curfn.Func.Dcl, n) + return n +} + +var newlabel_inl_label int + +func newlabel_inl() *Node { + newlabel_inl_label++ + n := newname(Lookupf(".inlret%.6d", newlabel_inl_label)) + n.Etype = 1 // flag 'safe' for escape analysis (no backjumps) + return n +} + +// inlsubst and inlsubstlist recursively copy the body of the saved +// pristine ->inl body of the function while substituting references +// to input/output parameters with ones to the tmpnames, and +// substituting returns with assignments to the output. +func inlsubstlist(ll *NodeList) *NodeList { + var l *NodeList + for ; ll != nil; ll = ll.Next { + l = list(l, inlsubst(ll.N)) + } + return l +} + +func inlsubst(n *Node) *Node { + if n == nil { + return nil + } + + switch n.Op { + case ONAME: + 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.Name.Inlvar, obj.FmtSign)) + } + return n.Name.Inlvar + } + + if Debug['m'] > 2 { + fmt.Printf("not substituting name %v\n", Nconv(n, obj.FmtSign)) + } + return n + + case OLITERAL, OTYPE: + return n + + // Since we don't handle bodies with closures, this return is guaranteed to belong to the current inlined function. + + // dump("Return before substitution", n); + case ORETURN: + m := Nod(OGOTO, inlretlabel, nil) + + m.Ninit = inlsubstlist(n.Ninit) + + if inlretvars != nil && n.List != nil { + as := Nod(OAS2, nil, nil) + + // shallow copy or OINLCALL->rlist will be the same list, and later walk and typecheck may clobber that. + for ll := inlretvars; ll != nil; ll = ll.Next { + as.List = list(as.List, ll.N) + } + as.Rlist = inlsubstlist(n.List) + typecheck(&as, Etop) + m.Ninit = list(m.Ninit, as) + } + + typechecklist(m.Ninit, Etop) + typecheck(&m, Etop) + + // dump("Return after substitution", m); + return m + + case OGOTO, OLABEL: + m := Nod(OXXX, nil, nil) + *m = *n + m.Ninit = nil + p := fmt.Sprintf("%s·%d", n.Left.Sym.Name, inlgen) + m.Left = newname(Lookup(p)) + + return m + } + + m := Nod(OXXX, nil, nil) + *m = *n + m.Ninit = nil + + if n.Op == OCLOSURE { + Fatal("cannot inline function containing closure: %v", Nconv(n, obj.FmtSign)) + } + + m.Left = inlsubst(n.Left) + m.Right = inlsubst(n.Right) + m.List = inlsubstlist(n.List) + m.Rlist = inlsubstlist(n.Rlist) + m.Ninit = concat(m.Ninit, inlsubstlist(n.Ninit)) + m.Ntest = inlsubst(n.Ntest) + m.Nincr = inlsubst(n.Nincr) + m.Nbody = inlsubstlist(n.Nbody) + m.Nelse = inlsubstlist(n.Nelse) + + return m +} + +// Plaster over linenumbers +func setlnolist(ll *NodeList, lno int) { + for ; ll != nil; ll = ll.Next { + setlno(ll.N, lno) + } +} + +func setlno(n *Node, lno int) { + if n == nil { + return + } + + // don't clobber names, unless they're freshly synthesized + if n.Op != ONAME || n.Lineno == 0 { + n.Lineno = int32(lno) + } + + setlno(n.Left, lno) + setlno(n.Right, lno) + setlnolist(n.List, lno) + setlnolist(n.Rlist, lno) + setlnolist(n.Ninit, lno) + setlno(n.Ntest, lno) + setlno(n.Nincr, lno) + setlnolist(n.Nbody, lno) + setlnolist(n.Nelse, lno) +} diff --git a/src/cmd/compile/internal/gc/lex.go b/src/cmd/compile/internal/gc/lex.go new file mode 100644 index 0000000000..f9211407fb --- /dev/null +++ b/src/cmd/compile/internal/gc/lex.go @@ -0,0 +1,2601 @@ +// Copyright 2009 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. + +//go:generate go tool yacc go.y +//go:generate go run mkbuiltin.go runtime unsafe + +package gc + +import ( + "bytes" + "cmd/internal/obj" + "flag" + "fmt" + "io" + "log" + "os" + "path" + "strconv" + "strings" + "unicode" + "unicode/utf8" +) + +var yyprev int + +var yylast int + +var imported_unsafe int + +var goos string + +var goarch string + +var goroot string + +var ( + Debug_wb int + Debug_append int + Debug_slice int +) + +// Debug arguments. +// These can be specified with the -d flag, as in "-d nil" +// to set the debug_checknil variable. In general the list passed +// to -d can be comma-separated. +var debugtab = []struct { + name string + val *int +}{ + {"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 +// of EOF and other out of range arguments. +func yy_isdigit(c int) bool { + return c >= 0 && c <= 0xFF && isdigit(c) +} + +func yy_isspace(c int) bool { + return c == ' ' || c == '\t' || c == '\n' || c == '\r' +} + +func yy_isalpha(c int) bool { + return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' +} + +func yy_isalnum(c int) bool { + return c >= 0 && c <= 0xFF && isalnum(c) +} + +// Disallow use of isdigit etc. + +const ( + EOF = -1 +) + +func usage() { + fmt.Printf("usage: %cg [options] file.go...\n", Thearch.Thechar) + obj.Flagprint(1) + Exit(2) +} + +func hidePanic() { + if nsavederrors+nerrors > 0 { + // If we've already complained about things + // in the program, don't bother complaining + // about a panic too; let the user clean up + // the code and try again. + if err := recover(); err != nil { + errorexit() + } + } +} + +func doversion() { + p := obj.Expstring() + if p == "X:none" { + p = "" + } + sep := "" + if p != "" { + sep = " " + } + fmt.Printf("%cg version %s%s%s\n", Thearch.Thechar, obj.Getgoversion(), sep, p) + os.Exit(0) +} + +func Main() { + defer hidePanic() + + // Allow GOARCH=thearch.thestring or GOARCH=thearch.thestringsuffix, + // but not other values. + p := obj.Getgoarch() + + if !strings.HasPrefix(p, Thearch.Thestring) { + log.Fatalf("cannot use %cg with GOARCH=%s", Thearch.Thechar, p) + } + goarch = p + + Thearch.Linkarchinit() + Ctxt = obj.Linknew(Thearch.Thelinkarch) + Ctxt.Diag = Yyerror + Ctxt.Bso = &bstdout + bstdout = *obj.Binitw(os.Stdout) + + localpkg = mkpkg("") + localpkg.Prefix = "\"\"" + + // pseudo-package, for scoping + builtinpkg = mkpkg("go.builtin") + + builtinpkg.Prefix = "go.builtin" // not go%2ebuiltin + + // pseudo-package, accessed by import "unsafe" + unsafepkg = mkpkg("unsafe") + + unsafepkg.Name = "unsafe" + + // real package, referred to by generated runtime calls + Runtimepkg = mkpkg("runtime") + + Runtimepkg.Name = "runtime" + + // pseudo-packages used in symbol tables + gostringpkg = mkpkg("go.string") + + gostringpkg.Name = "go.string" + gostringpkg.Prefix = "go.string" // not go%2estring + + itabpkg = mkpkg("go.itab") + + itabpkg.Name = "go.itab" + itabpkg.Prefix = "go.itab" // not go%2eitab + + weaktypepkg = mkpkg("go.weak.type") + + weaktypepkg.Name = "go.weak.type" + weaktypepkg.Prefix = "go.weak.type" // not go%2eweak%2etype + + typelinkpkg = mkpkg("go.typelink") + typelinkpkg.Name = "go.typelink" + typelinkpkg.Prefix = "go.typelink" // not go%2etypelink + + trackpkg = mkpkg("go.track") + + trackpkg.Name = "go.track" + trackpkg.Prefix = "go.track" // not go%2etrack + + typepkg = mkpkg("type") + + typepkg.Name = "type" + + goroot = obj.Getgoroot() + goos = obj.Getgoos() + + Nacl = goos == "nacl" + if Nacl { + flag_largemodel = 1 + } + + outfile = "" + obj.Flagcount("+", "compiling runtime", &compiling_runtime) + obj.Flagcount("%", "debug non-static initializers", &Debug['%']) + obj.Flagcount("A", "for bootstrapping, allow 'any' type", &Debug['A']) + obj.Flagcount("B", "disable bounds checking", &Debug['B']) + obj.Flagstr("D", "set relative `path` for local imports", &localimport) + obj.Flagcount("E", "debug symbol export", &Debug['E']) + obj.Flagfn1("I", "add `directory` to import search path", addidir) + obj.Flagcount("K", "debug missing line numbers", &Debug['K']) + obj.Flagcount("L", "use full (long) path in error messages", &Debug['L']) + obj.Flagcount("M", "debug move generation", &Debug['M']) + obj.Flagcount("N", "disable optimizations", &Debug['N']) + obj.Flagcount("P", "debug peephole optimizer", &Debug['P']) + obj.Flagcount("R", "debug register optimizer", &Debug['R']) + obj.Flagcount("S", "print assembly listing", &Debug['S']) + obj.Flagfn0("V", "print compiler version", doversion) + obj.Flagcount("W", "debug parse tree after type checking", &Debug['W']) + obj.Flagstr("asmhdr", "write assembly header to `file`", &asmhdr) + obj.Flagcount("complete", "compiling complete package (no C or assembly)", &pure_go) + obj.Flagstr("d", "print debug information about items in `list`", &debugstr) + obj.Flagcount("e", "no limit on number of errors reported", &Debug['e']) + obj.Flagcount("f", "debug stack frames", &Debug['f']) + obj.Flagcount("g", "debug code generation", &Debug['g']) + obj.Flagcount("h", "halt on error", &Debug['h']) + obj.Flagcount("i", "debug line number stack", &Debug['i']) + obj.Flagstr("installsuffix", "set pkg directory `suffix`", &flag_installsuffix) + obj.Flagcount("j", "debug runtime-initialized variables", &Debug['j']) + obj.Flagcount("l", "disable inlining", &Debug['l']) + obj.Flagcount("live", "debug liveness analysis", &debuglive) + obj.Flagcount("m", "print optimization decisions", &Debug['m']) + obj.Flagcount("nolocalimports", "reject local (relative) imports", &nolocalimports) + obj.Flagstr("o", "write output to `file`", &outfile) + obj.Flagstr("p", "set expected package import `path`", &myimportpath) + obj.Flagcount("pack", "write package file instead of object file", &writearchive) + obj.Flagcount("r", "debug generated wrappers", &Debug['r']) + obj.Flagcount("race", "enable race detector", &flag_race) + obj.Flagcount("s", "warn about composite literals that can be simplified", &Debug['s']) + obj.Flagstr("trimpath", "remove `prefix` from recorded source file paths", &Ctxt.LineHist.TrimPathPrefix) + obj.Flagcount("u", "reject unsafe code", &safemode) + obj.Flagcount("v", "increase debug verbosity", &Debug['v']) + obj.Flagcount("w", "debug type checking", &Debug['w']) + use_writebarrier = 1 + obj.Flagcount("wb", "enable write barrier", &use_writebarrier) + obj.Flagcount("x", "debug lexer", &Debug['x']) + obj.Flagcount("y", "debug declarations in canned imports (with -d)", &Debug['y']) + var flag_shared int + var flag_dynlink bool + if Thearch.Thechar == '6' { + obj.Flagcount("largemodel", "generate code that assumes a large memory model", &flag_largemodel) + obj.Flagcount("shared", "generate code that can be linked into a shared library", &flag_shared) + flag.BoolVar(&flag_dynlink, "dynlink", false, "support references to Go symbols defined in other shared libraries") + } + obj.Flagstr("cpuprofile", "write cpu profile to `file`", &cpuprofile) + obj.Flagstr("memprofile", "write memory profile to `file`", &memprofile) + obj.Flagint64("memprofilerate", "set runtime.MemProfileRate to `rate`", &memprofilerate) + obj.Flagparse(usage) + + if flag_dynlink { + flag_shared = 1 + } + Ctxt.Flag_shared = int32(flag_shared) + Ctxt.Flag_dynlink = flag_dynlink + + Ctxt.Debugasm = int32(Debug['S']) + Ctxt.Debugvlog = int32(Debug['v']) + + if flag.NArg() < 1 { + usage() + } + + startProfile() + + if flag_race != 0 { + racepkg = mkpkg("runtime/race") + racepkg.Name = "race" + } + + // parse -d argument + if debugstr != "" { + Split: + for _, name := range strings.Split(debugstr, ",") { + if name == "" { + continue + } + val := 1 + if i := strings.Index(name, "="); i >= 0 { + var err error + val, err = strconv.Atoi(name[i+1:]) + if err != nil { + log.Fatalf("invalid debug value %v", name) + } + name = name[:i] + } + for _, t := range debugtab { + if t.name == name { + if t.val != nil { + *t.val = val + continue Split + } + } + } + log.Fatalf("unknown debug key -d %s\n", name) + } + } + + // enable inlining. for now: + // default: inlining on. (debug['l'] == 1) + // -l: inlining off (debug['l'] == 0) + // -ll, -lll: inlining on again, with extra debugging (debug['l'] > 1) + if Debug['l'] <= 1 { + Debug['l'] = 1 - Debug['l'] + } + + Thearch.Betypeinit() + if Widthptr == 0 { + Fatal("betypeinit failed") + } + + lexinit() + typeinit() + lexinit1() + // TODO(rsc): Restore yytinit? + + blockgen = 1 + dclcontext = PEXTERN + nerrors = 0 + lexlineno = 1 + + for _, infile = range flag.Args() { + linehistpush(infile) + + curio.infile = infile + var err error + curio.bin, err = obj.Bopenr(infile) + if err != nil { + fmt.Printf("open %s: %v\n", infile, err) + errorexit() + } + + curio.peekc = 0 + curio.peekc1 = 0 + curio.nlsemi = 0 + curio.eofnl = 0 + curio.last = 0 + + // Skip initial BOM if present. + if obj.Bgetrune(curio.bin) != obj.BOM { + obj.Bungetrune(curio.bin) + } + + block = 1 + iota_ = -1000000 + + imported_unsafe = 0 + + yyparse() + if nsyntaxerrors != 0 { + errorexit() + } + + linehistpop() + if curio.bin != nil { + obj.Bterm(curio.bin) + } + } + + testdclstack() + mkpackage(localpkg.Name) // final import not used checks + lexfini() + + typecheckok = 1 + if Debug['f'] != 0 { + frame(1) + } + + // Process top-level declarations in phases. + + // Phase 1: const, type, and names and types of funcs. + // This will gather all the information about types + // and methods but doesn't depend on any of it. + defercheckwidth() + + for l := xtop; l != nil; l = l.Next { + if l.N.Op != ODCL && l.N.Op != OAS { + typecheck(&l.N, Etop) + } + } + + // Phase 2: Variable assignments. + // To check interface assignments, depends on phase 1. + for l := xtop; l != nil; l = l.Next { + if l.N.Op == ODCL || l.N.Op == OAS { + typecheck(&l.N, Etop) + } + } + resumecheckwidth() + + // Phase 3: Type check function bodies. + for l := xtop; l != nil; l = l.Next { + if l.N.Op == ODCLFUNC || l.N.Op == OCLOSURE { + Curfn = l.N + decldepth = 1 + saveerrors() + typechecklist(l.N.Nbody, Etop) + checkreturn(l.N) + if nerrors != 0 { + l.N.Nbody = nil // type errors; do not compile + } + } + } + + // Phase 4: Decide how to capture closed variables. + // 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 { + Curfn = l.N + capturevars(l.N) + } + } + + Curfn = nil + + if nsavederrors+nerrors != 0 { + errorexit() + } + + // Phase 5: Inlining + if Debug['l'] > 1 { + // Typecheck imported function bodies if debug['l'] > 1, + // otherwise lazily when used or re-exported. + for l := importlist; l != nil; l = l.Next { + if l.N.Func.Inl != nil { + saveerrors() + typecheckinl(l.N) + } + } + + if nsavederrors+nerrors != 0 { + errorexit() + } + } + + if Debug['l'] != 0 { + // Find functions that can be inlined and clone them before walk expands them. + visitBottomUp(xtop, func(list *NodeList, recursive bool) { + for l := list; l != nil; l = l.Next { + if l.N.Op == ODCLFUNC { + caninl(l.N) + inlcalls(l.N) + } + } + }) + } + + // Phase 6: Escape analysis. + // Required for moving heap allocations onto stack, + // which in turn is required by the closure implementation, + // 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. + 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. + for l := xtop; l != nil; l = l.Next { + if l.N.Op == ODCLFUNC && l.N.Closure != nil { + Curfn = l.N + transformclosure(l.N) + } + } + + Curfn = nil + + // Phase 8: Compile top level functions. + for l := xtop; l != nil; l = l.Next { + if l.N.Op == ODCLFUNC { + funccompile(l.N) + } + } + + if nsavederrors+nerrors == 0 { + fninit(xtop) + } + + // Phase 9: Check external declarations. + for l := externdcl; l != nil; l = l.Next { + if l.N.Op == ONAME { + typecheck(&l.N, Erv) + } + } + + if nerrors+nsavederrors != 0 { + errorexit() + } + + dumpobj() + + if asmhdr != "" { + dumpasmhdr() + } + + if nerrors+nsavederrors != 0 { + errorexit() + } + + Flusherrors() +} + +func saveerrors() { + nsavederrors += nerrors + nerrors = 0 +} + +func arsize(b *obj.Biobuf, name string) int { + var buf [ArhdrSize]byte + if _, err := io.ReadFull(b, buf[:]); err != nil { + return -1 + } + aname := strings.Trim(string(buf[0:16]), " ") + if !strings.HasPrefix(aname, name) { + return -1 + } + asize := strings.Trim(string(buf[48:58]), " ") + i, _ := strconv.Atoi(asize) + return i +} + +func skiptopkgdef(b *obj.Biobuf) bool { + /* archive header */ + p := obj.Brdline(b, '\n') + if p == "" { + return false + } + if obj.Blinelen(b) != 8 { + return false + } + if p != "!\n" { + return false + } + + /* symbol table may be first; skip it */ + sz := arsize(b, "__.GOSYMDEF") + + if sz >= 0 { + obj.Bseek(b, int64(sz), 1) + } else { + obj.Bseek(b, 8, 0) + } + + /* package export block is next */ + sz = arsize(b, "__.PKGDEF") + + if sz <= 0 { + return false + } + return true +} + +func addidir(dir string) { + if dir == "" { + return + } + + var pp **Idir + for pp = &idirs; *pp != nil; pp = &(*pp).link { + } + *pp = new(Idir) + (*pp).link = nil + (*pp).dir = dir +} + +// is this path a local name? begins with ./ or ../ or / +func islocalname(name string) bool { + return strings.HasPrefix(name, "/") || + Ctxt.Windows != 0 && len(name) >= 3 && yy_isalpha(int(name[0])) && name[1] == ':' && name[2] == '/' || + strings.HasPrefix(name, "./") || name == "." || + strings.HasPrefix(name, "../") || name == ".." +} + +func findpkg(name string) (file string, ok bool) { + if islocalname(name) { + if safemode != 0 || nolocalimports != 0 { + return "", false + } + + // try .a before .6. important for building libraries: + // if there is an array.6 in the array.a library, + // want to find all of array.a, not just array.6. + file = fmt.Sprintf("%s.a", name) + if obj.Access(file, 0) >= 0 { + return file, true + } + file = fmt.Sprintf("%s.%c", name, Thearch.Thechar) + if obj.Access(file, 0) >= 0 { + return file, true + } + return "", false + } + + // local imports should be canonicalized already. + // don't want to see "encoding/../encoding/base64" + // as different from "encoding/base64". + var q string + _ = q + if path.Clean(name) != name { + Yyerror("non-canonical import path %q (should be %q)", name, q) + return "", false + } + + for p := idirs; p != nil; p = p.link { + file = fmt.Sprintf("%s/%s.a", p.dir, name) + if obj.Access(file, 0) >= 0 { + return file, true + } + file = fmt.Sprintf("%s/%s.%c", p.dir, name, Thearch.Thechar) + if obj.Access(file, 0) >= 0 { + return file, true + } + } + + if goroot != "" { + suffix := "" + suffixsep := "" + if flag_installsuffix != "" { + suffixsep = "_" + suffix = flag_installsuffix + } else if flag_race != 0 { + suffixsep = "_" + suffix = "race" + } + + file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.a", goroot, goos, goarch, suffixsep, suffix, name) + 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) + if obj.Access(file, 0) >= 0 { + return file, true + } + } + + return "", false +} + +func fakeimport() { + importpkg = mkpkg("fake") + cannedimports("fake.6", "$$\n") +} + +func importfile(f *Val, line int) { + if f.Ctype != CTSTR { + Yyerror("import statement not a string") + fakeimport() + return + } + + if len(f.U.(string)) == 0 { + Yyerror("import path is empty") + fakeimport() + return + } + + if isbadimport(f.U.(string)) { + fakeimport() + return + } + + // The package name main is no longer reserved, + // 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.(string) == "main" { + Yyerror("cannot import \"main\"") + errorexit() + } + + if myimportpath != "" && f.U.(string) == myimportpath { + Yyerror("import %q while compiling that package (import cycle)", f.U.(string)) + errorexit() + } + + if f.U.(string) == "unsafe" { + if safemode != 0 { + Yyerror("cannot import package unsafe") + errorexit() + } + + importpkg = mkpkg(f.U.(string)) + cannedimports("unsafe.6", unsafeimport) + imported_unsafe = 1 + return + } + + path_ := f.U.(string) + if islocalname(path_) { + if path_[0] == '/' { + Yyerror("import path cannot be absolute path") + fakeimport() + return + } + + prefix := Ctxt.Pathname + if localimport != "" { + prefix = localimport + } + cleanbuf := prefix + cleanbuf += "/" + cleanbuf += path_ + cleanbuf = path.Clean(cleanbuf) + path_ = cleanbuf + + if isbadimport(path_) { + fakeimport() + return + } + } + + file, found := findpkg(path_) + if !found { + Yyerror("can't find import: %q", f.U.(string)) + errorexit() + } + + importpkg = mkpkg(path_) + + // If we already saw that package, feed a dummy statement + // to the lexer to avoid parsing export data twice. + if importpkg.Imported != 0 { + tag := "" + if importpkg.Safe { + tag = "safe" + } + + p := fmt.Sprintf("package %s %s\n$$\n", importpkg.Name, tag) + cannedimports(file, p) + return + } + + importpkg.Imported = 1 + + var err error + var imp *obj.Biobuf + imp, err = obj.Bopenr(file) + if err != nil { + Yyerror("can't open import: %q: %v", f.U.(string), err) + errorexit() + } + + if strings.HasSuffix(file, ".a") { + if !skiptopkgdef(imp) { + Yyerror("import %s: not a package file", file) + errorexit() + } + } + + // check object header + p := obj.Brdstr(imp, '\n', 1) + + if p != "empty archive" { + if !strings.HasPrefix(p, "go object ") { + Yyerror("import %s: not a go object file", file) + errorexit() + } + + q := fmt.Sprintf("%s %s %s %s", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring()) + if p[10:] != q { + Yyerror("import %s: object is [%s] expected [%s]", file, p[10:], q) + errorexit() + } + } + + // assume files move (get installed) + // so don't record the full path. + linehistpragma(file[len(file)-len(path_)-2:]) // acts as #pragma lib + + /* + * position the input right + * after $$ and return + */ + pushedio = curio + + curio.bin = imp + curio.peekc = 0 + curio.peekc1 = 0 + curio.infile = file + curio.nlsemi = 0 + typecheckok = 1 + + var c int32 + for { + c = int32(getc()) + if c == EOF { + break + } + if c != '$' { + continue + } + c = int32(getc()) + if c == EOF { + break + } + if c != '$' { + continue + } + return + } + + Yyerror("no import in %q", f.U.(string)) + unimportfile() +} + +func unimportfile() { + if curio.bin != nil { + obj.Bterm(curio.bin) + curio.bin = nil + } else { + lexlineno-- // re correct sys.6 line number + } + + curio = pushedio + + pushedio.bin = nil + incannedimport = 0 + typecheckok = 0 +} + +func cannedimports(file string, cp string) { + lexlineno++ // if sys.6 is included on line 1, + + pushedio = curio + + curio.bin = nil + curio.peekc = 0 + curio.peekc1 = 0 + curio.infile = file + curio.cp = cp + curio.nlsemi = 0 + curio.importsafe = false + + typecheckok = 1 + incannedimport = 1 +} + +func isfrog(c int) bool { + // complain about possibly invisible control characters + if c < ' ' { + return !yy_isspace(c) // exclude good white space + } + + if 0x7f <= c && c <= 0xa0 { // DEL, unicode block including unbreakable space. + return true + } + return false +} + +type Loophack struct { + v int + next *Loophack +} + +var _yylex_lstk *Loophack + +func _yylex(yylval *yySymType) int32 { + var c1 int + var escflag int + var v int64 + var cp *bytes.Buffer + var rune_ uint + var s *Sym + var h *Loophack + var str string + + prevlineno = lineno + +l0: + c := getc() + if yy_isspace(c) { + if c == '\n' && curio.nlsemi != 0 { + ungetc(c) + if Debug['x'] != 0 { + fmt.Printf("lex: implicit semi\n") + } + return ';' + } + + goto l0 + } + + lineno = lexlineno /* start of token */ + + if c >= utf8.RuneSelf { + /* all multibyte runes are alpha */ + cp = &lexbuf + cp.Reset() + + goto talph + } + + if yy_isalpha(c) { + cp = &lexbuf + cp.Reset() + goto talph + } + + if yy_isdigit(c) { + cp = &lexbuf + cp.Reset() + if c != '0' { + for { + cp.WriteByte(byte(c)) + c = getc() + if yy_isdigit(c) { + continue + } + if c == '.' { + goto casedot + } + if c == 'e' || c == 'E' || c == 'p' || c == 'P' { + goto caseep + } + if c == 'i' { + goto casei + } + goto ncu + } + } + + cp.WriteByte(byte(c)) + c = getc() + if c == 'x' || c == 'X' { + for { + cp.WriteByte(byte(c)) + c = getc() + if yy_isdigit(c) { + continue + } + if c >= 'a' && c <= 'f' { + continue + } + if c >= 'A' && c <= 'F' { + continue + } + if lexbuf.Len() == 2 { + Yyerror("malformed hex constant") + } + if c == 'p' { + goto caseep + } + goto ncu + } + } + + if c == 'p' { // 0p begins floating point zero + goto caseep + } + + c1 = 0 + for { + if !yy_isdigit(c) { + break + } + if c < '0' || c > '7' { + c1 = 1 // not octal + } + cp.WriteByte(byte(c)) + c = getc() + } + + if c == '.' { + goto casedot + } + if c == 'e' || c == 'E' { + goto caseep + } + if c == 'i' { + goto casei + } + if c1 != 0 { + Yyerror("malformed octal constant") + } + goto ncu + } + + switch c { + case EOF: + lineno = prevlineno + ungetc(EOF) + return -1 + + case '_': + cp = &lexbuf + cp.Reset() + goto talph + + case '.': + c1 = getc() + if yy_isdigit(c1) { + cp = &lexbuf + cp.Reset() + cp.WriteByte(byte(c)) + c = c1 + goto casedot + } + + if c1 == '.' { + c1 = getc() + if c1 == '.' { + c = LDDD + goto lx + } + + ungetc(c1) + c1 = '.' + } + + /* "..." */ + case '"': + lexbuf.Reset() + lexbuf.WriteString(`""`) + + cp = &strbuf + cp.Reset() + + for { + if escchar('"', &escflag, &v) { + break + } + if v < utf8.RuneSelf || escflag != 0 { + cp.WriteByte(byte(v)) + } else { + rune_ = uint(v) + cp.WriteRune(rune(rune_)) + } + } + + goto strlit + + /* `...` */ + case '`': + lexbuf.Reset() + lexbuf.WriteString("``") + + cp = &strbuf + cp.Reset() + + for { + c = int(getr()) + if c == '\r' { + continue + } + if c == EOF { + Yyerror("eof in string") + break + } + + if c == '`' { + break + } + cp.WriteRune(rune(c)) + } + + goto strlit + + /* '.' */ + case '\'': + if escchar('\'', &escflag, &v) { + Yyerror("empty character literal or unescaped ' in character literal") + v = '\'' + } + + if !escchar('\'', &escflag, &v) { + Yyerror("missing '") + ungetc(int(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") + } + litbuf = "string literal" + return LLITERAL + + case '/': + c1 = getc() + if c1 == '*' { + nl := 0 + for { + c = int(getr()) + if c == '\n' { + nl = 1 + } + for c == '*' { + c = int(getr()) + if c == '/' { + if nl != 0 { + ungetc('\n') + } + goto l0 + } + + if c == '\n' { + nl = 1 + } + } + + if c == EOF { + Yyerror("eof in comment") + errorexit() + } + } + } + + if c1 == '/' { + c = getlinepragma() + for { + if c == '\n' || c == EOF { + ungetc(c) + goto l0 + } + + c = int(getr()) + } + } + + if c1 == '=' { + c = ODIV + goto asop + } + + case ':': + c1 = getc() + if c1 == '=' { + c = LCOLAS + yylval.i = int(lexlineno) + goto lx + } + + case '*': + c1 = getc() + if c1 == '=' { + c = OMUL + goto asop + } + + case '%': + c1 = getc() + if c1 == '=' { + c = OMOD + goto asop + } + + case '+': + c1 = getc() + if c1 == '+' { + c = LINC + goto lx + } + + if c1 == '=' { + c = OADD + goto asop + } + + case '-': + c1 = getc() + if c1 == '-' { + c = LDEC + goto lx + } + + if c1 == '=' { + c = OSUB + goto asop + } + + case '>': + c1 = getc() + if c1 == '>' { + c = LRSH + c1 = getc() + if c1 == '=' { + c = ORSH + goto asop + } + + break + } + + if c1 == '=' { + c = LGE + goto lx + } + + c = LGT + + case '<': + c1 = getc() + if c1 == '<' { + c = LLSH + c1 = getc() + if c1 == '=' { + c = OLSH + goto asop + } + + break + } + + if c1 == '=' { + c = LLE + goto lx + } + + if c1 == '-' { + c = LCOMM + goto lx + } + + c = LLT + + case '=': + c1 = getc() + if c1 == '=' { + c = LEQ + goto lx + } + + case '!': + c1 = getc() + if c1 == '=' { + c = LNE + goto lx + } + + case '&': + c1 = getc() + if c1 == '&' { + c = LANDAND + goto lx + } + + if c1 == '^' { + c = LANDNOT + c1 = getc() + if c1 == '=' { + c = OANDNOT + goto asop + } + + break + } + + if c1 == '=' { + c = OAND + goto asop + } + + case '|': + c1 = getc() + if c1 == '|' { + c = LOROR + goto lx + } + + if c1 == '=' { + c = OOR + goto asop + } + + case '^': + c1 = getc() + if c1 == '=' { + c = OXOR + goto asop + } + + /* + * clumsy dance: + * to implement rule that disallows + * if T{1}[0] { ... } + * but allows + * if (T{1}[0]) { ... } + * the block bodies for if/for/switch/select + * begin with an LBODY token, not '{'. + * + * when we see the keyword, the next + * non-parenthesized '{' becomes an LBODY. + * loophack is normally 0. + * a keyword makes it go up to 1. + * parens push loophack onto a stack and go back to 0. + * a '{' with loophack == 1 becomes LBODY and disables loophack. + * + * i said it was clumsy. + */ + case '(', '[': + if loophack != 0 || _yylex_lstk != nil { + h = new(Loophack) + if h == nil { + Flusherrors() + Yyerror("out of memory") + errorexit() + } + + h.v = loophack + h.next = _yylex_lstk + _yylex_lstk = h + loophack = 0 + } + + goto lx + + case ')', ']': + if _yylex_lstk != nil { + h = _yylex_lstk + loophack = h.v + _yylex_lstk = h.next + } + + goto lx + + case '{': + if loophack == 1 { + if Debug['x'] != 0 { + fmt.Printf("%v lex: LBODY\n", Ctxt.Line(int(lexlineno))) + } + loophack = 0 + return LBODY + } + + goto lx + + default: + goto lx + } + + ungetc(c1) + +lx: + if c > 0xff { + if Debug['x'] != 0 { + fmt.Printf("%v lex: TOKEN %s\n", Ctxt.Line(int(lexlineno)), lexname(c)) + } + } else { + if Debug['x'] != 0 { + fmt.Printf("%v lex: TOKEN '%c'\n", Ctxt.Line(int(lexlineno)), c) + } + } + if isfrog(c) { + Yyerror("illegal character 0x%x", uint(c)) + goto l0 + } + + if importpkg == nil && (c == '#' || c == '$' || c == '?' || c == '@' || c == '\\') { + Yyerror("%s: unexpected %c", "syntax error", c) + goto l0 + } + + return int32(c) + +asop: + yylval.i = c // rathole to hold which asop + if Debug['x'] != 0 { + fmt.Printf("lex: TOKEN ASOP %c\n", c) + } + return LASOP + + /* + * cp is set to lexbuf and some + * prefix has been stored + */ +talph: + for { + if c >= utf8.RuneSelf { + ungetc(c) + rune_ = uint(getr()) + + // 0xb7 · is used for internal names + if !unicode.IsLetter(rune(rune_)) && !unicode.IsDigit(rune(rune_)) && (importpkg == nil || rune_ != 0xb7) { + Yyerror("invalid identifier character U+%04x", rune_) + } + cp.WriteRune(rune(rune_)) + } else if !yy_isalnum(c) && c != '_' { + break + } else { + cp.WriteByte(byte(c)) + } + c = getc() + } + + cp = nil + ungetc(c) + + s = LookupBytes(lexbuf.Bytes()) + switch s.Lexical { + case LIGNORE: + goto l0 + + case LFOR, LIF, LSWITCH, LSELECT: + loophack = 1 // see comment about loophack above + } + + if Debug['x'] != 0 { + fmt.Printf("lex: %s %s\n", s, lexname(int(s.Lexical))) + } + yylval.sym = s + return int32(s.Lexical) + +ncu: + cp = nil + ungetc(c) + + str = lexbuf.String() + 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.(*Mpint), 0) + } + + yylval.val.Ctype = CTINT + if Debug['x'] != 0 { + fmt.Printf("lex: integer literal\n") + } + litbuf = "literal " + str + return LLITERAL + +casedot: + for { + cp.WriteByte(byte(c)) + c = getc() + if !yy_isdigit(c) { + break + } + } + + if c == 'i' { + goto casei + } + if c != 'e' && c != 'E' { + goto caseout + } + +caseep: + cp.WriteByte(byte(c)) + c = getc() + if c == '+' || c == '-' { + cp.WriteByte(byte(c)) + c = getc() + } + + if !yy_isdigit(c) { + Yyerror("malformed fp constant exponent") + } + for yy_isdigit(c) { + cp.WriteByte(byte(c)) + c = getc() + } + + if c == 'i' { + goto casei + } + goto caseout + + // imaginary constant +casei: + cp = nil + + str = lexbuf.String() + 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.(*Mpcplx).Real, 0.0) + } + + yylval.val.Ctype = CTCPLX + if Debug['x'] != 0 { + fmt.Printf("lex: imaginary literal\n") + } + litbuf = "literal " + str + return LLITERAL + +caseout: + cp = nil + ungetc(c) + + str = lexbuf.String() + 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.(*Mpflt), 0.0) + } + + yylval.val.Ctype = CTFLT + if Debug['x'] != 0 { + fmt.Printf("lex: floating literal\n") + } + litbuf = "literal " + str + return LLITERAL + +strlit: + yylval.val.U = internString(cp.Bytes()) + yylval.val.Ctype = CTSTR + if Debug['x'] != 0 { + fmt.Printf("lex: string literal\n") + } + litbuf = "string literal" + return LLITERAL +} + +var internedStrings = map[string]string{} + +func internString(b []byte) string { + s, ok := internedStrings[string(b)] // string(b) here doesn't allocate + if ok { + return s + } + s = string(b) + internedStrings[s] = s + return s +} + +func more(pp *string) bool { + p := *pp + for p != "" && yy_isspace(int(p[0])) { + p = p[1:] + } + *pp = p + return p != "" +} + +/* + * read and interpret syntax that looks like + * //line parse.y:15 + * as a discontinuity in sequential line numbers. + * the next line of input comes from parse.y:15 + */ +func getlinepragma() int { + var cmd, verb, name string + + c := int(getr()) + if c == 'g' { + cp := &lexbuf + cp.Reset() + cp.WriteByte('g') // already read + for { + c = int(getr()) + if c == EOF || c >= utf8.RuneSelf { + return c + } + if c == '\n' { + break + } + cp.WriteByte(byte(c)) + } + cp = nil + + text := lexbuf.String() + + if strings.HasPrefix(text, "go:cgo_") { + pragcgo(text) + } + + cmd = text + verb = cmd + if i := strings.Index(verb, " "); i >= 0 { + verb = verb[:i] + } + + if verb == "go:linkname" { + if imported_unsafe == 0 { + Yyerror("//go:linkname only allowed in Go files that import \"unsafe\"") + } + f := strings.Fields(cmd) + if len(f) != 3 { + Yyerror("usage: //go:linkname localname linkname") + return c + } + + Lookup(f[1]).Linkname = f[2] + return c + } + + if verb == "go:nointerface" && obj.Fieldtrack_enabled != 0 { + nointerface = true + return c + } + + if verb == "go:noescape" { + noescape = true + return c + } + + if verb == "go:nosplit" { + nosplit = true + return c + } + + if verb == "go:nowritebarrier" { + if compiling_runtime == 0 { + Yyerror("//go:nowritebarrier only allowed in runtime") + } + nowritebarrier = true + return c + } + return c + } + if c != 'l' { + return c + } + for i := 1; i < 5; i++ { + c = int(getr()) + if c != int("line "[i]) { + return c + } + } + + cp := &lexbuf + cp.Reset() + linep := 0 + for { + c = int(getr()) + if c == EOF { + return c + } + if c == '\n' { + break + } + if c == ' ' { + continue + } + if c == ':' { + linep = cp.Len() + 1 + } + cp.WriteByte(byte(c)) + } + + cp = nil + + if linep == 0 { + return c + } + text := lexbuf.String() + n := 0 + for _, c := range text[linep:] { + if c < '0' || c > '9' { + goto out + } + n = n*10 + int(c) - '0' + if n > 1e8 { + Yyerror("line number out of range") + errorexit() + } + } + + if n <= 0 { + return c + } + + name = text[:linep-1] + linehistupdate(name, n) + return c + +out: + return c +} + +func getimpsym(pp *string) string { + more(pp) // skip spaces + p := *pp + if p == "" || p[0] == '"' { + return "" + } + i := 0 + for i < len(p) && !yy_isspace(int(p[i])) && p[i] != '"' { + i++ + } + sym := p[:i] + *pp = p[i:] + return sym +} + +func getquoted(pp *string) (string, bool) { + more(pp) // skip spaces + p := *pp + if p == "" || p[0] != '"' { + return "", false + } + p = p[1:] + i := strings.Index(p, `"`) + if i < 0 { + return "", false + } + *pp = p[i+1:] + return p[:i], true +} + +// Copied nearly verbatim from the C compiler's #pragma parser. +// TODO: Rewrite more cleanly once the compiler is written in Go. +func pragcgo(text string) { + var q string + + if i := strings.Index(text, " "); i >= 0 { + text, q = text[:i], text[i:] + } + + verb := text[3:] // skip "go:" + + if verb == "cgo_dynamic_linker" || verb == "dynlinker" { + var ok bool + var p string + p, ok = getquoted(&q) + if !ok { + Yyerror("usage: //go:cgo_dynamic_linker \"path\"") + return + } + pragcgobuf += fmt.Sprintf("cgo_dynamic_linker %v\n", plan9quote(p)) + return + + } + + if verb == "dynexport" { + verb = "cgo_export_dynamic" + } + if verb == "cgo_export_static" || verb == "cgo_export_dynamic" { + local := getimpsym(&q) + var remote string + if local == "" { + goto err2 + } + if !more(&q) { + pragcgobuf += fmt.Sprintf("%s %v\n", verb, plan9quote(local)) + return + } + + remote = getimpsym(&q) + if remote == "" { + goto err2 + } + pragcgobuf += fmt.Sprintf("%s %v %v\n", verb, plan9quote(local), plan9quote(remote)) + return + + err2: + Yyerror("usage: //go:%s local [remote]", verb) + return + } + + if verb == "cgo_import_dynamic" || verb == "dynimport" { + var ok bool + local := getimpsym(&q) + var p string + var remote string + if local == "" { + goto err3 + } + if !more(&q) { + pragcgobuf += fmt.Sprintf("cgo_import_dynamic %v\n", plan9quote(local)) + return + } + + remote = getimpsym(&q) + if remote == "" { + goto err3 + } + if !more(&q) { + pragcgobuf += fmt.Sprintf("cgo_import_dynamic %v %v\n", plan9quote(local), plan9quote(remote)) + return + } + + p, ok = getquoted(&q) + if !ok { + goto err3 + } + pragcgobuf += fmt.Sprintf("cgo_import_dynamic %v %v %v\n", plan9quote(local), plan9quote(remote), plan9quote(p)) + return + + err3: + Yyerror("usage: //go:cgo_import_dynamic local [remote [\"library\"]]") + return + } + + if verb == "cgo_import_static" { + local := getimpsym(&q) + if local == "" || more(&q) { + Yyerror("usage: //go:cgo_import_static local") + return + } + pragcgobuf += fmt.Sprintf("cgo_import_static %v\n", plan9quote(local)) + return + + } + + if verb == "cgo_ldflag" { + var ok bool + var p string + p, ok = getquoted(&q) + if !ok { + Yyerror("usage: //go:cgo_ldflag \"arg\"") + return + } + pragcgobuf += fmt.Sprintf("cgo_ldflag %v\n", plan9quote(p)) + return + + } +} + +type yy struct{} + +func (yy) Lex(v *yySymType) int { + return int(yylex(v)) +} + +func (yy) Error(msg string) { + Yyerror("%s", msg) +} + +var theparser yyParser +var parsing bool + +func yyparse() { + theparser = yyNewParser() + parsing = true + theparser.Parse(yy{}) + parsing = false +} + +func yylex(yylval *yySymType) int32 { + lx := int(_yylex(yylval)) + + if curio.nlsemi != 0 && lx == EOF { + // Treat EOF as "end of line" for the purposes + // of inserting a semicolon. + lx = ';' + } + + switch lx { + case LNAME, + LLITERAL, + LBREAK, + LCONTINUE, + LFALL, + LRETURN, + LINC, + LDEC, + ')', + '}', + ']': + curio.nlsemi = 1 + + default: + curio.nlsemi = 0 + } + + // Track last two tokens returned by yylex. + yyprev = yylast + + yylast = lx + return int32(lx) +} + +func getc() int { + c := curio.peekc + if c != 0 { + curio.peekc = curio.peekc1 + curio.peekc1 = 0 + goto check + } + + if curio.bin == nil { + if len(curio.cp) == 0 { + c = 0 + } else { + c = int(curio.cp[0]) + curio.cp = curio.cp[1:] + } + } else { + loop: + c = obj.Bgetc(curio.bin) + if c == 0xef { + buf, err := curio.bin.Peek(2) + if err != nil { + log.Fatalf("getc: peeking: %v", err) + } + if buf[0] == 0xbb && buf[1] == 0xbf { + yyerrorl(int(lexlineno), "Unicode (UTF-8) BOM in middle of file") + + // consume BOM bytes + obj.Bgetc(curio.bin) + obj.Bgetc(curio.bin) + goto loop + } + } + } + +check: + switch c { + case 0: + if curio.bin != nil { + Yyerror("illegal NUL byte") + break + } + fallthrough + + // insert \n at EOF + case EOF: + if curio.eofnl != 0 || curio.last == '\n' { + return EOF + } + curio.eofnl = 1 + c = '\n' + fallthrough + + case '\n': + if pushedio.bin == nil { + lexlineno++ + } + } + + curio.last = c + return c +} + +func ungetc(c int) { + curio.peekc1 = curio.peekc + curio.peekc = c + if c == '\n' && pushedio.bin == nil { + lexlineno-- + } +} + +func getr() int32 { + var buf [utf8.UTFMax]byte + + for i := 0; ; i++ { + c := getc() + if i == 0 && c < utf8.RuneSelf { + return int32(c) + } + buf[i] = byte(c) + if i+1 == len(buf) || utf8.FullRune(buf[:i+1]) { + r, w := utf8.DecodeRune(buf[:i+1]) + if r == utf8.RuneError && w == 1 { + lineno = lexlineno + // The string conversion here makes a copy for passing + // to fmt.Printf, so that buf itself does not escape and can + // be allocated on the stack. + Yyerror("illegal UTF-8 sequence % x", string(buf[:i+1])) + } + return int32(r) + } + } +} + +func escchar(e int, escflg *int, val *int64) bool { + *escflg = 0 + + c := int(getr()) + switch c { + case EOF: + Yyerror("eof in string") + return true + + case '\n': + Yyerror("newline in string") + return true + + case '\\': + break + + default: + if c == e { + return true + } + *val = int64(c) + return false + } + + u := 0 + c = int(getr()) + var i int + switch c { + case 'x': + *escflg = 1 // it's a byte + i = 2 + goto hex + + case 'u': + i = 4 + u = 1 + goto hex + + case 'U': + i = 8 + u = 1 + goto hex + + case '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7': + *escflg = 1 // it's a byte + l := int64(c) - '0' + for i := 2; i > 0; i-- { + c = getc() + if c >= '0' && c <= '7' { + l = l*8 + int64(c) - '0' + continue + } + + Yyerror("non-octal character in escape sequence: %c", c) + ungetc(c) + } + + if l > 255 { + Yyerror("octal escape value > 255: %d", l) + } + + *val = l + return false + + case 'a': + c = '\a' + case 'b': + c = '\b' + case 'f': + c = '\f' + case 'n': + c = '\n' + case 'r': + c = '\r' + case 't': + c = '\t' + case 'v': + c = '\v' + case '\\': + c = '\\' + + default: + if c != e { + Yyerror("unknown escape sequence: %c", c) + } + } + + *val = int64(c) + return false + +hex: + l := int64(0) + for ; i > 0; i-- { + c = getc() + if c >= '0' && c <= '9' { + l = l*16 + int64(c) - '0' + continue + } + + if c >= 'a' && c <= 'f' { + l = l*16 + int64(c) - 'a' + 10 + continue + } + + if c >= 'A' && c <= 'F' { + l = l*16 + int64(c) - 'A' + 10 + continue + } + + Yyerror("non-hex character in escape sequence: %c", c) + ungetc(c) + break + } + + if u != 0 && (l > utf8.MaxRune || (0xd800 <= l && l < 0xe000)) { + Yyerror("invalid Unicode code point in escape sequence: %#x", l) + l = utf8.RuneError + } + + *val = l + return false +} + +var syms = []struct { + name string + lexical int + etype int + op int +}{ + /* basic types */ + {"int8", LNAME, TINT8, OXXX}, + {"int16", LNAME, TINT16, OXXX}, + {"int32", LNAME, TINT32, OXXX}, + {"int64", LNAME, TINT64, OXXX}, + {"uint8", LNAME, TUINT8, OXXX}, + {"uint16", LNAME, TUINT16, OXXX}, + {"uint32", LNAME, TUINT32, OXXX}, + {"uint64", LNAME, TUINT64, OXXX}, + {"float32", LNAME, TFLOAT32, OXXX}, + {"float64", LNAME, TFLOAT64, OXXX}, + {"complex64", LNAME, TCOMPLEX64, OXXX}, + {"complex128", LNAME, TCOMPLEX128, OXXX}, + {"bool", LNAME, TBOOL, OXXX}, + {"string", LNAME, TSTRING, OXXX}, + {"any", LNAME, TANY, OXXX}, + {"break", LBREAK, Txxx, OXXX}, + {"case", LCASE, Txxx, OXXX}, + {"chan", LCHAN, Txxx, OXXX}, + {"const", LCONST, Txxx, OXXX}, + {"continue", LCONTINUE, Txxx, OXXX}, + {"default", LDEFAULT, Txxx, OXXX}, + {"else", LELSE, Txxx, OXXX}, + {"defer", LDEFER, Txxx, OXXX}, + {"fallthrough", LFALL, Txxx, OXXX}, + {"for", LFOR, Txxx, OXXX}, + {"func", LFUNC, Txxx, OXXX}, + {"go", LGO, Txxx, OXXX}, + {"goto", LGOTO, Txxx, OXXX}, + {"if", LIF, Txxx, OXXX}, + {"import", LIMPORT, Txxx, OXXX}, + {"interface", LINTERFACE, Txxx, OXXX}, + {"map", LMAP, Txxx, OXXX}, + {"package", LPACKAGE, Txxx, OXXX}, + {"range", LRANGE, Txxx, OXXX}, + {"return", LRETURN, Txxx, OXXX}, + {"select", LSELECT, Txxx, OXXX}, + {"struct", LSTRUCT, Txxx, OXXX}, + {"switch", LSWITCH, Txxx, OXXX}, + {"type", LTYPE, Txxx, OXXX}, + {"var", LVAR, Txxx, OXXX}, + {"append", LNAME, Txxx, OAPPEND}, + {"cap", LNAME, Txxx, OCAP}, + {"close", LNAME, Txxx, OCLOSE}, + {"complex", LNAME, Txxx, OCOMPLEX}, + {"copy", LNAME, Txxx, OCOPY}, + {"delete", LNAME, Txxx, ODELETE}, + {"imag", LNAME, Txxx, OIMAG}, + {"len", LNAME, Txxx, OLEN}, + {"make", LNAME, Txxx, OMAKE}, + {"new", LNAME, Txxx, ONEW}, + {"panic", LNAME, Txxx, OPANIC}, + {"print", LNAME, Txxx, OPRINT}, + {"println", LNAME, Txxx, OPRINTN}, + {"real", LNAME, Txxx, OREAL}, + {"recover", LNAME, Txxx, ORECOVER}, + {"notwithstanding", LIGNORE, Txxx, OXXX}, + {"thetruthofthematter", LIGNORE, Txxx, OXXX}, + {"despiteallobjections", LIGNORE, Txxx, OXXX}, + {"whereas", LIGNORE, Txxx, OXXX}, + {"insofaras", LIGNORE, Txxx, OXXX}, +} + +func lexinit() { + var lex int + var s *Sym + var s1 *Sym + var t *Type + var etype int + + /* + * initialize basic types array + * initialize known symbols + */ + for i := 0; i < len(syms); i++ { + lex = syms[i].lexical + s = Lookup(syms[i].name) + s.Lexical = uint16(lex) + + etype = syms[i].etype + if etype != Txxx { + if etype < 0 || etype >= len(Types) { + Fatal("lexinit: %s bad etype", s.Name) + } + s1 = Pkglookup(syms[i].name, builtinpkg) + t = Types[etype] + if t == nil { + t = typ(etype) + t.Sym = s1 + + if etype != TANY && etype != TSTRING { + dowidth(t) + } + Types[etype] = t + } + + s1.Lexical = LNAME + s1.Def = typenod(t) + continue + } + + etype = syms[i].op + if etype != OXXX { + s1 = Pkglookup(syms[i].name, builtinpkg) + s1.Lexical = LNAME + s1.Def = Nod(ONAME, nil, nil) + s1.Def.Sym = s1 + s1.Def.Etype = uint8(etype) + } + } + + // logically, the type of a string literal. + // types[TSTRING] is the named type string + // (the type of x in var x string or var x = "hello"). + // this is the ideal form + // (the type of x in const x = "hello"). + idealstring = typ(TSTRING) + + idealbool = typ(TBOOL) + + s = Pkglookup("true", builtinpkg) + s.Def = Nodbool(true) + s.Def.Sym = Lookup("true") + s.Def.Type = idealbool + + s = Pkglookup("false", builtinpkg) + s.Def = Nodbool(false) + s.Def.Sym = Lookup("false") + s.Def.Type = idealbool + + s = Lookup("_") + s.Block = -100 + s.Def = Nod(ONAME, nil, nil) + s.Def.Sym = s + Types[TBLANK] = typ(TBLANK) + s.Def.Type = Types[TBLANK] + nblank = s.Def + + s = Pkglookup("_", builtinpkg) + s.Block = -100 + s.Def = Nod(ONAME, nil, nil) + s.Def.Sym = s + Types[TBLANK] = typ(TBLANK) + s.Def.Type = Types[TBLANK] + + Types[TNIL] = typ(TNIL) + s = Pkglookup("nil", builtinpkg) + var v Val + v.Ctype = CTNIL + s.Def = nodlit(v) + s.Def.Sym = s +} + +func lexinit1() { + // t = interface { Error() string } + rcvr := typ(TSTRUCT) + + rcvr.Type = typ(TFIELD) + rcvr.Type.Type = Ptrto(typ(TSTRUCT)) + rcvr.Funarg = 1 + in := typ(TSTRUCT) + in.Funarg = 1 + out := typ(TSTRUCT) + out.Type = typ(TFIELD) + out.Type.Type = Types[TSTRING] + out.Funarg = 1 + f := typ(TFUNC) + *getthis(f) = rcvr + *Getoutarg(f) = out + *getinarg(f) = in + f.Thistuple = 1 + f.Intuple = 0 + f.Outnamed = 0 + f.Outtuple = 1 + t := typ(TINTER) + t.Type = typ(TFIELD) + t.Type.Sym = Lookup("Error") + t.Type.Type = f + + // error type + s := Lookup("error") + + s.Lexical = LNAME + s1 := Pkglookup("error", builtinpkg) + errortype = t + errortype.Sym = s1 + s1.Lexical = LNAME + s1.Def = typenod(errortype) + + // byte alias + s = Lookup("byte") + + s.Lexical = LNAME + s1 = Pkglookup("byte", builtinpkg) + bytetype = typ(TUINT8) + bytetype.Sym = s1 + s1.Lexical = LNAME + s1.Def = typenod(bytetype) + + // rune alias + s = Lookup("rune") + + s.Lexical = LNAME + s1 = Pkglookup("rune", builtinpkg) + runetype = typ(TINT32) + runetype.Sym = s1 + s1.Lexical = LNAME + s1.Def = typenod(runetype) +} + +func lexfini() { + var s *Sym + var lex int + var etype int + var i int + + for i = 0; i < len(syms); i++ { + lex = syms[i].lexical + if lex != LNAME { + continue + } + s = Lookup(syms[i].name) + s.Lexical = uint16(lex) + + etype = syms[i].etype + if etype != Txxx && (etype != TANY || Debug['A'] != 0) && s.Def == nil { + s.Def = typenod(Types[etype]) + s.Origpkg = builtinpkg + } + + etype = syms[i].op + if etype != OXXX && s.Def == nil { + s.Def = Nod(ONAME, nil, nil) + s.Def.Sym = s + s.Def.Etype = uint8(etype) + s.Origpkg = builtinpkg + } + } + + // backend-specific builtin types (e.g. int). + for i = range Thearch.Typedefs { + s = Lookup(Thearch.Typedefs[i].Name) + if s.Def == nil { + s.Def = typenod(Types[Thearch.Typedefs[i].Etype]) + s.Origpkg = builtinpkg + } + } + + // there's only so much table-driven we can handle. + // these are special cases. + s = Lookup("byte") + + if s.Def == nil { + s.Def = typenod(bytetype) + s.Origpkg = builtinpkg + } + + s = Lookup("error") + if s.Def == nil { + s.Def = typenod(errortype) + s.Origpkg = builtinpkg + } + + s = Lookup("rune") + if s.Def == nil { + s.Def = typenod(runetype) + s.Origpkg = builtinpkg + } + + s = Lookup("nil") + if s.Def == nil { + var v Val + v.Ctype = CTNIL + s.Def = nodlit(v) + s.Def.Sym = s + s.Origpkg = builtinpkg + } + + s = Lookup("iota") + if s.Def == nil { + s.Def = Nod(OIOTA, nil, nil) + s.Def.Sym = s + s.Origpkg = builtinpkg + } + + s = Lookup("true") + if s.Def == nil { + s.Def = Nodbool(true) + s.Def.Sym = s + s.Origpkg = builtinpkg + } + + s = Lookup("false") + if s.Def == nil { + s.Def = Nodbool(false) + s.Def.Sym = s + s.Origpkg = builtinpkg + } + + nodfp = Nod(ONAME, nil, nil) + nodfp.Type = Types[TINT32] + nodfp.Xoffset = 0 + nodfp.Class = PPARAM + nodfp.Sym = Lookup(".fp") +} + +var lexn = []struct { + lex int + name string +}{ + {LANDAND, "ANDAND"}, + {LANDNOT, "ANDNOT"}, + {LASOP, "ASOP"}, + {LBREAK, "BREAK"}, + {LCASE, "CASE"}, + {LCHAN, "CHAN"}, + {LCOLAS, "COLAS"}, + {LCOMM, "<-"}, + {LCONST, "CONST"}, + {LCONTINUE, "CONTINUE"}, + {LDDD, "..."}, + {LDEC, "DEC"}, + {LDEFAULT, "DEFAULT"}, + {LDEFER, "DEFER"}, + {LELSE, "ELSE"}, + {LEQ, "EQ"}, + {LFALL, "FALL"}, + {LFOR, "FOR"}, + {LFUNC, "FUNC"}, + {LGE, "GE"}, + {LGO, "GO"}, + {LGOTO, "GOTO"}, + {LGT, "GT"}, + {LIF, "IF"}, + {LIMPORT, "IMPORT"}, + {LINC, "INC"}, + {LINTERFACE, "INTERFACE"}, + {LLE, "LE"}, + {LLITERAL, "LITERAL"}, + {LLSH, "LSH"}, + {LLT, "LT"}, + {LMAP, "MAP"}, + {LNAME, "NAME"}, + {LNE, "NE"}, + {LOROR, "OROR"}, + {LPACKAGE, "PACKAGE"}, + {LRANGE, "RANGE"}, + {LRETURN, "RETURN"}, + {LRSH, "RSH"}, + {LSELECT, "SELECT"}, + {LSTRUCT, "STRUCT"}, + {LSWITCH, "SWITCH"}, + {LTYPE, "TYPE"}, + {LVAR, "VAR"}, +} + +func lexname(lex int) string { + for i := 0; i < len(lexn); i++ { + if lexn[i].lex == lex { + return lexn[i].name + } + } + return fmt.Sprintf("LEX-%d", lex) +} + +var yytfix = []struct { + have string + want string +}{ + {"$end", "EOF"}, + {"LASOP", "op="}, + {"LBREAK", "break"}, + {"LCASE", "case"}, + {"LCHAN", "chan"}, + {"LCOLAS", ":="}, + {"LCONST", "const"}, + {"LCONTINUE", "continue"}, + {"LDDD", "..."}, + {"LDEFAULT", "default"}, + {"LDEFER", "defer"}, + {"LELSE", "else"}, + {"LFALL", "fallthrough"}, + {"LFOR", "for"}, + {"LFUNC", "func"}, + {"LGO", "go"}, + {"LGOTO", "goto"}, + {"LIF", "if"}, + {"LIMPORT", "import"}, + {"LINTERFACE", "interface"}, + {"LMAP", "map"}, + {"LNAME", "name"}, + {"LPACKAGE", "package"}, + {"LRANGE", "range"}, + {"LRETURN", "return"}, + {"LSELECT", "select"}, + {"LSTRUCT", "struct"}, + {"LSWITCH", "switch"}, + {"LTYPE", "type"}, + {"LVAR", "var"}, + {"LANDAND", "&&"}, + {"LANDNOT", "&^"}, + {"LBODY", "{"}, + {"LCOMM", "<-"}, + {"LDEC", "--"}, + {"LINC", "++"}, + {"LEQ", "=="}, + {"LGE", ">="}, + {"LGT", ">"}, + {"LLE", "<="}, + {"LLT", "<"}, + {"LLSH", "<<"}, + {"LRSH", ">>"}, + {"LOROR", "||"}, + {"LNE", "!="}, + // spell out to avoid confusion with punctuation in error messages + {"';'", "semicolon or newline"}, + {"','", "comma"}, +} + +func init() { + yyErrorVerbose = true + +Outer: + for i, s := range yyToknames { + // Apply yytfix if possible. + for _, fix := range yytfix { + if s == fix.have { + yyToknames[i] = fix.want + continue Outer + } + } + + // Turn 'x' into x. + if len(s) == 3 && s[0] == '\'' && s[2] == '\'' { + yyToknames[i] = s[1:2] + continue + } + } +} + +func pkgnotused(lineno int, path string, name string) { + // If the package was imported with a name other than the final + // import path element, show it explicitly in the error message. + // Note that this handles both renamed imports and imports of + // packages containing unconventional package declarations. + // Note that this uses / always, even on Windows, because Go import + // paths always use forward slashes. + elem := path + if i := strings.LastIndex(elem, "/"); i >= 0 { + elem = elem[i+1:] + } + if name == "" || elem == name { + yyerrorl(int(lineno), "imported and not used: %q", path) + } else { + yyerrorl(int(lineno), "imported and not used: %q as %s", path, name) + } +} + +func mkpackage(pkgname string) { + if localpkg.Name == "" { + if pkgname == "_" { + Yyerror("invalid package name _") + } + localpkg.Name = pkgname + } else { + if pkgname != localpkg.Name { + Yyerror("package %s; expected %s", pkgname, localpkg.Name) + } + for _, s := range localpkg.Syms { + if s.Def == nil { + continue + } + if s.Def.Op == OPACK { + // throw away top-level package name leftover + // from previous file. + // leave s->block set to cause redeclaration + // errors if a conflicting top-level name is + // introduced by a different file. + if !s.Def.Used && nsyntaxerrors == 0 { + pkgnotused(int(s.Def.Lineno), s.Def.Pkg.Path, s.Name) + } + s.Def = nil + continue + } + + if s.Def.Sym != s { + // throw away top-level name left over + // from previous import . "x" + if s.Def.Pack != nil && !s.Def.Pack.Used && nsyntaxerrors == 0 { + pkgnotused(int(s.Def.Pack.Lineno), s.Def.Pack.Pkg.Path, "") + s.Def.Pack.Used = true + } + + s.Def = nil + continue + } + } + } + + if outfile == "" { + p := infile + if i := strings.LastIndex(p, "/"); i >= 0 { + p = p[i+1:] + } + if Ctxt.Windows != 0 { + if i := strings.LastIndex(p, `\`); i >= 0 { + p = p[i+1:] + } + } + if i := strings.LastIndex(p, "."); i >= 0 { + p = p[:i] + } + outfile = fmt.Sprintf("%s.%c", p, Thearch.Thechar) + } +} diff --git a/src/cmd/compile/internal/gc/mkbuiltin.go b/src/cmd/compile/internal/gc/mkbuiltin.go new file mode 100644 index 0000000000..b2362a6f01 --- /dev/null +++ b/src/cmd/compile/internal/gc/mkbuiltin.go @@ -0,0 +1,104 @@ +// Copyright 2009 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 + +// Generate builtin.go from $* (runtime.go and unsafe.go). +// Run this after changing runtime.go and unsafe.go +// or after changing the export metadata format in the compiler. +// Either way, you need to have a working compiler binary first. +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) + } + defer f.Close() + w := bufio.NewWriter(f) + + fmt.Fprintln(w, "// AUTO-GENERATED by mkbuiltin.go; DO NOT EDIT") + fmt.Fprintln(w, "") + fmt.Fprintln(w, "package gc") + + for _, name := range os.Args[1:] { + mkbuiltin(w, gochar, name) + } + + if err := w.Flush(); err != nil { + log.Fatal(err) + } +} + +// 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 { + log.Fatal(err) + } + obj := fmt.Sprintf("%s.%s", name, gochar) + defer os.Remove(obj) + + r, err := os.Open(obj) + if err != nil { + log.Fatal(err) + } + defer r.Close() + scanner := bufio.NewScanner(r) + + // Look for $$ that introduces imports. + for scanner.Scan() { + if strings.Contains(scanner.Text(), "$$") { + goto Begin + } + } + log.Fatal("did not find beginning of imports") + +Begin: + initfunc := fmt.Sprintf("init_%s_function", name) + + 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. + replacer := strings.NewReplacer("PACKAGE", name) + + // Process imports, stopping at $$ that closes them. + for scanner.Scan() { + p := scanner.Text() + if strings.Contains(p, "$$") { + goto End + } + + // Chop leading white space. + p = strings.TrimLeft(p, " \t") + + // Cut out decl of init_$1_function - it doesn't exist. + if strings.Contains(p, initfunc) { + continue + } + + fmt.Fprintf(w, "\t%q +\n", replacer.Replace(p)+"\n") + } + log.Fatal("did not find end of imports") + +End: + fmt.Fprintf(w, "\t\"$$\\n\"\n") +} diff --git a/src/cmd/compile/internal/gc/mparith2.go b/src/cmd/compile/internal/gc/mparith2.go new file mode 100644 index 0000000000..2c7e5176ac --- /dev/null +++ b/src/cmd/compile/internal/gc/mparith2.go @@ -0,0 +1,300 @@ +// Copyright 2009 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 gc + +import ( + "cmd/compile/internal/big" + "cmd/internal/obj" + "fmt" +) + +/// implements fix arithmetic + +func mpsetovf(a *Mpint) { + a.Val.SetUint64(1) // avoid spurious div-zero errors + a.Ovf = true +} + +func mptestovf(a *Mpint, extra int) bool { + // We don't need to be precise here, any reasonable upper limit would do. + // For now, use existing limit so we pass all the tests unchanged. + if a.Val.BitLen()+extra > Mpprec { + mpsetovf(a) + } + return a.Ovf +} + +func mpmovefixfix(a, b *Mpint) { + a.Val.Set(&b.Val) +} + +func mpmovefltfix(a *Mpint, b *Mpflt) int { + if _, acc := b.Val.Int(&a.Val); acc == big.Exact { + return 0 + } + + const delta = 16 // a reasonably small number of bits > 0 + var t big.Float + t.SetPrec(Mpprec - delta) + + // try rounding down a little + t.SetMode(big.ToZero) + t.Set(&b.Val) + if _, acc := t.Int(&a.Val); acc == big.Exact { + return 0 + } + + // try rounding up a little + t.SetMode(big.AwayFromZero) + t.Set(&b.Val) + if _, acc := t.Int(&a.Val); acc == big.Exact { + return 0 + } + + return -1 +} + +func mpaddfixfix(a, b *Mpint, quiet int) { + if a.Ovf || b.Ovf { + if nsavederrors+nerrors == 0 { + Yyerror("ovf in mpaddfixfix") + } + mpsetovf(a) + return + } + + a.Val.Add(&a.Val, &b.Val) + + if mptestovf(a, 0) && quiet == 0 { + Yyerror("constant addition overflow") + } +} + +func mpsubfixfix(a, b *Mpint) { + if a.Ovf || b.Ovf { + if nsavederrors+nerrors == 0 { + Yyerror("ovf in mpsubfixfix") + } + mpsetovf(a) + return + } + + a.Val.Sub(&a.Val, &b.Val) + + if mptestovf(a, 0) { + Yyerror("constant subtraction overflow") + } +} + +func mpmulfixfix(a, b *Mpint) { + if a.Ovf || b.Ovf { + if nsavederrors+nerrors == 0 { + Yyerror("ovf in mpmulfixfix") + } + mpsetovf(a) + return + } + + a.Val.Mul(&a.Val, &b.Val) + + if mptestovf(a, 0) { + Yyerror("constant multiplication overflow") + } +} + +func mpdivfixfix(a, b *Mpint) { + if a.Ovf || b.Ovf { + if nsavederrors+nerrors == 0 { + Yyerror("ovf in mpdivfixfix") + } + mpsetovf(a) + return + } + + a.Val.Quo(&a.Val, &b.Val) + + if mptestovf(a, 0) { + // can only happen for div-0 which should be checked elsewhere + Yyerror("constant division overflow") + } +} + +func mpmodfixfix(a, b *Mpint) { + if a.Ovf || b.Ovf { + if nsavederrors+nerrors == 0 { + Yyerror("ovf in mpmodfixfix") + } + mpsetovf(a) + return + } + + a.Val.Rem(&a.Val, &b.Val) + + if mptestovf(a, 0) { + // should never happen + Yyerror("constant modulo overflow") + } +} + +func mporfixfix(a, b *Mpint) { + if a.Ovf || b.Ovf { + if nsavederrors+nerrors == 0 { + Yyerror("ovf in mporfixfix") + } + mpsetovf(a) + return + } + + a.Val.Or(&a.Val, &b.Val) +} + +func mpandfixfix(a, b *Mpint) { + if a.Ovf || b.Ovf { + if nsavederrors+nerrors == 0 { + Yyerror("ovf in mpandfixfix") + } + mpsetovf(a) + return + } + + a.Val.And(&a.Val, &b.Val) +} + +func mpandnotfixfix(a, b *Mpint) { + if a.Ovf || b.Ovf { + if nsavederrors+nerrors == 0 { + Yyerror("ovf in mpandnotfixfix") + } + mpsetovf(a) + return + } + + a.Val.AndNot(&a.Val, &b.Val) +} + +func mpxorfixfix(a, b *Mpint) { + if a.Ovf || b.Ovf { + if nsavederrors+nerrors == 0 { + Yyerror("ovf in mpxorfixfix") + } + mpsetovf(a) + return + } + + a.Val.Xor(&a.Val, &b.Val) +} + +// shift left by s (or right by -s) +func Mpshiftfix(a *Mpint, s int) { + switch { + case s > 0: + if mptestovf(a, s) { + Yyerror("constant shift overflow") + return + } + a.Val.Lsh(&a.Val, uint(s)) + case s < 0: + a.Val.Rsh(&a.Val, uint(-s)) + } +} + +func mplshfixfix(a, b *Mpint) { + if a.Ovf || b.Ovf { + if nsavederrors+nerrors == 0 { + Yyerror("ovf in mplshfixfix") + } + mpsetovf(a) + return + } + + s := Mpgetfix(b) + if s < 0 || s >= Mpprec { + Yyerror("stupid shift: %d", s) + Mpmovecfix(a, 0) + return + } + + Mpshiftfix(a, int(s)) +} + +func mprshfixfix(a, b *Mpint) { + if a.Ovf || b.Ovf { + if nsavederrors+nerrors == 0 { + Yyerror("ovf in mprshfixfix") + } + mpsetovf(a) + return + } + + s := Mpgetfix(b) + if s < 0 || s >= Mpprec { + Yyerror("stupid shift: %d", s) + if a.Val.Sign() < 0 { + Mpmovecfix(a, -1) + } else { + Mpmovecfix(a, 0) + } + return + } + + Mpshiftfix(a, int(-s)) +} + +func Mpcmpfixfix(a, b *Mpint) int { + return a.Val.Cmp(&b.Val) +} + +func mpcmpfixc(b *Mpint, c int64) int { + return b.Val.Cmp(big.NewInt(c)) +} + +func mpnegfix(a *Mpint) { + a.Val.Neg(&a.Val) +} + +func Mpgetfix(a *Mpint) int64 { + if a.Ovf { + if nsavederrors+nerrors == 0 { + Yyerror("constant overflow") + } + return 0 + } + + return a.Val.Int64() +} + +func Mpmovecfix(a *Mpint, c int64) { + a.Val.SetInt64(c) +} + +func mpatofix(a *Mpint, as string) { + _, ok := a.Val.SetString(as, 0) + if !ok { + // required syntax is [+-][0[x]]d* + // At the moment we lose precise error cause; + // the old code distinguished between: + // - malformed hex constant + // - malformed octal constant + // - malformed decimal constant + // TODO(gri) use different conversion function + Yyerror("malformed integer constant: %s", as) + a.Val.SetUint64(0) + return + } + if mptestovf(a, 0) { + Yyerror("constant too large: %s", as) + } +} + +func (x *Mpint) String() string { + return Bconv(x, 0) +} + +func Bconv(xval *Mpint, flag int) string { + if flag&obj.FmtSharp != 0 { + return fmt.Sprintf("%#x", &xval.Val) + } + return xval.Val.String() +} diff --git a/src/cmd/compile/internal/gc/mparith3.go b/src/cmd/compile/internal/gc/mparith3.go new file mode 100644 index 0000000000..181e91c87d --- /dev/null +++ b/src/cmd/compile/internal/gc/mparith3.go @@ -0,0 +1,235 @@ +// Copyright 2009 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 gc + +import ( + "cmd/compile/internal/big" + "cmd/internal/obj" + "fmt" + "math" +) + +/// implements float arihmetic + +func newMpflt() *Mpflt { + var a Mpflt + a.Val.SetPrec(Mpprec) + return &a +} + +func Mpmovefixflt(a *Mpflt, b *Mpint) { + if b.Ovf { + // sign doesn't really matter but copy anyway + a.Val.SetInf(b.Val.Sign() < 0) + return + } + a.Val.SetInt(&b.Val) +} + +func mpmovefltflt(a *Mpflt, b *Mpflt) { + a.Val.Set(&b.Val) +} + +func mpaddfltflt(a *Mpflt, b *Mpflt) { + if Mpdebug { + fmt.Printf("\n%v + %v", a, b) + } + + a.Val.Add(&a.Val, &b.Val) + + if Mpdebug { + fmt.Printf(" = %v\n\n", a) + } +} + +func mpaddcflt(a *Mpflt, c float64) { + var b Mpflt + + Mpmovecflt(&b, c) + mpaddfltflt(a, &b) +} + +func mpsubfltflt(a *Mpflt, b *Mpflt) { + if Mpdebug { + fmt.Printf("\n%v - %v", a, b) + } + + a.Val.Sub(&a.Val, &b.Val) + + if Mpdebug { + fmt.Printf(" = %v\n\n", a) + } +} + +func mpmulfltflt(a *Mpflt, b *Mpflt) { + if Mpdebug { + fmt.Printf("%v\n * %v\n", a, b) + } + + a.Val.Mul(&a.Val, &b.Val) + + if Mpdebug { + fmt.Printf(" = %v\n\n", a) + } +} + +func mpmulcflt(a *Mpflt, c float64) { + var b Mpflt + + Mpmovecflt(&b, c) + mpmulfltflt(a, &b) +} + +func mpdivfltflt(a *Mpflt, b *Mpflt) { + if Mpdebug { + fmt.Printf("%v\n / %v\n", a, b) + } + + a.Val.Quo(&a.Val, &b.Val) + + if Mpdebug { + fmt.Printf(" = %v\n\n", a) + } +} + +func mpcmpfltflt(a *Mpflt, b *Mpflt) int { + return a.Val.Cmp(&b.Val) +} + +func mpcmpfltc(b *Mpflt, c float64) int { + var a Mpflt + + Mpmovecflt(&a, c) + 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: + // 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() + default: + panic("unreachable") + } + + // check for overflow + if math.IsInf(x, 0) && nsavederrors+nerrors == 0 { + Yyerror("mpgetflt ovf") + } + + return x +} + +func mpgetflt(a *Mpflt) float64 { + return mpgetfltN(a, 53, -1023) +} + +func mpgetflt32(a *Mpflt) float64 { + return mpgetfltN(a, 24, -127) +} + +func Mpmovecflt(a *Mpflt, c float64) { + if Mpdebug { + fmt.Printf("\nconst %g", c) + } + + a.Val.SetFloat64(c) + + if Mpdebug { + fmt.Printf(" = %v\n", a) + } +} + +func mpnegflt(a *Mpflt) { + a.Val.Neg(&a.Val) +} + +// +// floating point input +// required syntax is [+-]d*[.]d*[e[+-]d*] or [+-]0xH*[e[+-]d*] +// +func mpatoflt(a *Mpflt, as string) { + for len(as) > 0 && (as[0] == ' ' || as[0] == '\t') { + as = as[1:] + } + + f, ok := a.Val.SetString(as) + if !ok { + // At the moment we lose precise error cause; + // the old code additionally distinguished between: + // - malformed hex constant + // - decimal point in hex constant + // - constant exponent out of range + // - decimal point and binary point in constant + // TODO(gri) use different conversion function or check separately + Yyerror("malformed constant: %s", as) + a.Val.SetUint64(0) + } + + if f.IsInf() { + Yyerror("constant too large: %s", as) + a.Val.SetUint64(0) + } +} + +func (f *Mpflt) String() string { + return Fconv(f, 0) +} + +func Fconv(fvp *Mpflt, flag int) string { + if flag&obj.FmtSharp == 0 { + return fvp.Val.Format('b', 0) + } + + // use decimal format for error messages + + // determine sign + f := &fvp.Val + var sign string + if fvp.Val.Signbit() { + sign = "-" + f = new(big.Float).Abs(f) + } else if flag&obj.FmtSign != 0 { + sign = "+" + } + + // Use fmt formatting if in float64 range (common case). + if x, _ := f.Float64(); !math.IsInf(x, 0) { + return fmt.Sprintf("%s%.6g", sign, x) + } + + // Out of float64 range. Do approximate manual to decimal + // conversion to avoid precise but possibly slow Float + // formatting. The exponent is > 0 since a negative out- + // of-range exponent would have underflowed and led to 0. + // f = mant * 2**exp + var mant big.Float + exp := float64(f.MantExp(&mant)) // 0.5 <= mant < 1.0, exp > 0 + + // approximate float64 mantissa m and decimal exponent d + // f ~ m * 10**d + m, _ := mant.Float64() // 0.5 <= m < 1.0 + d := exp * (math.Ln2 / math.Ln10) // log_10(2) + + // adjust m for truncated (integer) decimal exponent e + e := int64(d) + m *= math.Pow(10, d-float64(e)) + for m >= 10 { + m /= 10 + e++ + } + + return fmt.Sprintf("%s%.5fe+%d", sign, m, e) +} diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go new file mode 100644 index 0000000000..2afd786dc1 --- /dev/null +++ b/src/cmd/compile/internal/gc/obj.go @@ -0,0 +1,434 @@ +// Copyright 2009 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 gc + +import ( + "cmd/internal/obj" + "fmt" + "strconv" +) + +/* + * architecture-independent object file output + */ +const ( + ArhdrSize = 60 +) + +func formathdr(arhdr []byte, name string, size int64) { + copy(arhdr[:], fmt.Sprintf("%-16s%-12d%-6d%-6d%-8o%-10d`\n", name, 0, 0, 0, 0644, size)) +} + +func dumpobj() { + var err error + bout, err = obj.Bopenw(outfile) + if err != nil { + Flusherrors() + fmt.Printf("can't create %s: %v\n", outfile, err) + errorexit() + } + + startobj := int64(0) + var arhdr [ArhdrSize]byte + if writearchive != 0 { + obj.Bwritestring(bout, "!\n") + arhdr = [ArhdrSize]byte{} + bout.Write(arhdr[:]) + startobj = obj.Boffset(bout) + } + + fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring()) + dumpexport() + + if writearchive != 0 { + bout.Flush() + size := obj.Boffset(bout) - startobj + if size&1 != 0 { + obj.Bputc(bout, 0) + } + obj.Bseek(bout, startobj-ArhdrSize, 0) + formathdr(arhdr[:], "__.PKGDEF", size) + bout.Write(arhdr[:]) + bout.Flush() + + obj.Bseek(bout, startobj+size+(size&1), 0) + arhdr = [ArhdrSize]byte{} + bout.Write(arhdr[:]) + startobj = obj.Boffset(bout) + fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring()) + } + + if pragcgobuf != "" { + if writearchive != 0 { + // write empty export section; must be before cgo section + fmt.Fprintf(bout, "\n$$\n\n$$\n\n") + } + + fmt.Fprintf(bout, "\n$$ // cgo\n") + fmt.Fprintf(bout, "%s\n$$\n\n", pragcgobuf) + } + + fmt.Fprintf(bout, "\n!\n") + + var externs *NodeList + if externdcl != nil { + externs = externdcl.End + } + + dumpglobls() + dumptypestructs() + + // Dump extra globals. + tmp := externdcl + + if externs != nil { + externdcl = externs.Next + } + dumpglobls() + externdcl = tmp + + zero := Pkglookup("zerovalue", Runtimepkg) + ggloblsym(zero, int32(zerosize), obj.DUPOK|obj.RODATA) + + dumpdata() + obj.Writeobjdirect(Ctxt, bout) + + if writearchive != 0 { + bout.Flush() + size := obj.Boffset(bout) - startobj + if size&1 != 0 { + obj.Bputc(bout, 0) + } + obj.Bseek(bout, startobj-ArhdrSize, 0) + name := fmt.Sprintf("_go_.%c", Thearch.Thechar) + formathdr(arhdr[:], name, size) + bout.Write(arhdr[:]) + } + + obj.Bterm(bout) +} + +func dumpglobls() { + var n *Node + + // add globals + for l := externdcl; l != nil; l = l.Next { + n = l.N + if n.Op != ONAME { + continue + } + + if n.Type == nil { + Fatal("external %v nil type\n", n) + } + if n.Class == PFUNC { + continue + } + if n.Sym.Pkg != localpkg { + continue + } + dowidth(n.Type) + + ggloblnod(n) + } + + for l := funcsyms; l != nil; l = l.Next { + n = l.N + dsymptr(n.Sym, 0, n.Sym.Def.Func.Shortname.Sym, 0) + ggloblsym(n.Sym, int32(Widthptr), obj.DUPOK|obj.RODATA) + } + + // Do not reprocess funcsyms on next dumpglobls call. + funcsyms = nil +} + +func Bputname(b *obj.Biobuf, s *obj.LSym) { + obj.Bwritestring(b, s.Name) + obj.Bputc(b, 0) +} + +func Linksym(s *Sym) *obj.LSym { + if s == nil { + return nil + } + if s.Lsym != nil { + return s.Lsym + } + var name string + if isblanksym(s) { + name = "_" + } else if s.Linkname != "" { + name = s.Linkname + } else { + name = s.Pkg.Prefix + "." + s.Name + } + + ls := obj.Linklookup(Ctxt, name, 0) + s.Lsym = ls + return ls +} + +func duintxx(s *Sym, off int, v uint64, wid int) int { + // Update symbol data directly instead of generating a + // DATA instruction that liblink will have to interpret later. + // This reduces compilation time and memory usage. + off = int(Rnd(int64(off), int64(wid))) + + return int(obj.Setuintxx(Ctxt, Linksym(s), int64(off), v, int64(wid))) +} + +func duint8(s *Sym, off int, v uint8) int { + return duintxx(s, off, uint64(v), 1) +} + +func duint16(s *Sym, off int, v uint16) int { + return duintxx(s, off, uint64(v), 2) +} + +func duint32(s *Sym, off int, v uint32) int { + return duintxx(s, off, uint64(v), 4) +} + +func duint64(s *Sym, off int, v uint64) int { + return duintxx(s, off, v, 8) +} + +func duintptr(s *Sym, off int, v uint64) int { + return duintxx(s, off, v, Widthptr) +} + +var stringsym_gen int + +func stringsym(s string) *Sym { + var symname string + var pkg *Pkg + if len(s) > 100 { + // huge strings are made static to avoid long names + stringsym_gen++ + symname = fmt.Sprintf(".gostring.%d", stringsym_gen) + + pkg = localpkg + } else { + // small strings get named by their contents, + // so that multiple modules using the same string + // can share it. + symname = strconv.Quote(s) + pkg = gostringpkg + } + + sym := Pkglookup(symname, pkg) + + // SymUniq flag indicates that data is generated already + if sym.Flags&SymUniq != 0 { + return sym + } + sym.Flags |= SymUniq + sym.Def = newname(sym) + + off := 0 + + // string header + off = dsymptr(sym, off, sym, Widthptr+Widthint) + off = duintxx(sym, off, uint64(len(s)), Widthint) + + // string data + var m int + for n := 0; n < len(s); n += m { + m = 8 + if m > len(s)-n { + m = len(s) - n + } + off = dsname(sym, off, s[n:n+m]) + } + + off = duint8(sym, off, 0) // terminating NUL for runtime + off = (off + Widthptr - 1) &^ (Widthptr - 1) // round to pointer alignment + ggloblsym(sym, int32(off), obj.DUPOK|obj.RODATA|obj.LOCAL) + + return sym +} + +var slicebytes_gen int + +func slicebytes(nam *Node, s string, len int) { + var m int + + slicebytes_gen++ + symname := fmt.Sprintf(".gobytes.%d", slicebytes_gen) + sym := Pkglookup(symname, localpkg) + sym.Def = newname(sym) + + off := 0 + for n := 0; n < len; n += m { + m = 8 + if m > len-n { + m = len - n + } + off = dsname(sym, off, s[n:n+m]) + } + + ggloblsym(sym, int32(off), obj.NOPTR|obj.LOCAL) + + if nam.Op != ONAME { + Fatal("slicebytes %v", nam) + } + off = int(nam.Xoffset) + off = dsymptr(nam.Sym, off, sym, 0) + off = duintxx(nam.Sym, off, uint64(len), Widthint) + duintxx(nam.Sym, off, uint64(len), Widthint) +} + +func dstringptr(s *Sym, off int, str string) int { + off = int(Rnd(int64(off), int64(Widthptr))) + p := Thearch.Gins(obj.ADATA, nil, nil) + p.From.Type = obj.TYPE_MEM + p.From.Name = obj.NAME_EXTERN + p.From.Sym = Linksym(s) + p.From.Offset = int64(off) + p.From3.Type = obj.TYPE_CONST + p.From3.Offset = int64(Widthptr) + + Datastring(str+"\x00", &p.To) // TODO(rsc): Remove NUL + p.To.Type = obj.TYPE_ADDR + p.To.Etype = Simtype[TINT] + off += Widthptr + + return off +} + +func Datastring(s string, a *obj.Addr) { + sym := stringsym(s) + a.Type = obj.TYPE_MEM + a.Name = obj.NAME_EXTERN + a.Sym = Linksym(sym) + a.Node = sym.Def + a.Offset = int64(Widthptr) + int64(Widthint) // skip header + a.Etype = Simtype[TINT] +} + +func datagostring(sval string, a *obj.Addr) { + sym := stringsym(sval) + a.Type = obj.TYPE_MEM + a.Name = obj.NAME_EXTERN + a.Sym = Linksym(sym) + a.Node = sym.Def + a.Offset = 0 // header + a.Etype = TSTRING +} + +func dgostringptr(s *Sym, off int, str string) int { + if str == "" { + return duintptr(s, off, 0) + } + return dgostrlitptr(s, off, &str) +} + +func dgostrlitptr(s *Sym, off int, lit *string) int { + if lit == nil { + return duintptr(s, off, 0) + } + off = int(Rnd(int64(off), int64(Widthptr))) + p := Thearch.Gins(obj.ADATA, nil, nil) + p.From.Type = obj.TYPE_MEM + p.From.Name = obj.NAME_EXTERN + p.From.Sym = Linksym(s) + p.From.Offset = int64(off) + p.From3.Type = obj.TYPE_CONST + p.From3.Offset = int64(Widthptr) + datagostring(*lit, &p.To) + p.To.Type = obj.TYPE_ADDR + p.To.Etype = Simtype[TINT] + off += Widthptr + + return off +} + +func dsname(s *Sym, off int, t string) int { + p := Thearch.Gins(obj.ADATA, nil, nil) + p.From.Type = obj.TYPE_MEM + p.From.Name = obj.NAME_EXTERN + p.From.Offset = int64(off) + p.From.Sym = Linksym(s) + p.From3.Type = obj.TYPE_CONST + p.From3.Offset = int64(len(t)) + + p.To.Type = obj.TYPE_SCONST + p.To.Val = t + return off + len(t) +} + +func dsymptr(s *Sym, off int, x *Sym, xoff int) int { + off = int(Rnd(int64(off), int64(Widthptr))) + + p := Thearch.Gins(obj.ADATA, nil, nil) + p.From.Type = obj.TYPE_MEM + p.From.Name = obj.NAME_EXTERN + p.From.Sym = Linksym(s) + p.From.Offset = int64(off) + p.From3.Type = obj.TYPE_CONST + p.From3.Offset = int64(Widthptr) + p.To.Type = obj.TYPE_ADDR + p.To.Name = obj.NAME_EXTERN + p.To.Sym = Linksym(x) + p.To.Offset = int64(xoff) + off += Widthptr + + return off +} + +func gdata(nam *Node, nr *Node, wid int) { + if nr.Op == OLITERAL { + switch nr.Val.Ctype { + case CTCPLX: + gdatacomplex(nam, nr.Val.U.(*Mpcplx)) + return + + case CTSTR: + gdatastring(nam, nr.Val.U.(string)) + return + } + } + + p := Thearch.Gins(obj.ADATA, nam, nr) + p.From3.Type = obj.TYPE_CONST + p.From3.Offset = int64(wid) +} + +func gdatacomplex(nam *Node, cval *Mpcplx) { + w := cplxsubtype(int(nam.Type.Etype)) + w = int(Types[w].Width) + + p := Thearch.Gins(obj.ADATA, nam, nil) + p.From3.Type = obj.TYPE_CONST + p.From3.Offset = int64(w) + p.To.Type = obj.TYPE_FCONST + p.To.Val = mpgetflt(&cval.Real) + + p = Thearch.Gins(obj.ADATA, nam, nil) + p.From3.Type = obj.TYPE_CONST + p.From3.Offset = int64(w) + p.From.Offset += int64(w) + p.To.Type = obj.TYPE_FCONST + p.To.Val = mpgetflt(&cval.Imag) +} + +func gdatastring(nam *Node, sval string) { + var nod1 Node + + p := Thearch.Gins(obj.ADATA, nam, nil) + Datastring(sval, &p.To) + p.From3.Type = obj.TYPE_CONST + p.From3.Offset = Types[Tptr].Width + p.To.Type = obj.TYPE_ADDR + + //print("%P\n", p); + + Nodconst(&nod1, Types[TINT], int64(len(sval))) + + p = Thearch.Gins(obj.ADATA, nam, &nod1) + p.From3.Type = obj.TYPE_CONST + p.From3.Offset = int64(Widthint) + p.From.Offset += int64(Widthptr) +} diff --git a/src/cmd/compile/internal/gc/opnames.go b/src/cmd/compile/internal/gc/opnames.go new file mode 100644 index 0000000000..fc03ec6666 --- /dev/null +++ b/src/cmd/compile/internal/gc/opnames.go @@ -0,0 +1,162 @@ +// Copyright 2011 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 gc + +// auto generated by go tool dist +var opnames = []string{ + OXXX: "XXX", + ONAME: "NAME", + ONONAME: "NONAME", + OTYPE: "TYPE", + OPACK: "PACK", + OLITERAL: "LITERAL", + OADD: "ADD", + OSUB: "SUB", + OOR: "OR", + OXOR: "XOR", + OADDSTR: "ADDSTR", + OADDR: "ADDR", + OANDAND: "ANDAND", + OAPPEND: "APPEND", + OARRAYBYTESTR: "ARRAYBYTESTR", + OARRAYBYTESTRTMP: "ARRAYBYTESTRTMP", + OARRAYRUNESTR: "ARRAYRUNESTR", + OSTRARRAYBYTE: "STRARRAYBYTE", + OSTRARRAYBYTETMP: "STRARRAYBYTETMP", + OSTRARRAYRUNE: "STRARRAYRUNE", + OAS: "AS", + OAS2: "AS2", + OAS2FUNC: "AS2FUNC", + OAS2RECV: "AS2RECV", + OAS2MAPR: "AS2MAPR", + OAS2DOTTYPE: "AS2DOTTYPE", + OASOP: "ASOP", + OCALL: "CALL", + OCALLFUNC: "CALLFUNC", + OCALLMETH: "CALLMETH", + OCALLINTER: "CALLINTER", + OCALLPART: "CALLPART", + OCAP: "CAP", + OCLOSE: "CLOSE", + OCLOSURE: "CLOSURE", + OCMPIFACE: "CMPIFACE", + OCMPSTR: "CMPSTR", + OCOMPLIT: "COMPLIT", + OMAPLIT: "MAPLIT", + OSTRUCTLIT: "STRUCTLIT", + OARRAYLIT: "ARRAYLIT", + OPTRLIT: "PTRLIT", + OCONV: "CONV", + OCONVIFACE: "CONVIFACE", + OCONVNOP: "CONVNOP", + OCOPY: "COPY", + ODCL: "DCL", + ODCLFUNC: "DCLFUNC", + ODCLFIELD: "DCLFIELD", + ODCLCONST: "DCLCONST", + ODCLTYPE: "DCLTYPE", + ODELETE: "DELETE", + ODOT: "DOT", + ODOTPTR: "DOTPTR", + ODOTMETH: "DOTMETH", + ODOTINTER: "DOTINTER", + OXDOT: "XDOT", + ODOTTYPE: "DOTTYPE", + ODOTTYPE2: "DOTTYPE2", + OEQ: "EQ", + ONE: "NE", + OLT: "LT", + OLE: "LE", + OGE: "GE", + OGT: "GT", + OIND: "IND", + OINDEX: "INDEX", + OINDEXMAP: "INDEXMAP", + OKEY: "KEY", + OPARAM: "PARAM", + OLEN: "LEN", + OMAKE: "MAKE", + OMAKECHAN: "MAKECHAN", + OMAKEMAP: "MAKEMAP", + OMAKESLICE: "MAKESLICE", + OMUL: "MUL", + ODIV: "DIV", + OMOD: "MOD", + OLSH: "LSH", + ORSH: "RSH", + OAND: "AND", + OANDNOT: "ANDNOT", + ONEW: "NEW", + ONOT: "NOT", + OCOM: "COM", + OPLUS: "PLUS", + OMINUS: "MINUS", + OOROR: "OROR", + OPANIC: "PANIC", + OPRINT: "PRINT", + OPRINTN: "PRINTN", + OPAREN: "PAREN", + OSEND: "SEND", + OSLICE: "SLICE", + OSLICEARR: "SLICEARR", + OSLICESTR: "SLICESTR", + OSLICE3: "SLICE3", + OSLICE3ARR: "SLICE3ARR", + ORECOVER: "RECOVER", + ORECV: "RECV", + ORUNESTR: "RUNESTR", + OSELRECV: "SELRECV", + OSELRECV2: "SELRECV2", + OIOTA: "IOTA", + OREAL: "REAL", + OIMAG: "IMAG", + OCOMPLEX: "COMPLEX", + OBLOCK: "BLOCK", + OBREAK: "BREAK", + OCASE: "CASE", + OXCASE: "XCASE", + OCONTINUE: "CONTINUE", + ODEFER: "DEFER", + OEMPTY: "EMPTY", + OFALL: "FALL", + OXFALL: "XFALL", + OFOR: "FOR", + OGOTO: "GOTO", + OIF: "IF", + OLABEL: "LABEL", + OPROC: "PROC", + ORANGE: "RANGE", + ORETURN: "RETURN", + OSELECT: "SELECT", + OSWITCH: "SWITCH", + OTYPESW: "TYPESW", + OTCHAN: "TCHAN", + OTMAP: "TMAP", + OTSTRUCT: "TSTRUCT", + OTINTER: "TINTER", + OTFUNC: "TFUNC", + OTARRAY: "TARRAY", + ODDD: "DDD", + ODDDARG: "DDDARG", + OINLCALL: "INLCALL", + OEFACE: "EFACE", + OITAB: "ITAB", + OSPTR: "SPTR", + OCLOSUREVAR: "CLOSUREVAR", + OCFUNC: "CFUNC", + OCHECKNIL: "CHECKNIL", + OVARKILL: "VARKILL", + OREGISTER: "REGISTER", + OINDREG: "INDREG", + OCMP: "CMP", + ODEC: "DEC", + OINC: "INC", + OEXTEND: "EXTEND", + OHMUL: "HMUL", + OLROT: "LROT", + ORROTC: "RROTC", + ORETJMP: "RETJMP", + OEND: "END", +} diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go new file mode 100644 index 0000000000..b3fd282a68 --- /dev/null +++ b/src/cmd/compile/internal/gc/order.go @@ -0,0 +1,1170 @@ +// Copyright 2012 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 gc + +import ( + "fmt" + "strings" +) + +// Rewrite tree to use separate statements to enforce +// order of evaluation. Makes walk easier, because it +// can (after this runs) reorder at will within an expression. +// +// Rewrite x op= y into x = x op y. +// +// Introduce temporaries as needed by runtime routines. +// For example, the map runtime routines take the map key +// by reference, so make sure all map keys are addressable +// by copying them to temporaries as needed. +// The same is true for channel operations. +// +// Arrange that map index expressions only appear in direct +// assignments x = m[k] or m[k] = x, never in larger expressions. +// +// Arrange that receive expressions only appear in direct assignments +// x = <-c or as standalone statements <-c, never in larger expressions. + +// TODO(rsc): The temporary introduction during multiple assignments +// should be moved into this file, so that the temporaries can be cleaned +// and so that conversions implicit in the OAS2FUNC and OAS2RECV +// nodes can be made explicit and then have their temporaries cleaned. + +// TODO(rsc): Goto and multilevel break/continue can jump over +// inserted VARKILL annotations. Work out a way to handle these. +// The current implementation is safe, in that it will execute correctly. +// But it won't reuse temporaries as aggressively as it might, and +// it can result in unnecessary zeroing of those variables in the function +// prologue. + +// Order holds state during the ordering process. +type Order struct { + out *NodeList // list of generated statements + temp *NodeList // head of stack of temporary variables + free *NodeList // free list of NodeList* structs (for use in temp) +} + +// Order rewrites fn->nbody to apply the ordering constraints +// described in the comment at the top of the file. +func order(fn *Node) { + if Debug['W'] > 1 { + s := fmt.Sprintf("\nbefore order %v", fn.Nname.Sym) + dumplist(s, fn.Nbody) + } + + orderblock(&fn.Nbody) +} + +// Ordertemp allocates a new temporary with the given type, +// pushes it onto the temp stack, and returns it. +// If clear is true, ordertemp emits code to zero the temporary. +func ordertemp(t *Type, order *Order, clear bool) *Node { + var_ := temp(t) + if clear { + a := Nod(OAS, var_, nil) + typecheck(&a, Etop) + order.out = list(order.out, a) + } + + l := order.free + if l == nil { + l = new(NodeList) + } + order.free = l.Next + l.Next = order.temp + l.N = var_ + order.temp = l + return var_ +} + +// Ordercopyexpr behaves like ordertemp but also emits +// code to initialize the temporary to the value n. +// +// The clear argument is provided for use when the evaluation +// of tmp = n turns into a function call that is passed a pointer +// to the temporary as the output space. If the call blocks before +// tmp has been written, the garbage collector will still treat the +// temporary as live, so we must zero it before entering that call. +// Today, this only happens for channel receive operations. +// (The other candidate would be map access, but map access +// returns a pointer to the result data instead of taking a pointer +// to be filled in.) +func ordercopyexpr(n *Node, t *Type, order *Order, clear int) *Node { + var_ := ordertemp(t, order, clear != 0) + a := Nod(OAS, var_, n) + typecheck(&a, Etop) + order.out = list(order.out, a) + return var_ +} + +// Ordercheapexpr returns a cheap version of n. +// The definition of cheap is that n is a variable or constant. +// 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) +} + +// Ordersafeexpr returns a safe version of n. +// The definition of safe is that n can appear multiple times +// without violating the semantics of the original program, +// and that assigning to the safe version has the same effect +// as assigning to the original n. +// +// The intended use is to apply to x when rewriting x += y into x = x + y. +func ordersafeexpr(n *Node, order *Order) *Node { + switch n.Op { + case ONAME, OLITERAL: + return n + + case ODOT, OLEN, OCAP: + l := ordersafeexpr(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 + + case ODOTPTR, OIND: + 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 + + case OINDEX, OINDEXMAP: + var l *Node + if Isfixedarray(n.Left.Type) { + l = ordersafeexpr(n.Left, order) + } else { + l = ordercheapexpr(n.Left, order) + } + r := ordercheapexpr(n.Right, order) + if l == n.Left && r == n.Right { + return n + } + a := Nod(OXXX, nil, nil) + *a = *n + a.Orig = a + a.Left = l + a.Right = r + typecheck(&a, Erv) + return a + } + + Fatal("ordersafeexpr %v", Oconv(int(n.Op), 0)) + return nil // not reached +} + +// Istemp reports whether n is a temporary variable. +func istemp(n *Node) bool { + if n.Op != ONAME { + return false + } + return strings.HasPrefix(n.Sym.Name, "autotmp_") +} + +// Isaddrokay reports whether it is okay to pass n's address to runtime routines. +// Taking the address of a variable makes the liveness and optimization analyses +// lose track of where the variable's lifetime ends. To avoid hurting the analyses +// of ordinary stack variables, those are not 'isaddrokay'. Temporaries are okay, +// because we emit explicit VARKILL instructions marking the end of those +// temporaries' lifetimes. +func isaddrokay(n *Node) bool { + return islvalue(n) && (n.Op != ONAME || n.Class == PEXTERN || istemp(n)) +} + +// Orderaddrtemp ensures that *np is okay to pass by address to runtime routines. +// If the original argument *np is not okay, orderaddrtemp creates a tmp, emits +// tmp = *np, and then sets *np to the tmp variable. +func orderaddrtemp(np **Node, order *Order) { + n := *np + if isaddrokay(n) { + return + } + *np = ordercopyexpr(n, n.Type, order, 0) +} + +// Marktemp returns the top of the temporary variable stack. +func marktemp(order *Order) *NodeList { + return order.temp +} + +// Poptemp pops temporaries off the stack until reaching the mark, +// which must have been returned by marktemp. +func poptemp(mark *NodeList, order *Order) { + var l *NodeList + + for { + l = order.temp + if l == mark { + break + } + order.temp = l.Next + l.Next = order.free + order.free = l + } +} + +// Cleantempnopop emits to *out VARKILL instructions for each temporary +// above the mark on the temporary stack, but it does not pop them +// from the stack. +func cleantempnopop(mark *NodeList, order *Order, out **NodeList) { + var kill *Node + + for l := order.temp; l != mark; l = l.Next { + kill = Nod(OVARKILL, l.N, nil) + typecheck(&kill, Etop) + *out = list(*out, kill) + } +} + +// Cleantemp emits VARKILL instructions for each temporary above the +// mark on the temporary stack and removes them from the stack. +func cleantemp(top *NodeList, order *Order) { + cleantempnopop(top, order, &order.out) + poptemp(top, order) +} + +// Orderstmtlist orders each of the statements in the list. +func orderstmtlist(l *NodeList, order *Order) { + for ; l != nil; l = l.Next { + orderstmt(l.N, order) + } +} + +// Orderblock orders the block of statements *l onto a new list, +// and then replaces *l with that list. +func orderblock(l **NodeList) { + var order Order + mark := marktemp(&order) + orderstmtlist(*l, &order) + cleantemp(mark, &order) + *l = order.out +} + +// Orderexprinplace orders the side effects in *np and +// leaves them as the init list of the final *np. +func orderexprinplace(np **Node, outer *Order) { + n := *np + var order Order + orderexpr(&n, &order, nil) + addinit(&n, order.out) + + // insert new temporaries from order + // at head of outer list. + lp := &order.temp + + for *lp != nil { + lp = &(*lp).Next + } + *lp = outer.temp + outer.temp = order.temp + + *np = n +} + +// Orderstmtinplace orders the side effects of the single statement *np +// and replaces it with the resulting statement list. +func orderstmtinplace(np **Node) { + n := *np + var order Order + mark := marktemp(&order) + orderstmt(n, &order) + cleantemp(mark, &order) + *np = liststmt(order.out) +} + +// Orderinit moves n's init list to order->out. +func orderinit(n *Node, order *Order) { + orderstmtlist(n.Ninit, order) + n.Ninit = nil +} + +// Ismulticall reports whether the list l is f() for a multi-value function. +// Such an f() could appear as the lone argument to a multi-arg function. +func ismulticall(l *NodeList) bool { + // one arg only + if l == nil || l.Next != nil { + return false + } + n := l.N + + // must be call + switch n.Op { + default: + return false + + case OCALLFUNC, OCALLMETH, OCALLINTER: + break + } + + // call must return multiple values + return n.Left.Type.Outtuple > 1 +} + +// Copyret emits t1, t2, ... = n, where n is a function call, +// and then returns the list t1, t2, .... +func copyret(n *Node, order *Order) *NodeList { + if n.Type.Etype != TSTRUCT || n.Type.Funarg == 0 { + Fatal("copyret %v %d", n.Type, n.Left.Type.Outtuple) + } + + var l1 *NodeList + var l2 *NodeList + var tl Iter + var tmp *Node + for t := Structfirst(&tl, &n.Type); t != nil; t = structnext(&tl) { + tmp = temp(t.Type) + l1 = list(l1, tmp) + l2 = list(l2, tmp) + } + + as := Nod(OAS2, nil, nil) + as.List = l1 + as.Rlist = list1(n) + typecheck(&as, Etop) + orderstmt(as, order) + + return l2 +} + +// Ordercallargs orders the list of call arguments *l. +func ordercallargs(l **NodeList, order *Order) { + if ismulticall(*l) { + // return f() where f() is multiple values. + *l = copyret((*l).N, order) + } else { + orderexprlist(*l, 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, nil) + orderexpr(&n.Right, order, nil) // ODDDARG temp + ordercallargs(&n.List, order) +} + +// Ordermapassign appends n to order->out, introducing temporaries +// to make sure that all map assignments have the form m[k] = x, +// where x is adressable. +// (Orderexpr has already been called on n, so we know k is addressable.) +// +// If n is m[k] = x where x is not addressable, the rewrite is: +// tmp = x +// m[k] = tmp +// +// If n is the multiple assignment form ..., m[k], ... = ..., the rewrite is +// t1 = m +// t2 = k +// ...., t3, ... = x +// t1[t2] = t3 +// +// The temporaries t1, t2 are needed in case the ... being assigned +// contain m or k. They are usually unnecessary, but in the unnecessary +// cases they are also typically registerizable, so not much harm done. +// And this only applies to the multiple-assignment form. +// We could do a more precise analysis if needed, like in walk.c. +// +// Ordermapassign also inserts these temporaries if needed for +// calling writebarrierfat with a pointer to n->right. +func ordermapassign(n *Node, order *Order) { + switch n.Op { + default: + Fatal("ordermapassign %v", Oconv(int(n.Op), 0)) + + case OAS: + order.out = list(order.out, n) + + // We call writebarrierfat only for values > 4 pointers long. See walk.c. + if (n.Left.Op == OINDEXMAP || (needwritebarrier(n.Left, n.Right) && n.Left.Type.Width > int64(4*Widthptr))) && !isaddrokay(n.Right) { + m := n.Left + n.Left = ordertemp(m.Type, order, false) + a := Nod(OAS, m, n.Left) + typecheck(&a, Etop) + order.out = list(order.out, a) + } + + case OAS2, OAS2DOTTYPE, OAS2MAPR, OAS2FUNC: + var post *NodeList + var m *Node + var a *Node + for l := n.List; l != nil; l = l.Next { + if l.N.Op == OINDEXMAP { + m = l.N + if !istemp(m.Left) { + m.Left = ordercopyexpr(m.Left, m.Left.Type, order, 0) + } + if !istemp(m.Right) { + m.Right = ordercopyexpr(m.Right, m.Right.Type, order, 0) + } + l.N = ordertemp(m.Type, order, false) + a = Nod(OAS, m, l.N) + typecheck(&a, Etop) + post = list(post, a) + } + } + + order.out = list(order.out, n) + order.out = concat(order.out, post) + } +} + +// Orderstmt orders the statement n, appending to order->out. +// Temporaries created during the statement are cleaned +// up using VARKILL instructions as possible. +func orderstmt(n *Node, order *Order) { + if n == nil { + return + } + + lno := int(setlineno(n)) + + orderinit(n, order) + + switch n.Op { + default: + Fatal("orderstmt %v", Oconv(int(n.Op), 0)) + + case OVARKILL: + order.out = list(order.out, n) + + 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, + OPRINTN, + ORECOVER, + ORECV: + t := marktemp(order) + orderexpr(&n.Left, order, nil) + orderexpr(&n.Right, order, nil) + orderexprlist(n.List, order) + orderexprlist(n.Rlist, order) + switch n.Op { + case OAS2, OAS2DOTTYPE: + ordermapassign(n, order) + default: + order.out = list(order.out, n) + } + cleantemp(t, order) + + 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, nil) + n.Left = ordersafeexpr(n.Left, order) + tmp1 := treecopy(n.Left) + if tmp1.Op == OINDEXMAP { + tmp1.Etype = 0 // now an rvalue not an lvalue + } + 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, nil) + n.Etype = 0 + n.Op = OAS + ordermapassign(n, order) + cleantemp(t, order) + + // Special: make sure key is addressable, + // and make sure OINDEXMAP is not copied out. + case OAS2MAPR: + t := marktemp(order) + + orderexprlist(n.List, order) + r := n.Rlist.N + orderexpr(&r.Left, order, nil) + orderexpr(&r.Right, order, nil) + + // See case OINDEXMAP below. + if r.Right.Op == OARRAYBYTESTR { + r.Right.Op = OARRAYBYTESTRTMP + } + orderaddrtemp(&r.Right, order) + ordermapassign(n, order) + cleantemp(t, order) + + // Special: avoid copy of func call n->rlist->n. + case OAS2FUNC: + t := marktemp(order) + + orderexprlist(n.List, order) + ordercall(n.Rlist.N, order) + ordermapassign(n, order) + cleantemp(t, order) + + // Special: use temporary variables to hold result, + // so that assertI2Tetc can take address of temporary. + // No temporary for blank assignment. + case OAS2DOTTYPE: + t := marktemp(order) + + orderexprlist(n.List, order) + orderexpr(&n.Rlist.N.Left, order, nil) // i in i.(T) + if isblank(n.List.N) { + order.out = list(order.out, n) + } else { + typ := n.Rlist.N.Type + tmp1 := ordertemp(typ, order, haspointers(typ)) + order.out = list(order.out, n) + r := Nod(OAS, n.List.N, tmp1) + typecheck(&r, Etop) + ordermapassign(r, order) + n.List = list(list1(tmp1), n.List.Next.N) + } + + cleantemp(t, order) + + // Special: use temporary variables to hold result, + // so that chanrecv can take address of temporary. + case OAS2RECV: + t := marktemp(order) + + orderexprlist(n.List, order) + 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 + if !isblank(n.List.Next.N) { + tmp2 = ordertemp(n.List.Next.N.Type, order, false) + } else { + tmp2 = ordertemp(Types[TBOOL], order, false) + } + order.out = list(order.out, n) + r := Nod(OAS, n.List.N, tmp1) + typecheck(&r, Etop) + ordermapassign(r, order) + r = Nod(OAS, n.List.Next.N, tmp2) + typecheck(&r, Etop) + ordermapassign(r, order) + n.List = list(list1(tmp1), tmp2) + cleantemp(t, order) + + // Special: does not save n onto out. + case OBLOCK, OEMPTY: + orderstmtlist(n.List, order) + + // Special: n->left is not an expression; save as is. + case OBREAK, + OCONTINUE, + ODCL, + ODCLCONST, + ODCLTYPE, + OFALL, + OXFALL, + OGOTO, + OLABEL, + ORETJMP: + order.out = list(order.out, n) + + // Special: handle call arguments. + case OCALLFUNC, OCALLINTER, OCALLMETH: + t := marktemp(order) + + ordercall(n, order) + order.out = list(order.out, n) + cleantemp(t, order) + + // Special: order arguments to inner call but not call itself. + case ODEFER, OPROC: + t := marktemp(order) + + switch n.Left.Op { + // Delete will take the address of the key. + // Copy key into new temp and do not clean it + // (it persists beyond the statement). + case ODELETE: + orderexprlist(n.Left.List, order) + + t1 := marktemp(order) + np := &n.Left.List.Next.N // map key + *np = ordercopyexpr(*np, (*np).Type, order, 0) + poptemp(t1, order) + + default: + ordercall(n.Left, order) + } + + order.out = list(order.out, n) + cleantemp(t, order) + + case ODELETE: + t := marktemp(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) + + // Clean temporaries from condition evaluation at + // beginning of loop body and after for statement. + case OFOR: + t := marktemp(order) + + orderexprinplace(&n.Ntest, order) + var l *NodeList + cleantempnopop(t, order, &l) + n.Nbody = concat(l, n.Nbody) + orderblock(&n.Nbody) + orderstmtinplace(&n.Nincr) + order.out = list(order.out, n) + cleantemp(t, order) + + // Clean temporaries from condition at + // beginning of both branches. + case OIF: + t := marktemp(order) + + orderexprinplace(&n.Ntest, order) + var l *NodeList + cleantempnopop(t, order, &l) + n.Nbody = concat(l, n.Nbody) + l = nil + cleantempnopop(t, order, &l) + n.Nelse = concat(l, n.Nelse) + poptemp(t, order) + orderblock(&n.Nbody) + orderblock(&n.Nelse) + order.out = list(order.out, n) + + // Special: argument will be converted to interface using convT2E + // so make sure it is an addressable temporary. + case OPANIC: + t := marktemp(order) + + orderexpr(&n.Left, order, nil) + if !Isinter(n.Left.Type) { + orderaddrtemp(&n.Left, order) + } + order.out = list(order.out, n) + cleantemp(t, order) + + // n->right is the expression being ranged over. + // order it, and then make a copy if we need one. + // We almost always do, to ensure that we don't + // see any value changes made during the loop. + // Usually the copy is cheap (e.g., array pointer, chan, slice, string are all tiny). + // The exception is ranging over an array value (not a slice, not a pointer to array), + // which must make a copy to avoid seeing updates made during + // the range body. Ranging over an array value is uncommon though. + case ORANGE: + t := marktemp(order) + + orderexpr(&n.Right, order, nil) + switch n.Type.Etype { + default: + Fatal("orderstmt range %v", n.Type) + + // Mark []byte(str) range expression to reuse string backing storage. + // It is safe because the storage cannot be mutated. + case TARRAY: + if n.Right.Op == OSTRARRAYBYTE { + n.Right.Op = OSTRARRAYBYTETMP + } + if count(n.List) < 2 || isblank(n.List.Next.N) { + // for i := range x will only use x once, to compute len(x). + // No need to copy it. + break + } + fallthrough + + // chan, string, slice, array ranges use value multiple times. + // make copy. + // fall through + case TCHAN, TSTRING: + r := n.Right + + if r.Type.Etype == TSTRING && r.Type != Types[TSTRING] { + r = Nod(OCONV, r, nil) + r.Type = Types[TSTRING] + typecheck(&r, Erv) + } + + n.Right = ordercopyexpr(r, r.Type, order, 0) + + // copy the map value in case it is a map literal. + // TODO(rsc): Make tmp = literal expressions reuse tmp. + // For maps tmp is just one word so it hardly matters. + case TMAP: + r := n.Right + + n.Right = ordercopyexpr(r, r.Type, order, 0) + + // n->alloc is the temp for the iterator. + n.Alloc = ordertemp(Types[TUINT8], order, true) + } + + for l := n.List; l != nil; l = l.Next { + orderexprinplace(&l.N, order) + } + orderblock(&n.Nbody) + order.out = list(order.out, n) + cleantemp(t, order) + + case ORETURN: + ordercallargs(&n.List, order) + order.out = list(order.out, n) + + // Special: clean case temporaries in each block entry. + // Select must enter one of its blocks, so there is no + // need for a cleaning at the end. + // Doubly special: evaluation order for select is stricter + // than ordinary expressions. Even something like p.c + // has to be hoisted into a temporary, so that it cannot be + // reordered after the channel evaluation for a different + // case (if p were nil, then the timing of the fault would + // give this away). + case OSELECT: + t := marktemp(order) + + var tmp1 *Node + var tmp2 *Node + var r *Node + for l := n.List; l != nil; l = l.Next { + if l.N.Op != OXCASE { + Fatal("order select case %v", Oconv(int(l.N.Op), 0)) + } + r = l.N.Left + setlineno(l.N) + + // Append any new body prologue to ninit. + // The next loop will insert ninit into nbody. + if l.N.Ninit != nil { + Fatal("order select ninit") + } + if r != nil { + switch r.Op { + default: + Yyerror("unknown op in select %v", Oconv(int(r.Op), 0)) + Dump("select case", r) + + // If this is case x := <-ch or case x, y := <-ch, the case has + // the ODCL nodes to declare x and y. We want to delay that + // declaration (and possible allocation) until inside the case body. + // Delete the ODCL nodes here and recreate them inside the body below. + case OSELRECV, OSELRECV2: + if r.Colas { + t = r.Ninit + if t != nil && t.N.Op == ODCL && t.N.Left == r.Left { + t = t.Next + } + if t != nil && t.N.Op == ODCL && t.N.Left == r.Ntest { + t = t.Next + } + if t == nil { + r.Ninit = nil + } + } + + if r.Ninit != nil { + Yyerror("ninit on select recv") + dumplist("ninit", r.Ninit) + } + + // case x = <-c + // case x, ok = <-c + // 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, nil) + + if r.Right.Left.Op != ONAME { + r.Right.Left = ordercopyexpr(r.Right.Left, r.Right.Left.Type, order, 0) + } + + // Introduce temporary for receive and move actual copy into case body. + // avoids problems with target being addressed, as usual. + // NOTE: If we wanted to be clever, we could arrange for just one + // temporary per distinct type, sharing the temp among all receives + // with that temp. Similarly one ok bool could be shared among all + // the x,ok receives. Not worth doing until there's a clear need. + if r.Left != nil && isblank(r.Left) { + r.Left = nil + } + if r.Left != nil { + // use channel element type for temporary to avoid conversions, + // such as in case interfacevalue = <-intchan. + // the conversion happens in the OAS instead. + tmp1 = r.Left + + if r.Colas { + tmp2 = Nod(ODCL, tmp1, nil) + typecheck(&tmp2, Etop) + l.N.Ninit = list(l.N.Ninit, tmp2) + } + + r.Left = ordertemp(r.Right.Left.Type.Type, order, haspointers(r.Right.Left.Type.Type)) + tmp2 = Nod(OAS, tmp1, r.Left) + typecheck(&tmp2, Etop) + l.N.Ninit = list(l.N.Ninit, tmp2) + } + + if r.Ntest != nil && isblank(r.Ntest) { + r.Ntest = nil + } + if r.Ntest != nil { + tmp1 = r.Ntest + if r.Colas { + tmp2 = Nod(ODCL, tmp1, nil) + typecheck(&tmp2, Etop) + l.N.Ninit = list(l.N.Ninit, tmp2) + } + + r.Ntest = ordertemp(tmp1.Type, order, false) + tmp2 = Nod(OAS, tmp1, r.Ntest) + typecheck(&tmp2, Etop) + l.N.Ninit = list(l.N.Ninit, tmp2) + } + + orderblock(&l.N.Ninit) + + case OSEND: + if r.Ninit != nil { + Yyerror("ninit on select send") + dumplist("ninit", r.Ninit) + } + + // case c <- x + // r->left is c, r->right is x, both are always evaluated. + orderexpr(&r.Left, order, nil) + + if !istemp(r.Left) { + r.Left = ordercopyexpr(r.Left, r.Left.Type, order, 0) + } + orderexpr(&r.Right, order, nil) + if !istemp(r.Right) { + r.Right = ordercopyexpr(r.Right, r.Right.Type, order, 0) + } + } + } + + orderblock(&l.N.Nbody) + } + + // Now that we have accumulated all the temporaries, clean them. + // Also insert any ninit queued during the previous loop. + // (The temporary cleaning must follow that ninit work.) + for l := n.List; l != nil; l = l.Next { + cleantempnopop(t, order, &l.N.Ninit) + l.N.Nbody = concat(l.N.Ninit, l.N.Nbody) + l.N.Ninit = nil + } + + order.out = list(order.out, n) + poptemp(t, order) + + // Special: value being sent is passed as a pointer; make it addressable. + case OSEND: + t := marktemp(order) + + orderexpr(&n.Left, order, nil) + orderexpr(&n.Right, order, nil) + orderaddrtemp(&n.Right, order) + order.out = list(order.out, n) + cleantemp(t, order) + + // TODO(rsc): Clean temporaries more aggressively. + // Note that because walkswitch will rewrite some of the + // switch into a binary search, this is not as easy as it looks. + // (If we ran that code here we could invoke orderstmt on + // the if-else chain instead.) + // For now just clean all the temporaries at the end. + // In practice that's fine. + case OSWITCH: + t := marktemp(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)) + } + orderexprlistinplace(l.N.List, order) + orderblock(&l.N.Nbody) + } + + order.out = list(order.out, n) + cleantemp(t, order) + } + + lineno = int32(lno) +} + +// Orderexprlist orders the expression list l into order. +func orderexprlist(l *NodeList, order *Order) { + for ; l != nil; l = l.Next { + orderexpr(&l.N, order, nil) + } +} + +// Orderexprlist orders the expression list l but saves +// the side effects on the individual expression ninit lists. +func orderexprlistinplace(l *NodeList, order *Order) { + for ; l != nil; l = l.Next { + orderexprinplace(&l.N, order) + } +} + +// Orderexpr orders a single expression, appending side +// effects to order->out as needed. +// 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 + } + + lno := int(setlineno(n)) + orderinit(n, order) + + switch n.Op { + default: + orderexpr(&n.Left, order, nil) + orderexpr(&n.Right, order, nil) + orderexprlist(n.List, order) + orderexprlist(n.Rlist, order) + + // Addition of strings turns into a function call. + // Allocate a temporary to hold the strings. + // Fewer than 5 strings use direct runtime helpers. + case OADDSTR: + orderexprlist(n.List, order) + + if count(n.List) > 5 { + t := typ(TARRAY) + t.Bound = int64(count(n.List)) + t.Type = Types[TSTRING] + n.Alloc = ordertemp(t, order, false) + } + + // Mark string(byteSlice) arguments to reuse byteSlice backing + // buffer during conversion. String concatenation does not + // memorize the strings for later use, so it is safe. + // However, we can do it only if there is at least one non-empty string literal. + // Otherwise if all other arguments are empty strings, + // concatstrings will return the reference to the temp string + // to the caller. + hasbyte := false + + 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.(string)) != 0 + } + + if haslit && hasbyte { + for l := n.List; l != nil; l = l.Next { + if l.N.Op == OARRAYBYTESTR { + l.N.Op = OARRAYBYTESTRTMP + } + } + } + + case OCMPSTR: + 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 + // memorize the strings for later use, so it is safe. + if n.Left.Op == OARRAYBYTESTR { + n.Left.Op = OARRAYBYTESTRTMP + } + if n.Right.Op == OARRAYBYTESTR { + n.Right.Op = OARRAYBYTESTRTMP + } + + // key must be addressable + case OINDEXMAP: + orderexpr(&n.Left, order, nil) + + 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 + // the []byte backing array. This is a special case that it + // would be nice to handle more generally, but because + // there are no []byte-keyed maps, this specific case comes + // up in important cases in practice. See issue 3512. + // Nothing can change the []byte we are not copying before + // the map index, because the map access is going to + // be forced to happen immediately following this + // conversion (by the ordercopyexpr a few lines below). + if n.Etype == 0 && n.Right.Op == OARRAYBYTESTR { + n.Right.Op = OARRAYBYTESTRTMP + } + + orderaddrtemp(&n.Right, order) + if n.Etype == 0 { + // use of value (not being assigned); + // make copy in temporary. + n = ordercopyexpr(n, n.Type, order, 0) + } + + // concrete type (not interface) argument must be addressable + // temporary to pass to runtime. + case OCONVIFACE: + orderexpr(&n.Left, order, nil) + + if !Isinter(n.Left.Type) { + orderaddrtemp(&n.Left, order) + } + + case OANDAND, OOROR: + mark := marktemp(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 + // context in case the short circuit is taken. + var l *NodeList + + cleantempnopop(mark, order, &l) + n.Right.Ninit = concat(l, n.Right.Ninit) + orderexprinplace(&n.Right, order) + + case OCALLFUNC, + OCALLINTER, + OCALLMETH, + OCAP, + OCOMPLEX, + OCOPY, + OIMAG, + OLEN, + OMAKECHAN, + OMAKEMAP, + OMAKESLICE, + ONEW, + OREAL, + ORECOVER: + ordercall(n, order) + if lhs == nil || lhs.Op != ONAME || flag_race != 0 { + 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 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 + } + + case OARRAYLIT, OCALLPART: + orderexpr(&n.Left, order, nil) + orderexpr(&n.Right, order, nil) + orderexprlist(n.List, order) + orderexprlist(n.Rlist, order) + if n.Noescape { + n.Alloc = ordertemp(Types[TUINT8], order, false) // walk will fill in correct type + } + + case ODDDARG: + if n.Noescape { + // The ddd argument does not live beyond the call it is created for. + // Allocate a temporary that will be cleaned up when this statement + // completes. We could be more aggressive and try to arrange for it + // to be cleaned up when the call completes. + n.Alloc = ordertemp(n.Type.Type, order, false) + } + + case ODOTTYPE, ODOTTYPE2: + 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). + if !isdirectiface(n.Type) || Isfat(n.Type) || flag_race != 0 { + n = ordercopyexpr(n, n.Type, order, 1) + } + + case ORECV: + orderexpr(&n.Left, order, nil) + n = ordercopyexpr(n, n.Type, order, 1) + + case OEQ, ONE: + 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 + // addressable so we can pass them to the runtime. + orderaddrtemp(&n.Left, order) + orderaddrtemp(&n.Right, order) + } + } + + lineno = int32(lno) + + *np = n +} diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go new file mode 100644 index 0000000000..1b67cf2c3e --- /dev/null +++ b/src/cmd/compile/internal/gc/pgen.go @@ -0,0 +1,555 @@ +// Copyright 2011 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 gc + +import ( + "cmd/internal/obj" + "crypto/md5" + "fmt" + "strings" +) + +// "Portable" code generation. + +var makefuncdatasym_nsym int32 + +func makefuncdatasym(namefmt string, funcdatakind int64) *Sym { + var nod Node + + sym := Lookupf(namefmt, makefuncdatasym_nsym) + makefuncdatasym_nsym++ + pnod := newname(sym) + pnod.Class = PEXTERN + Nodconst(&nod, Types[TINT32], funcdatakind) + Thearch.Gins(obj.AFUNCDATA, &nod, pnod) + return sym +} + +// gvardef inserts a VARDEF for n into the instruction stream. +// VARDEF is an annotation for the liveness analysis, marking a place +// where a complete initialization (definition) of a variable begins. +// Since the liveness analysis can see initialization of single-word +// variables quite easy, gvardef is usually only called for multi-word +// or 'fat' variables, those satisfying isfat(n->type). +// However, gvardef is also called when a non-fat variable is initialized +// via a block move; the only time this happens is when you have +// return f() +// for a function with multiple return values exactly matching the return +// types of the current function. +// +// A 'VARDEF x' annotation in the instruction stream tells the liveness +// analysis to behave as though the variable x is being initialized at that +// point in the instruction stream. The VARDEF must appear before the +// actual (multi-instruction) initialization, and it must also appear after +// any uses of the previous value, if any. For example, if compiling: +// +// x = x[1:] +// +// it is important to generate code like: +// +// base, len, cap = pieces of x[1:] +// VARDEF x +// x = {base, len, cap} +// +// If instead the generated code looked like: +// +// VARDEF x +// base, len, cap = pieces of x[1:] +// x = {base, len, cap} +// +// then the liveness analysis would decide the previous value of x was +// unnecessary even though it is about to be used by the x[1:] computation. +// Similarly, if the generated code looked like: +// +// base, len, cap = pieces of x[1:] +// x = {base, len, cap} +// VARDEF x +// +// then the liveness analysis will not preserve the new value of x, because +// the VARDEF appears to have "overwritten" it. +// +// VARDEF is a bit of a kludge to work around the fact that the instruction +// stream is working on single-word values but the liveness analysis +// wants to work on individual variables, which might be multi-word +// aggregates. It might make sense at some point to look into letting +// the liveness analysis work on single-word values as well, although +// there are complications around interface values, slices, and strings, +// all of which cannot be treated as individual words. +// +// VARKILL is the opposite of VARDEF: it marks a value as no longer needed, +// even if its address has been taken. That is, a VARKILL annotation asserts +// that its argument is certainly dead, for use when the liveness analysis +// would not otherwise be able to deduce that fact. + +func gvardefx(n *Node, as int) { + if n == nil { + Fatal("gvardef nil") + } + if n.Op != ONAME { + Yyerror("gvardef %v; %v", Oconv(int(n.Op), obj.FmtSharp), n) + return + } + + switch n.Class { + case PAUTO, PPARAM, PPARAMOUT: + Thearch.Gins(as, nil, n) + } +} + +func Gvardef(n *Node) { + gvardefx(n, obj.AVARDEF) +} + +func gvarkill(n *Node) { + gvardefx(n, obj.AVARKILL) +} + +func removevardef(firstp *obj.Prog) { + for p := firstp; p != nil; p = p.Link { + for p.Link != nil && (p.Link.As == obj.AVARDEF || p.Link.As == obj.AVARKILL) { + p.Link = p.Link.Link + } + if p.To.Type == obj.TYPE_BRANCH { + for p.To.Val.(*obj.Prog) != nil && (p.To.Val.(*obj.Prog).As == obj.AVARDEF || p.To.Val.(*obj.Prog).As == obj.AVARKILL) { + p.To.Val = p.To.Val.(*obj.Prog).Link + } + } + } +} + +func gcsymdup(s *Sym) { + ls := Linksym(s) + if len(ls.R) > 0 { + Fatal("cannot rosymdup %s with relocations", ls.Name) + } + ls.Name = fmt.Sprintf("gclocals·%x", md5.Sum(ls.P)) + ls.Dupok = 1 +} + +func emitptrargsmap() { + sym := Lookup(fmt.Sprintf("%s.args_stackmap", Curfn.Nname.Sym.Name)) + + nptr := int(Curfn.Type.Argwid / int64(Widthptr)) + bv := bvalloc(int32(nptr) * 2) + nbitmap := 1 + if Curfn.Type.Outtuple > 0 { + nbitmap = 2 + } + off := duint32(sym, 0, uint32(nbitmap)) + off = duint32(sym, off, uint32(bv.n)) + var xoffset int64 + if Curfn.Type.Thistuple > 0 { + xoffset = 0 + onebitwalktype1(getthisx(Curfn.Type), &xoffset, bv) + } + + if Curfn.Type.Intuple > 0 { + xoffset = 0 + onebitwalktype1(getinargx(Curfn.Type), &xoffset, bv) + } + + for j := 0; int32(j) < bv.n; j += 32 { + off = duint32(sym, off, bv.b[j/32]) + } + if Curfn.Type.Outtuple > 0 { + xoffset = 0 + onebitwalktype1(getoutargx(Curfn.Type), &xoffset, bv) + for j := 0; int32(j) < bv.n; j += 32 { + off = duint32(sym, off, bv.b[j/32]) + } + } + + ggloblsym(sym, int32(off), obj.RODATA|obj.LOCAL) +} + +// Sort the list of stack variables. Autos after anything else, +// within autos, unused after used, within used, things with +// pointers first, zeroed things first, and then decreasing size. +// Because autos are laid out in decreasing addresses +// on the stack, pointers first, zeroed things first and decreasing size +// really means, in memory, things with pointers needing zeroing at +// the top of the stack and increasing in size. +// Non-autos sort on offset. +func cmpstackvar(a *Node, b *Node) int { + if a.Class != b.Class { + if a.Class == PAUTO { + return +1 + } + return -1 + } + + if a.Class != PAUTO { + if a.Xoffset < b.Xoffset { + return -1 + } + if a.Xoffset > b.Xoffset { + return +1 + } + return 0 + } + + if a.Used != b.Used { + return obj.Bool2int(b.Used) - obj.Bool2int(a.Used) + } + + ap := obj.Bool2int(haspointers(a.Type)) + bp := obj.Bool2int(haspointers(b.Type)) + if ap != bp { + return bp - ap + } + + ap = obj.Bool2int(a.Name.Needzero) + bp = obj.Bool2int(b.Name.Needzero) + if ap != bp { + return bp - ap + } + + if a.Type.Width < b.Type.Width { + return +1 + } + if a.Type.Width > b.Type.Width { + return -1 + } + + return stringsCompare(a.Sym.Name, b.Sym.Name) +} + +// TODO(lvd) find out where the PAUTO/OLITERAL nodes come from. +func allocauto(ptxt *obj.Prog) { + Stksize = 0 + stkptrsize = 0 + + if Curfn.Func.Dcl == nil { + return + } + + // Mark the PAUTO's unused. + for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { + if ll.N.Class == PAUTO { + ll.N.Used = false + } + } + + markautoused(ptxt) + + listsort(&Curfn.Func.Dcl, cmpstackvar) + + // Unused autos are at the end, chop 'em off. + ll := Curfn.Func.Dcl + + n := ll.N + if n.Class == PAUTO && n.Op == ONAME && !n.Used { + // No locals used at all + Curfn.Func.Dcl = nil + + fixautoused(ptxt) + return + } + + for ll := Curfn.Func.Dcl; ll.Next != nil; ll = ll.Next { + n = ll.Next.N + if n.Class == PAUTO && n.Op == ONAME && !n.Used { + ll.Next = nil + Curfn.Func.Dcl.End = ll + break + } + } + + // Reassign stack offsets of the locals that are still there. + var w int64 + for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { + n = ll.N + if n.Class != PAUTO || n.Op != ONAME { + continue + } + + dowidth(n.Type) + w = n.Type.Width + if w >= Thearch.MAXWIDTH || w < 0 { + Fatal("bad width") + } + Stksize += w + Stksize = Rnd(Stksize, int64(n.Type.Align)) + if haspointers(n.Type) { + stkptrsize = Stksize + } + if Thearch.Thechar == '5' || Thearch.Thechar == '7' || Thearch.Thechar == '9' { + Stksize = Rnd(Stksize, int64(Widthptr)) + } + if Stksize >= 1<<31 { + setlineno(Curfn) + Yyerror("stack frame too large (>2GB)") + } + + n.Stkdelta = -Stksize - n.Xoffset + } + + Stksize = Rnd(Stksize, int64(Widthreg)) + stkptrsize = Rnd(stkptrsize, int64(Widthreg)) + + fixautoused(ptxt) + + // The debug information needs accurate offsets on the symbols. + for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { + if ll.N.Class != PAUTO || ll.N.Op != ONAME { + continue + } + ll.N.Xoffset += ll.N.Stkdelta + ll.N.Stkdelta = 0 + } +} + +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 + } + + // Ideally we wouldn't see any integer types here, but we do. + if n.Type == nil || (!Isptr[n.Type.Etype] && !Isint[n.Type.Etype] && n.Type.Etype != TUNSAFEPTR) { + Dump("checknil", n) + Fatal("bad checknil") + } + + if ((Thearch.Thechar == '5' || Thearch.Thechar == '7' || Thearch.Thechar == '9') && n.Op != OREGISTER) || !n.Addable || n.Op == OLITERAL { + var reg Node + Regalloc(®, Types[Tptr], n) + Cgen(n, ®) + Thearch.Gins(obj.ACHECKNIL, ®, nil) + Regfree(®) + return + } + + Thearch.Gins(obj.ACHECKNIL, n, nil) +} + +func compile(fn *Node) { + if Newproc == nil { + Newproc = Sysfunc("newproc") + Deferproc = Sysfunc("deferproc") + Deferreturn = Sysfunc("deferreturn") + Panicindex = Sysfunc("panicindex") + panicslice = Sysfunc("panicslice") + throwreturn = Sysfunc("throwreturn") + } + + lno := setlineno(fn) + + Curfn = fn + dowidth(Curfn.Type) + + var oldstksize int64 + var nod1 Node + var ptxt *obj.Prog + var pl *obj.Plist + var p *obj.Prog + var n *Node + var nam *Node + var gcargs *Sym + var gclocals *Sym + if fn.Nbody == nil { + if pure_go != 0 || strings.HasPrefix(fn.Nname.Sym.Name, "init.") { + Yyerror("missing function body for %q", fn.Nname.Sym.Name) + goto ret + } + + if Debug['A'] != 0 { + goto ret + } + emitptrargsmap() + goto ret + } + + saveerrors() + + // set up domain for labels + clearlabels() + + if Curfn.Type.Outnamed != 0 { + // add clearing of the output parameters + var save Iter + t := Structfirst(&save, Getoutarg(Curfn.Type)) + + for t != nil { + if t.Nname != nil { + n = Nod(OAS, t.Nname, nil) + typecheck(&n, Etop) + Curfn.Nbody = concat(list1(n), Curfn.Nbody) + } + + t = structnext(&save) + } + } + + order(Curfn) + if nerrors != 0 { + goto ret + } + + Hasdefer = 0 + walk(Curfn) + if nerrors != 0 { + goto ret + } + if flag_race != 0 { + racewalk(Curfn) + } + if nerrors != 0 { + goto ret + } + + continpc = nil + breakpc = nil + + pl = newplist() + pl.Name = Linksym(Curfn.Nname.Sym) + + setlineno(Curfn) + + Nodconst(&nod1, Types[TINT32], 0) + nam = Curfn.Nname + if isblank(nam) { + nam = nil + } + ptxt = Thearch.Gins(obj.ATEXT, nam, &nod1) + if fn.Func.Dupok { + ptxt.From3.Offset |= obj.DUPOK + } + if fn.Func.Wrapper { + ptxt.From3.Offset |= obj.WRAPPER + } + if fn.Func.Needctxt { + ptxt.From3.Offset |= obj.NEEDCTXT + } + if fn.Func.Nosplit { + ptxt.From3.Offset |= obj.NOSPLIT + } + + // Clumsy but important. + // See test/recover.go for test cases and src/reflect/value.go + // for the actual functions being considered. + if myimportpath != "" && myimportpath == "reflect" { + if Curfn.Nname.Sym.Name == "callReflect" || Curfn.Nname.Sym.Name == "callMethod" { + ptxt.From3.Offset |= obj.WRAPPER + } + } + + Afunclit(&ptxt.From, Curfn.Nname) + + ginit() + + gcargs = makefuncdatasym("gcargs·%d", obj.FUNCDATA_ArgsPointerMaps) + gclocals = makefuncdatasym("gclocals·%d", obj.FUNCDATA_LocalsPointerMaps) + + for t := Curfn.Paramfld; t != nil; t = t.Down { + gtrack(tracksym(t.Type)) + } + + for l := fn.Func.Dcl; l != nil; l = l.Next { + n = l.N + if n.Op != ONAME { // might be OTYPE or OLITERAL + continue + } + switch n.Class { + case PAUTO, PPARAM, PPARAMOUT: + Nodconst(&nod1, Types[TUINTPTR], l.N.Type.Width) + p = Thearch.Gins(obj.ATYPE, l.N, &nod1) + p.From.Gotype = Linksym(ngotype(l.N)) + } + } + + Genlist(Curfn.Func.Enter) + Genlist(Curfn.Nbody) + gclean() + checklabels() + if nerrors != 0 { + goto ret + } + if Curfn.Func.Endlineno != 0 { + lineno = Curfn.Func.Endlineno + } + + if Curfn.Type.Outtuple != 0 { + Ginscall(throwreturn, 0) + } + + ginit() + + // TODO: Determine when the final cgen_ret can be omitted. Perhaps always? + cgen_ret(nil) + + if Hasdefer != 0 { + // deferreturn pretends to have one uintptr argument. + // Reserve space for it so stack scanner is happy. + if Maxarg < int64(Widthptr) { + Maxarg = int64(Widthptr) + } + } + + gclean() + if nerrors != 0 { + goto ret + } + + Pc.As = obj.ARET // overwrite AEND + Pc.Lineno = lineno + + fixjmp(ptxt) + if Debug['N'] == 0 || Debug['R'] != 0 || Debug['P'] != 0 { + regopt(ptxt) + nilopt(ptxt) + } + + Thearch.Expandchecks(ptxt) + + oldstksize = Stksize + allocauto(ptxt) + + if false { + fmt.Printf("allocauto: %d to %d\n", oldstksize, int64(Stksize)) + } + + setlineno(Curfn) + if int64(Stksize)+Maxarg > 1<<31 { + Yyerror("stack frame too large (>2GB)") + goto ret + } + + // Emit garbage collection symbols. + liveness(Curfn, ptxt, gcargs, gclocals) + + gcsymdup(gcargs) + gcsymdup(gclocals) + + Thearch.Defframe(ptxt) + + if Debug['f'] != 0 { + frame(0) + } + + // Remove leftover instrumentation from the instruction stream. + removevardef(ptxt) + +ret: + lineno = lno +} diff --git a/src/cmd/compile/internal/gc/plive.go b/src/cmd/compile/internal/gc/plive.go new file mode 100644 index 0000000000..b4d0699d1f --- /dev/null +++ b/src/cmd/compile/internal/gc/plive.go @@ -0,0 +1,1830 @@ +// Copyright 2013 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. + +// Garbage collector liveness bitmap generation. + +// The command line flag -live causes this code to print debug information. +// The levels are: +// +// -live (aka -live=1): print liveness lists as code warnings at safe points +// -live=2: print an assembly listing with liveness annotations +// -live=3: print information during each computation phase (much chattier) +// +// Each level includes the earlier output as well. + +package gc + +import ( + "cmd/internal/obj" + "fmt" + "sort" +) + +const ( + UNVISITED = 0 + VISITED = 1 +) + +// An ordinary basic block. +// +// Instructions are threaded together in a doubly-linked list. To iterate in +// program order follow the link pointer from the first node and stop after the +// last node has been visited +// +// for(p = bb->first;; p = p->link) { +// ... +// if(p == bb->last) +// break; +// } +// +// To iterate in reverse program order by following the opt pointer from the +// last node +// +// for(p = bb->last; p != nil; p = p->opt) { +// ... +// } +type BasicBlock struct { + pred []*BasicBlock // predecessors; if none, probably start of CFG + succ []*BasicBlock // successors; if none, probably ends in return statement + first *obj.Prog // first instruction in block + last *obj.Prog // last instruction in block + rpo int // reverse post-order number (also index in cfg) + mark int // mark bit for traversals + lastbitmapindex int // for livenessepilogue + + // Summary sets of block effects. + + // Computed during livenessprologue using only the content of + // individual blocks: + // + // uevar: upward exposed variables (used before set in block) + // varkill: killed variables (set in block) + // avarinit: addrtaken variables set or used (proof of initialization) + uevar Bvec + varkill Bvec + avarinit Bvec + + // Computed during livenesssolve using control flow information: + // + // livein: variables live at block entry + // liveout: variables live at block exit + // avarinitany: addrtaken variables possibly initialized at block exit + // (initialized in block or at exit from any predecessor block) + // avarinitall: addrtaken variables certainly initialized at block exit + // (initialized in block or at exit from all predecessor blocks) + livein Bvec + liveout Bvec + avarinitany Bvec + avarinitall Bvec +} + +// A collection of global state used by liveness analysis. +type Liveness struct { + fn *Node + ptxt *obj.Prog + vars []*Node + cfg []*BasicBlock + + // An array with a bit vector for each safe point tracking live pointers + // in the arguments and locals area, indexed by bb.rpo. + argslivepointers []Bvec + livepointers []Bvec +} + +func xmalloc(size uint32) interface{} { + result := (interface{})(make([]byte, size)) + if result == nil { + Fatal("malloc failed") + } + return result +} + +// Constructs a new basic block containing a single instruction. +func newblock(prog *obj.Prog) *BasicBlock { + if prog == nil { + Fatal("newblock: prog cannot be nil") + } + result := new(BasicBlock) + result.rpo = -1 + result.mark = UNVISITED + result.first = prog + result.last = prog + result.pred = make([]*BasicBlock, 0, 2) + result.succ = make([]*BasicBlock, 0, 2) + return result +} + +// Frees a basic block and all of its leaf data structures. +func freeblock(bb *BasicBlock) { + if bb == nil { + Fatal("freeblock: cannot free nil") + } +} + +// Adds an edge between two basic blocks by making from a predecessor of to and +// to a successor of from. +func addedge(from *BasicBlock, to *BasicBlock) { + if from == nil { + Fatal("addedge: from is nil") + } + if to == nil { + Fatal("addedge: to is nil") + } + from.succ = append(from.succ, to) + to.pred = append(to.pred, from) +} + +// Inserts prev before curr in the instruction +// stream. Any control flow, such as branches or fall throughs, that target the +// existing instruction are adjusted to target the new instruction. +func splicebefore(lv *Liveness, bb *BasicBlock, prev *obj.Prog, curr *obj.Prog) { + // There may be other instructions pointing at curr, + // and we want them to now point at prev. Instead of + // trying to find all such instructions, swap the contents + // so that the problem becomes inserting next after curr. + // The "opt" field is the backward link in the linked list. + + // Overwrite curr's data with prev, but keep the list links. + tmp := *curr + + *curr = *prev + curr.Opt = tmp.Opt + curr.Link = tmp.Link + + // Overwrite prev (now next) with curr's old data. + next := prev + + *next = tmp + next.Opt = nil + next.Link = nil + + // Now insert next after curr. + next.Link = curr.Link + + next.Opt = curr + curr.Link = next + if next.Link != nil && next.Link.Opt == curr { + next.Link.Opt = next + } + + if bb.last == curr { + bb.last = next + } +} + +// A pretty printer for basic blocks. +func printblock(bb *BasicBlock) { + fmt.Printf("basic block %d\n", bb.rpo) + fmt.Printf("\tpred:") + for _, pred := range bb.pred { + fmt.Printf(" %d", pred.rpo) + } + fmt.Printf("\n") + fmt.Printf("\tsucc:") + for _, succ := range bb.succ { + fmt.Printf(" %d", succ.rpo) + } + fmt.Printf("\n") + fmt.Printf("\tprog:\n") + for prog := bb.first; ; prog = prog.Link { + fmt.Printf("\t\t%v\n", prog) + if prog == bb.last { + break + } + } +} + +// Iterates over a basic block applying a callback to each instruction. There +// are two criteria for termination. If the end of basic block is reached a +// value of zero is returned. If the callback returns a non-zero value, the +// iteration is stopped and the value of the callback is returned. +func blockany(bb *BasicBlock, f func(*obj.Prog) bool) bool { + for p := bb.last; p != nil; p = p.Opt.(*obj.Prog) { + if f(p) { + return true + } + } + return false +} + +// Collects and returns and array of Node*s for functions arguments and local +// variables. +func getvariables(fn *Node) []*Node { + result := make([]*Node, 0, 0) + for ll := fn.Func.Dcl; ll != nil; ll = ll.Next { + if ll.N.Op == ONAME { + // In order for GODEBUG=gcdead=1 to work, each bitmap needs + // to contain information about all variables covered by the bitmap. + // For local variables, the bitmap only covers the stkptrsize + // bytes in the frame where variables containing pointers live. + // For arguments and results, the bitmap covers all variables, + // so we must include all the variables, even the ones without + // pointers. + // + // The Node.opt field is available for use by optimization passes. + // We use it to hold the index of the node in the variables array, plus 1 + // (so that 0 means the Node is not in the variables array). + // Each pass should clear opt when done, but you never know, + // so clear them all ourselves too. + // The Node.curfn field is supposed to be set to the current function + // already, but for some compiler-introduced names it seems not to be, + // so fix that here. + // Later, when we want to find the index of a node in the variables list, + // we will check that n->curfn == curfn and n->opt > 0. Then n->opt - 1 + // is the index in the variables list. + ll.N.Opt = nil + + // The compiler doesn't emit initializations for zero-width parameters or results. + if ll.N.Type.Width == 0 { + continue + } + + ll.N.Curfn = Curfn + switch ll.N.Class { + case PAUTO: + if haspointers(ll.N.Type) { + ll.N.Opt = int32(len(result)) + result = append(result, ll.N) + } + + case PPARAM, PPARAMOUT: + ll.N.Opt = int32(len(result)) + result = append(result, ll.N) + } + } + } + + return result +} + +// A pretty printer for control flow graphs. Takes an array of BasicBlock*s. +func printcfg(cfg []*BasicBlock) { + for _, bb := range cfg { + printblock(bb) + } +} + +// Assigns a reverse post order number to each connected basic block using the +// standard algorithm. Unconnected blocks will not be affected. +func reversepostorder(root *BasicBlock, rpo *int32) { + root.mark = VISITED + for _, bb := range root.succ { + if bb.mark == UNVISITED { + reversepostorder(bb, rpo) + } + } + *rpo -= 1 + root.rpo = int(*rpo) +} + +// Comparison predicate used for sorting basic blocks by their rpo in ascending +// order. +type blockrpocmp []*BasicBlock + +func (x blockrpocmp) Len() int { return len(x) } +func (x blockrpocmp) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x blockrpocmp) Less(i, j int) bool { return x[i].rpo < x[j].rpo } + +// A pattern matcher for call instructions. Returns true when the instruction +// is a call to a specific package qualified function name. +func iscall(prog *obj.Prog, name *obj.LSym) bool { + if prog == nil { + Fatal("iscall: prog is nil") + } + if name == nil { + Fatal("iscall: function name is nil") + } + if prog.As != obj.ACALL { + return false + } + return name == prog.To.Sym +} + +// Returns true for instructions that call a runtime function implementing a +// select communication clause. + +var selectNames [4]*obj.LSym + +func isselectcommcasecall(prog *obj.Prog) bool { + if selectNames[0] == nil { + selectNames[0] = Linksym(Pkglookup("selectsend", Runtimepkg)) + selectNames[1] = Linksym(Pkglookup("selectrecv", Runtimepkg)) + selectNames[2] = Linksym(Pkglookup("selectrecv2", Runtimepkg)) + selectNames[3] = Linksym(Pkglookup("selectdefault", Runtimepkg)) + } + + for _, name := range selectNames { + if iscall(prog, name) { + return true + } + } + return false +} + +// Returns true for call instructions that target runtime·newselect. + +var isnewselect_sym *obj.LSym + +func isnewselect(prog *obj.Prog) bool { + if isnewselect_sym == nil { + isnewselect_sym = Linksym(Pkglookup("newselect", Runtimepkg)) + } + return iscall(prog, isnewselect_sym) +} + +// Returns true for call instructions that target runtime·selectgo. + +var isselectgocall_sym *obj.LSym + +func isselectgocall(prog *obj.Prog) bool { + if isselectgocall_sym == nil { + isselectgocall_sym = Linksym(Pkglookup("selectgo", Runtimepkg)) + } + return iscall(prog, isselectgocall_sym) +} + +var isdeferreturn_sym *obj.LSym + +func isdeferreturn(prog *obj.Prog) bool { + if isdeferreturn_sym == nil { + isdeferreturn_sym = Linksym(Pkglookup("deferreturn", Runtimepkg)) + } + return iscall(prog, isdeferreturn_sym) +} + +// Walk backwards from a runtime·selectgo call up to its immediately dominating +// runtime·newselect call. Any successor nodes of communication clause nodes +// are implicit successors of the runtime·selectgo call node. The goal of this +// analysis is to add these missing edges to complete the control flow graph. +func addselectgosucc(selectgo *BasicBlock) { + var succ *BasicBlock + + pred := selectgo + for { + if len(pred.pred) == 0 { + Fatal("selectgo does not have a newselect") + } + pred = pred.pred[0] + if blockany(pred, isselectcommcasecall) { + // A select comm case block should have exactly one + // successor. + if len(pred.succ) != 1 { + Fatal("select comm case has too many successors") + } + succ = pred.succ[0] + + // Its successor should have exactly two successors. + // The drop through should flow to the selectgo block + // and the branch should lead to the select case + // statements block. + if len(succ.succ) != 2 { + Fatal("select comm case successor has too many successors") + } + + // Add the block as a successor of the selectgo block. + addedge(selectgo, succ) + } + + if blockany(pred, isnewselect) { + // Reached the matching newselect. + break + } + } +} + +// The entry point for the missing selectgo control flow algorithm. Takes an +// array of BasicBlock*s containing selectgo calls. +func fixselectgo(selectgo []*BasicBlock) { + for _, bb := range selectgo { + addselectgosucc(bb) + } +} + +// Constructs a control flow graph from a sequence of instructions. This +// procedure is complicated by various sources of implicit control flow that are +// not accounted for using the standard cfg construction algorithm. Returns an +// array of BasicBlock*s in control flow graph form (basic blocks ordered by +// their RPO number). +func newcfg(firstp *obj.Prog) []*BasicBlock { + // Reset the opt field of each prog to nil. In the first and second + // passes, instructions that are labels temporarily use the opt field to + // point to their basic block. In the third pass, the opt field reset + // to point to the predecessor of an instruction in its basic block. + for p := firstp; p != nil; p = p.Link { + p.Opt = nil + } + + // Allocate an array to remember where we have seen selectgo calls. + // These blocks will be revisited to add successor control flow edges. + selectgo := make([]*BasicBlock, 0, 0) + + // Loop through all instructions identifying branch targets + // and fall-throughs and allocate basic blocks. + cfg := make([]*BasicBlock, 0, 0) + + bb := newblock(firstp) + cfg = append(cfg, bb) + for p := firstp; p != nil; p = p.Link { + Thearch.Proginfo(p) + if p.To.Type == obj.TYPE_BRANCH { + if p.To.Val == nil { + Fatal("prog branch to nil") + } + if p.To.Val.(*obj.Prog).Opt == nil { + p.To.Val.(*obj.Prog).Opt = newblock(p.To.Val.(*obj.Prog)) + cfg = append(cfg, p.To.Val.(*obj.Prog).Opt.(*BasicBlock)) + } + + if p.As != obj.AJMP && p.Link != nil && p.Link.Opt == nil { + p.Link.Opt = newblock(p.Link) + cfg = append(cfg, p.Link.Opt.(*BasicBlock)) + } + } else if isselectcommcasecall(p) || isselectgocall(p) { + // Accommodate implicit selectgo control flow. + if p.Link.Opt == nil { + p.Link.Opt = newblock(p.Link) + cfg = append(cfg, p.Link.Opt.(*BasicBlock)) + } + } + } + + // Loop through all basic blocks maximally growing the list of + // contained instructions until a label is reached. Add edges + // for branches and fall-through instructions. + for _, bb := range cfg { + for p := bb.last; p != nil; p = p.Link { + if p.Opt != nil && p != bb.last { + break + } + bb.last = p + + // Stop before an unreachable RET, to avoid creating + // unreachable control flow nodes. + if p.Link != nil && p.Link.As == obj.ARET && p.Link.Mode == 1 { + break + } + + // Collect basic blocks with selectgo calls. + if isselectgocall(p) { + selectgo = append(selectgo, bb) + } + } + + if bb.last.To.Type == obj.TYPE_BRANCH { + addedge(bb, bb.last.To.Val.(*obj.Prog).Opt.(*BasicBlock)) + } + if bb.last.Link != nil { + // Add a fall-through when the instruction is + // not an unconditional control transfer. + if bb.last.As != obj.AJMP && bb.last.As != obj.ARET && bb.last.As != obj.AUNDEF { + addedge(bb, bb.last.Link.Opt.(*BasicBlock)) + } + } + } + + // Add back links so the instructions in a basic block can be traversed + // backward. This is the final state of the instruction opt field. + for _, bb := range cfg { + p := bb.first + var prev *obj.Prog + for { + p.Opt = prev + if p == bb.last { + break + } + prev = p + p = p.Link + } + } + + // Add missing successor edges to the selectgo blocks. + if len(selectgo) != 0 { + fixselectgo([]*BasicBlock(selectgo)) + } + + // Find a depth-first order and assign a depth-first number to + // all basic blocks. + for _, bb := range cfg { + bb.mark = UNVISITED + } + bb = cfg[0] + rpo := int32(len(cfg)) + reversepostorder(bb, &rpo) + + // Sort the basic blocks by their depth first number. The + // array is now a depth-first spanning tree with the first + // node being the root. + sort.Sort(blockrpocmp(cfg)) + + // Unreachable control flow nodes are indicated by a -1 in the rpo + // field. If we see these nodes something must have gone wrong in an + // upstream compilation phase. + bb = cfg[0] + if bb.rpo == -1 { + fmt.Printf("newcfg: unreachable basic block for %v\n", bb.last) + printcfg(cfg) + Fatal("newcfg: invalid control flow graph") + } + + return cfg +} + +// Frees a control flow graph (an array of BasicBlock*s) and all of its leaf +// data structures. +func freecfg(cfg []*BasicBlock) { + if len(cfg) > 0 { + bb0 := cfg[0] + for p := bb0.first; p != nil; p = p.Link { + p.Opt = nil + } + } +} + +// Returns true if the node names a variable that is otherwise uninteresting to +// the liveness computation. +func isfunny(n *Node) bool { + return n.Sym != nil && (n.Sym.Name == ".fp" || n.Sym.Name == ".args") +} + +// Computes the effects of an instruction on a set of +// variables. The vars argument is an array of Node*s. +// +// The output vectors give bits for variables: +// uevar - used by this instruction +// varkill - killed by this instruction +// for variables without address taken, means variable was set +// for variables with address taken, means variable was marked dead +// avarinit - initialized or referred to by this instruction, +// only for variables with address taken but not escaping to heap +// +// The avarinit output serves as a signal that the data has been +// initialized, because any use of a variable must come after its +// initialization. +func progeffects(prog *obj.Prog, vars []*Node, uevar Bvec, varkill Bvec, avarinit Bvec) { + bvresetall(uevar) + bvresetall(varkill) + bvresetall(avarinit) + + if prog.As == obj.ARET { + // Return instructions implicitly read all the arguments. For + // the sake of correctness, out arguments must be read. For the + // sake of backtrace quality, we read in arguments as well. + // + // A return instruction with a p->to is a tail return, which brings + // the stack pointer back up (if it ever went down) and then jumps + // to a new function entirely. That form of instruction must read + // all the parameters for correctness, and similarly it must not + // read the out arguments - they won't be set until the new + // function runs. + for i, node := range vars { + switch node.Class &^ PHEAP { + case PPARAM: + bvset(uevar, int32(i)) + + // If the result had its address taken, it is being tracked + // by the avarinit code, which does not use uevar. + // If we added it to uevar too, we'd not see any kill + // and decide that the varible was live entry, which it is not. + // So only use uevar in the non-addrtaken case. + // The p->to.type == thearch.D_NONE limits the bvset to + // non-tail-call return instructions; see note above + // the for loop for details. + case PPARAMOUT: + if !node.Addrtaken && prog.To.Type == obj.TYPE_NONE { + bvset(uevar, int32(i)) + } + } + } + + return + } + + if prog.As == obj.ATEXT { + // A text instruction marks the entry point to a function and + // the definition point of all in arguments. + for i, node := range vars { + switch node.Class &^ PHEAP { + case PPARAM: + if node.Addrtaken { + bvset(avarinit, int32(i)) + } + bvset(varkill, int32(i)) + } + } + + return + } + + if prog.Info.Flags&(LeftRead|LeftWrite|LeftAddr) != 0 { + from := &prog.From + if from.Node != nil && from.Sym != nil && ((from.Node).(*Node)).Curfn == Curfn { + switch ((from.Node).(*Node)).Class &^ PHEAP { + case PAUTO, PPARAM, PPARAMOUT: + pos, ok := from.Node.(*Node).Opt.(int32) // index in vars + if !ok { + goto Next + } + if pos >= int32(len(vars)) || vars[pos] != from.Node { + Fatal("bad bookkeeping in liveness %v %d", Nconv(from.Node.(*Node), 0), pos) + } + if ((from.Node).(*Node)).Addrtaken { + bvset(avarinit, pos) + } else { + if prog.Info.Flags&(LeftRead|LeftAddr) != 0 { + bvset(uevar, pos) + } + if prog.Info.Flags&LeftWrite != 0 { + if from.Node != nil && !Isfat(((from.Node).(*Node)).Type) { + bvset(varkill, pos) + } + } + } + } + } + } + +Next: + if prog.Info.Flags&(RightRead|RightWrite|RightAddr) != 0 { + to := &prog.To + if to.Node != nil && to.Sym != nil && ((to.Node).(*Node)).Curfn == Curfn { + switch ((to.Node).(*Node)).Class &^ PHEAP { + case PAUTO, PPARAM, PPARAMOUT: + pos, ok := to.Node.(*Node).Opt.(int32) // index in vars + if !ok { + return + } + if pos >= int32(len(vars)) || vars[pos] != to.Node { + Fatal("bad bookkeeping in liveness %v %d", Nconv(to.Node.(*Node), 0), pos) + } + if ((to.Node).(*Node)).Addrtaken { + if prog.As != obj.AVARKILL { + bvset(avarinit, pos) + } + if prog.As == obj.AVARDEF || prog.As == obj.AVARKILL { + bvset(varkill, pos) + } + } else { + // RightRead is a read, obviously. + // RightAddr by itself is also implicitly a read. + // + // RightAddr|RightWrite means that the address is being taken + // but only so that the instruction can write to the value. + // It is not a read. It is equivalent to RightWrite except that + // having the RightAddr bit set keeps the registerizer from + // trying to substitute a register for the memory location. + if (prog.Info.Flags&RightRead != 0) || prog.Info.Flags&(RightAddr|RightWrite) == RightAddr { + bvset(uevar, pos) + } + if prog.Info.Flags&RightWrite != 0 { + if to.Node != nil && (!Isfat(((to.Node).(*Node)).Type) || prog.As == obj.AVARDEF) { + bvset(varkill, pos) + } + } + } + } + } + } +} + +// Constructs a new liveness structure used to hold the global state of the +// liveness computation. The cfg argument is an array of BasicBlock*s and the +// vars argument is an array of Node*s. +func newliveness(fn *Node, ptxt *obj.Prog, cfg []*BasicBlock, vars []*Node) *Liveness { + result := new(Liveness) + result.fn = fn + result.ptxt = ptxt + result.cfg = cfg + result.vars = vars + + nblocks := int32(len(cfg)) + nvars := int32(len(vars)) + bulk := bvbulkalloc(nvars, nblocks*7) + for _, bb := range cfg { + bb.uevar = bulk.next() + bb.varkill = bulk.next() + bb.livein = bulk.next() + bb.liveout = bulk.next() + bb.avarinit = bulk.next() + bb.avarinitany = bulk.next() + bb.avarinitall = bulk.next() + } + + result.livepointers = make([]Bvec, 0, 0) + result.argslivepointers = make([]Bvec, 0, 0) + return result +} + +// Frees the liveness structure and all of its leaf data structures. +func freeliveness(lv *Liveness) { + if lv == nil { + Fatal("freeliveness: cannot free nil") + } +} + +func printeffects(p *obj.Prog, uevar Bvec, varkill Bvec, avarinit Bvec) { + fmt.Printf("effects of %v", p) + fmt.Printf("\nuevar: ") + bvprint(uevar) + fmt.Printf("\nvarkill: ") + bvprint(varkill) + fmt.Printf("\navarinit: ") + bvprint(avarinit) + fmt.Printf("\n") +} + +// Pretty print a variable node. Uses Pascal like conventions for pointers and +// addresses to avoid confusing the C like conventions used in the node variable +// names. +func printnode(node *Node) { + p := "" + if haspointers(node.Type) { + p = "^" + } + a := "" + if node.Addrtaken { + a = "@" + } + fmt.Printf(" %v%s%s", node, p, a) +} + +// Pretty print a list of variables. The vars argument is an array of Node*s. +func printvars(name string, bv Bvec, vars []*Node) { + fmt.Printf("%s:", name) + for i, node := range vars { + if bvget(bv, int32(i)) != 0 { + printnode(node) + } + } + fmt.Printf("\n") +} + +// Prints a basic block annotated with the information computed by liveness +// analysis. +func livenessprintblock(lv *Liveness, bb *BasicBlock) { + fmt.Printf("basic block %d\n", bb.rpo) + + fmt.Printf("\tpred:") + for _, pred := range bb.pred { + fmt.Printf(" %d", pred.rpo) + } + fmt.Printf("\n") + + fmt.Printf("\tsucc:") + for _, succ := range bb.succ { + fmt.Printf(" %d", succ.rpo) + } + fmt.Printf("\n") + + printvars("\tuevar", bb.uevar, []*Node(lv.vars)) + printvars("\tvarkill", bb.varkill, []*Node(lv.vars)) + printvars("\tlivein", bb.livein, []*Node(lv.vars)) + printvars("\tliveout", bb.liveout, []*Node(lv.vars)) + printvars("\tavarinit", bb.avarinit, []*Node(lv.vars)) + printvars("\tavarinitany", bb.avarinitany, []*Node(lv.vars)) + printvars("\tavarinitall", bb.avarinitall, []*Node(lv.vars)) + + fmt.Printf("\tprog:\n") + for prog := bb.first; ; prog = prog.Link { + fmt.Printf("\t\t%v", prog) + if prog.As == obj.APCDATA && prog.From.Offset == obj.PCDATA_StackMapIndex { + pos := int32(prog.To.Offset) + live := lv.livepointers[pos] + fmt.Printf(" ") + bvprint(live) + } + + fmt.Printf("\n") + if prog == bb.last { + break + } + } +} + +// Prints a control flow graph annotated with any information computed by +// liveness analysis. +func livenessprintcfg(lv *Liveness) { + for _, bb := range lv.cfg { + livenessprintblock(lv, bb) + } +} + +func checkauto(fn *Node, p *obj.Prog, n *Node) { + for l := fn.Func.Dcl; l != nil; l = l.Next { + if l.N.Op == ONAME && l.N.Class == PAUTO && l.N == n { + return + } + } + + if n == nil { + fmt.Printf("%v: checkauto %v: nil node in %v\n", p.Line(), Curfn, p) + return + } + + fmt.Printf("checkauto %v: %v (%p; class=%d) not found in %v\n", Curfn, n, n, n.Class, p) + for l := fn.Func.Dcl; l != nil; l = l.Next { + fmt.Printf("\t%v (%p; class=%d)\n", l.N, l.N, l.N.Class) + } + Yyerror("checkauto: invariant lost") +} + +func checkparam(fn *Node, p *obj.Prog, n *Node) { + if isfunny(n) { + return + } + var a *Node + var class uint8 + for l := fn.Func.Dcl; l != nil; l = l.Next { + a = l.N + class = a.Class &^ PHEAP + if a.Op == ONAME && (class == PPARAM || class == PPARAMOUT) && a == n { + return + } + } + + fmt.Printf("checkparam %v: %v (%p; class=%d) not found in %v\n", Curfn, n, n, n.Class, p) + for l := fn.Func.Dcl; l != nil; l = l.Next { + fmt.Printf("\t%v (%p; class=%d)\n", l.N, l.N, l.N.Class) + } + Yyerror("checkparam: invariant lost") +} + +func checkprog(fn *Node, p *obj.Prog) { + if p.From.Name == obj.NAME_AUTO { + checkauto(fn, p, p.From.Node.(*Node)) + } + if p.From.Name == obj.NAME_PARAM { + checkparam(fn, p, p.From.Node.(*Node)) + } + if p.To.Name == obj.NAME_AUTO { + checkauto(fn, p, p.To.Node.(*Node)) + } + if p.To.Name == obj.NAME_PARAM { + checkparam(fn, p, p.To.Node.(*Node)) + } +} + +// Check instruction invariants. We assume that the nodes corresponding to the +// sources and destinations of memory operations will be declared in the +// function. This is not strictly true, as is the case for the so-called funny +// nodes and there are special cases to skip over that stuff. The analysis will +// fail if this invariant blindly changes. +func checkptxt(fn *Node, firstp *obj.Prog) { + if debuglive == 0 { + return + } + + for p := firstp; p != nil; p = p.Link { + if false { + fmt.Printf("analyzing '%v'\n", p) + } + if p.As != obj.ADATA && p.As != obj.AGLOBL && p.As != obj.ATYPE { + checkprog(fn, p) + } + } +} + +// NOTE: The bitmap for a specific type t should be cached in t after the first run +// and then simply copied into bv at the correct offset on future calls with +// the same type t. On https://rsc.googlecode.com/hg/testdata/slow.go, onebitwalktype1 +// accounts for 40% of the 6g execution time. +func onebitwalktype1(t *Type, xoffset *int64, bv Bvec) { + if t.Align > 0 && *xoffset&int64(t.Align-1) != 0 { + Fatal("onebitwalktype1: invalid initial alignment, %v", t) + } + + switch t.Etype { + case TINT8, + TUINT8, + TINT16, + TUINT16, + TINT32, + TUINT32, + TINT64, + TUINT64, + TINT, + TUINT, + TUINTPTR, + TBOOL, + TFLOAT32, + TFLOAT64, + TCOMPLEX64, + TCOMPLEX128: + *xoffset += t.Width + + case TPTR32, + TPTR64, + TUNSAFEPTR, + TFUNC, + TCHAN, + TMAP: + if *xoffset&int64(Widthptr-1) != 0 { + Fatal("onebitwalktype1: invalid alignment, %v", t) + } + bvset(bv, int32(*xoffset/int64(Widthptr))) // pointer + *xoffset += t.Width + + case TSTRING: + // struct { byte *str; intgo len; } + if *xoffset&int64(Widthptr-1) != 0 { + Fatal("onebitwalktype1: invalid alignment, %v", t) + } + bvset(bv, int32(*xoffset/int64(Widthptr))) //pointer in first slot + *xoffset += t.Width + + case TINTER: + // struct { Itab *tab; void *data; } + // or, when isnilinter(t)==true: + // struct { Type *type; void *data; } + if *xoffset&int64(Widthptr-1) != 0 { + Fatal("onebitwalktype1: invalid alignment, %v", t) + } + bvset(bv, int32(*xoffset/int64(Widthptr))) // pointer in first slot + bvset(bv, int32(*xoffset/int64(Widthptr)+1)) // pointer in second slot + *xoffset += t.Width + + case TARRAY: + // 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) + } + if Isslice(t) { + // struct { byte *array; uintgo len; uintgo cap; } + if *xoffset&int64(Widthptr-1) != 0 { + Fatal("onebitwalktype1: invalid TARRAY alignment, %v", t) + } + bvset(bv, int32(*xoffset/int64(Widthptr))) // pointer in first slot (BitsPointer) + *xoffset += t.Width + } else { + for i := int64(0); i < t.Bound; i++ { + onebitwalktype1(t.Type, xoffset, bv) + } + } + + case TSTRUCT: + o := int64(0) + var fieldoffset int64 + for t1 := t.Type; t1 != nil; t1 = t1.Down { + fieldoffset = t1.Width + *xoffset += fieldoffset - o + onebitwalktype1(t1.Type, xoffset, bv) + o = fieldoffset + t1.Type.Width + } + + *xoffset += t.Width - o + + default: + Fatal("onebitwalktype1: unexpected type, %v", t) + } +} + +// Returns the number of words of local variables. +func localswords() int32 { + return int32(stkptrsize / int64(Widthptr)) +} + +// Returns the number of words of in and out arguments. +func argswords() int32 { + return int32(Curfn.Type.Argwid / int64(Widthptr)) +} + +// Generates live pointer value maps for arguments and local variables. The +// this argument and the in arguments are always assumed live. The vars +// argument is an array of Node*s. +func onebitlivepointermap(lv *Liveness, liveout Bvec, vars []*Node, args Bvec, locals Bvec) { + var node *Node + var xoffset int64 + + for i := int32(0); ; i++ { + i = int32(bvnext(liveout, i)) + if i < 0 { + break + } + node = vars[i] + switch node.Class { + case PAUTO: + xoffset = node.Xoffset + stkptrsize + onebitwalktype1(node.Type, &xoffset, locals) + + case PPARAM, PPARAMOUT: + xoffset = node.Xoffset + onebitwalktype1(node.Type, &xoffset, args) + } + } + + // The node list only contains declared names. + // If the receiver or arguments are unnamed, they will be omitted + // from the list above. Preserve those values - even though they are unused - + // in order to keep their addresses live for use in stack traces. + thisargtype := getthisx(lv.fn.Type) + + if thisargtype != nil { + xoffset = 0 + onebitwalktype1(thisargtype, &xoffset, args) + } + + inargtype := getinargx(lv.fn.Type) + if inargtype != nil { + xoffset = 0 + onebitwalktype1(inargtype, &xoffset, args) + } +} + +// Construct a disembodied instruction. +func unlinkedprog(as int) *obj.Prog { + p := Ctxt.NewProg() + Clearp(p) + p.As = int16(as) + return p +} + +// Construct a new PCDATA instruction associated with and for the purposes of +// covering an existing instruction. +func newpcdataprog(prog *obj.Prog, index int32) *obj.Prog { + var from Node + var to Node + + Nodconst(&from, Types[TINT32], obj.PCDATA_StackMapIndex) + Nodconst(&to, Types[TINT32], int64(index)) + pcdata := unlinkedprog(obj.APCDATA) + pcdata.Lineno = prog.Lineno + Naddr(&pcdata.From, &from) + Naddr(&pcdata.To, &to) + return pcdata +} + +// Returns true for instructions that are safe points that must be annotated +// with liveness information. +func issafepoint(prog *obj.Prog) bool { + return prog.As == obj.ATEXT || prog.As == obj.ACALL +} + +// Initializes the sets for solving the live variables. Visits all the +// instructions in each basic block to summarizes the information at each basic +// block +func livenessprologue(lv *Liveness) { + nvars := int32(len(lv.vars)) + uevar := bvalloc(nvars) + varkill := bvalloc(nvars) + avarinit := bvalloc(nvars) + for _, bb := range lv.cfg { + // Walk the block instructions backward and update the block + // effects with the each prog effects. + for p := bb.last; p != nil; p = p.Opt.(*obj.Prog) { + progeffects(p, []*Node(lv.vars), uevar, varkill, avarinit) + if debuglive >= 3 { + printeffects(p, uevar, varkill, avarinit) + } + bvor(bb.varkill, bb.varkill, varkill) + bvandnot(bb.uevar, bb.uevar, varkill) + bvor(bb.uevar, bb.uevar, uevar) + } + + // Walk the block instructions forward to update avarinit bits. + // avarinit describes the effect at the end of the block, not the beginning. + bvresetall(varkill) + + for p := bb.first; ; p = p.Link { + progeffects(p, []*Node(lv.vars), uevar, varkill, avarinit) + if debuglive >= 3 { + printeffects(p, uevar, varkill, avarinit) + } + bvandnot(bb.avarinit, bb.avarinit, varkill) + bvor(bb.avarinit, bb.avarinit, avarinit) + if p == bb.last { + break + } + } + } +} + +// Solve the liveness dataflow equations. +func livenesssolve(lv *Liveness) { + // These temporary bitvectors exist to avoid successive allocations and + // frees within the loop. + newlivein := bvalloc(int32(len(lv.vars))) + + newliveout := bvalloc(int32(len(lv.vars))) + any := bvalloc(int32(len(lv.vars))) + all := bvalloc(int32(len(lv.vars))) + + // Push avarinitall, avarinitany forward. + // avarinitall says the addressed var is initialized along all paths reaching the block exit. + // avarinitany says the addressed var is initialized along some path reaching the block exit. + for i, bb := range lv.cfg { + if i == 0 { + bvcopy(bb.avarinitall, bb.avarinit) + } else { + bvresetall(bb.avarinitall) + bvnot(bb.avarinitall) + } + bvcopy(bb.avarinitany, bb.avarinit) + } + + change := int32(1) + for change != 0 { + change = 0 + for _, bb := range lv.cfg { + bvresetall(any) + bvresetall(all) + for j, pred := range bb.pred { + if j == 0 { + bvcopy(any, pred.avarinitany) + bvcopy(all, pred.avarinitall) + } else { + bvor(any, any, pred.avarinitany) + bvand(all, all, pred.avarinitall) + } + } + + bvandnot(any, any, bb.varkill) + bvandnot(all, all, bb.varkill) + bvor(any, any, bb.avarinit) + bvor(all, all, bb.avarinit) + if bvcmp(any, bb.avarinitany) != 0 { + change = 1 + bvcopy(bb.avarinitany, any) + } + + if bvcmp(all, bb.avarinitall) != 0 { + change = 1 + bvcopy(bb.avarinitall, all) + } + } + } + + // Iterate through the blocks in reverse round-robin fashion. A work + // queue might be slightly faster. As is, the number of iterations is + // so low that it hardly seems to be worth the complexity. + change = 1 + + for change != 0 { + change = 0 + + // Walk blocks in the general direction of propagation. This + // improves convergence. + for i := len(lv.cfg) - 1; i >= 0; i-- { + bb := lv.cfg[i] + + // A variable is live on output from this block + // if it is live on input to some successor. + // + // out[b] = \bigcup_{s \in succ[b]} in[s] + bvresetall(newliveout) + for _, succ := range bb.succ { + bvor(newliveout, newliveout, succ.livein) + } + + if bvcmp(bb.liveout, newliveout) != 0 { + change = 1 + bvcopy(bb.liveout, newliveout) + } + + // A variable is live on input to this block + // if it is live on output from this block and + // not set by the code in this block. + // + // in[b] = uevar[b] \cup (out[b] \setminus varkill[b]) + bvandnot(newlivein, bb.liveout, bb.varkill) + + bvor(bb.livein, newlivein, bb.uevar) + } + } +} + +// This function is slow but it is only used for generating debug prints. +// Check whether n is marked live in args/locals. +func islive(n *Node, args Bvec, locals Bvec) bool { + switch n.Class { + case PPARAM, PPARAMOUT: + for i := 0; int64(i) < n.Type.Width/int64(Widthptr); i++ { + if bvget(args, int32(n.Xoffset/int64(Widthptr)+int64(i))) != 0 { + return true + } + } + + case PAUTO: + for i := 0; int64(i) < n.Type.Width/int64(Widthptr); i++ { + if bvget(locals, int32((n.Xoffset+stkptrsize)/int64(Widthptr)+int64(i))) != 0 { + return true + } + } + } + + return false +} + +// Visits all instructions in a basic block and computes a bit vector of live +// variables at each safe point locations. +func livenessepilogue(lv *Liveness) { + var pred *BasicBlock + var args Bvec + var locals Bvec + var n *Node + var p *obj.Prog + var j int32 + var pos int32 + var xoffset int64 + + nvars := int32(len(lv.vars)) + livein := bvalloc(nvars) + liveout := bvalloc(nvars) + uevar := bvalloc(nvars) + varkill := bvalloc(nvars) + avarinit := bvalloc(nvars) + any := bvalloc(nvars) + all := bvalloc(nvars) + ambig := bvalloc(localswords()) + nmsg := int32(0) + startmsg := int32(0) + + for _, bb := range lv.cfg { + // Compute avarinitany and avarinitall for entry to block. + // This duplicates information known during livenesssolve + // but avoids storing two more vectors for each block. + bvresetall(any) + + bvresetall(all) + for j = 0; j < int32(len(bb.pred)); j++ { + pred = bb.pred[j] + if j == 0 { + bvcopy(any, pred.avarinitany) + bvcopy(all, pred.avarinitall) + } else { + bvor(any, any, pred.avarinitany) + bvand(all, all, pred.avarinitall) + } + } + + // Walk forward through the basic block instructions and + // allocate liveness maps for those instructions that need them. + // Seed the maps with information about the addrtaken variables. + for p = bb.first; ; p = p.Link { + progeffects(p, []*Node(lv.vars), uevar, varkill, avarinit) + bvandnot(any, any, varkill) + bvandnot(all, all, varkill) + bvor(any, any, avarinit) + bvor(all, all, avarinit) + + if issafepoint(p) { + // Annotate ambiguously live variables so that they can + // be zeroed at function entry. + // livein and liveout are dead here and used as temporaries. + bvresetall(livein) + + bvandnot(liveout, any, all) + if !bvisempty(liveout) { + for pos = 0; pos < liveout.n; pos++ { + if bvget(liveout, pos) == 0 { + continue + } + bvset(all, pos) // silence future warnings in this block + n = lv.vars[pos] + 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)) + } + + // Record in 'ambiguous' bitmap. + xoffset = n.Xoffset + stkptrsize + + onebitwalktype1(n.Type, &xoffset, ambig) + } + } + } + + // Allocate a bit vector for each class and facet of + // value we are tracking. + + // Live stuff first. + args = bvalloc(argswords()) + + lv.argslivepointers = append(lv.argslivepointers, args) + locals = bvalloc(localswords()) + lv.livepointers = append(lv.livepointers, locals) + + if debuglive >= 3 { + fmt.Printf("%v\n", p) + printvars("avarinitany", any, lv.vars) + } + + // Record any values with an "address taken" reaching + // this code position as live. Must do now instead of below + // because the any/all calculation requires walking forward + // over the block (as this loop does), while the liveout + // requires walking backward (as the next loop does). + onebitlivepointermap(lv, any, lv.vars, args, locals) + } + + if p == bb.last { + break + } + } + + bb.lastbitmapindex = len(lv.livepointers) - 1 + } + + var fmt_ string + var next *obj.Prog + var numlive int32 + var msg []string + for _, bb := range lv.cfg { + if debuglive >= 1 && Curfn.Nname.Sym.Name != "init" && Curfn.Nname.Sym.Name[0] != '.' { + nmsg = int32(len(lv.livepointers)) + startmsg = nmsg + msg = make([]string, nmsg) + for j = 0; j < nmsg; j++ { + msg[j] = "" + } + } + + // walk backward, emit pcdata and populate the maps + pos = int32(bb.lastbitmapindex) + + if pos < 0 { + // the first block we encounter should have the ATEXT so + // at no point should pos ever be less than zero. + Fatal("livenessepilogue") + } + + bvcopy(livein, bb.liveout) + for p = bb.last; p != nil; p = next { + next = p.Opt.(*obj.Prog) // splicebefore modifies p->opt + + // Propagate liveness information + progeffects(p, lv.vars, uevar, varkill, avarinit) + + bvcopy(liveout, livein) + bvandnot(livein, liveout, varkill) + bvor(livein, livein, uevar) + if debuglive >= 3 && issafepoint(p) { + fmt.Printf("%v\n", p) + printvars("uevar", uevar, lv.vars) + printvars("varkill", varkill, lv.vars) + printvars("livein", livein, lv.vars) + printvars("liveout", liveout, lv.vars) + } + + if issafepoint(p) { + // Found an interesting instruction, record the + // corresponding liveness information. + + // Useful sanity check: on entry to the function, + // the only things that can possibly be live are the + // input parameters. + if p.As == obj.ATEXT { + for j = 0; j < liveout.n; j++ { + if bvget(liveout, j) == 0 { + continue + } + n = lv.vars[j] + if n.Class != PPARAM { + yyerrorl(int(p.Lineno), "internal error: %v %v recorded as live on entry", Curfn.Nname, Nconv(n, obj.FmtLong)) + } + } + } + + // Record live pointers. + args = lv.argslivepointers[pos] + + locals = lv.livepointers[pos] + onebitlivepointermap(lv, liveout, lv.vars, args, locals) + + // Ambiguously live variables are zeroed immediately after + // function entry. Mark them live for all the non-entry bitmaps + // so that GODEBUG=gcdead=1 mode does not poison them. + if p.As == obj.ACALL { + bvor(locals, locals, ambig) + } + + // Show live pointer bitmaps. + // We're interpreting the args and locals bitmap instead of liveout so that we + // include the bits added by the avarinit logic in the + // previous loop. + if msg != nil { + fmt_ = "" + fmt_ += fmt.Sprintf("%v: live at ", p.Line()) + if p.As == obj.ACALL && p.To.Node != nil { + fmt_ += fmt.Sprintf("call to %s:", ((p.To.Node).(*Node)).Sym.Name) + } else if p.As == obj.ACALL { + fmt_ += "indirect call:" + } else { + fmt_ += fmt.Sprintf("entry to %s:", ((p.From.Node).(*Node)).Sym.Name) + } + numlive = 0 + for j = 0; j < int32(len(lv.vars)); j++ { + n = lv.vars[j] + if islive(n, args, locals) { + fmt_ += fmt.Sprintf(" %v", n) + numlive++ + } + } + + fmt_ += "\n" + if numlive == 0 { // squelch message + + } else { + startmsg-- + msg[startmsg] = fmt_ + } + } + + // Only CALL instructions need a PCDATA annotation. + // The TEXT instruction annotation is implicit. + if p.As == obj.ACALL { + if isdeferreturn(p) { + // runtime.deferreturn modifies its return address to return + // back to the CALL, not to the subsequent instruction. + // Because the return comes back one instruction early, + // the PCDATA must begin one instruction early too. + // The instruction before a call to deferreturn is always a + // no-op, to keep PC-specific data unambiguous. + splicebefore(lv, bb, newpcdataprog(p.Opt.(*obj.Prog), pos), p.Opt.(*obj.Prog)) + } else { + splicebefore(lv, bb, newpcdataprog(p, pos), p) + } + } + + pos-- + } + } + + if msg != nil { + for j = startmsg; j < nmsg; j++ { + if msg[j] != "" { + fmt.Printf("%s", msg[j]) + } + } + + msg = nil + nmsg = 0 + startmsg = 0 + } + } + + Flusherrors() +} + +// FNV-1 hash function constants. +const ( + H0 = 2166136261 + Hp = 16777619 +) + +func hashbitmap(h uint32, bv Bvec) uint32 { + var w uint32 + + n := int((bv.n + 31) / 32) + for i := 0; i < n; i++ { + w = bv.b[i] + h = (h * Hp) ^ (w & 0xff) + h = (h * Hp) ^ ((w >> 8) & 0xff) + h = (h * Hp) ^ ((w >> 16) & 0xff) + h = (h * Hp) ^ ((w >> 24) & 0xff) + } + + return h +} + +// Compact liveness information by coalescing identical per-call-site bitmaps. +// The merging only happens for a single function, not across the entire binary. +// +// There are actually two lists of bitmaps, one list for the local variables and one +// list for the function arguments. Both lists are indexed by the same PCDATA +// index, so the corresponding pairs must be considered together when +// merging duplicates. The argument bitmaps change much less often during +// function execution than the local variable bitmaps, so it is possible that +// we could introduce a separate PCDATA index for arguments vs locals and +// then compact the set of argument bitmaps separately from the set of +// local variable bitmaps. As of 2014-04-02, doing this to the godoc binary +// is actually a net loss: we save about 50k of argument bitmaps but the new +// PCDATA tables cost about 100k. So for now we keep using a single index for +// both bitmap lists. +func livenesscompact(lv *Liveness) { + // Linear probing hash table of bitmaps seen so far. + // The hash table has 4n entries to keep the linear + // scan short. An entry of -1 indicates an empty slot. + n := len(lv.livepointers) + + tablesize := 4 * n + table := make([]int, tablesize) + for i := range table { + table[i] = -1 + } + + // remap[i] = the new index of the old bit vector #i. + remap := make([]int, n) + + for i := range remap { + remap[i] = -1 + } + uniq := 0 // unique tables found so far + + // Consider bit vectors in turn. + // If new, assign next number using uniq, + // record in remap, record in lv->livepointers and lv->argslivepointers + // under the new index, and add entry to hash table. + // If already seen, record earlier index in remap and free bitmaps. + var jarg Bvec + var j int + var h uint32 + var arg Bvec + var jlocal Bvec + var local Bvec + for i := 0; i < n; i++ { + local = lv.livepointers[i] + arg = lv.argslivepointers[i] + h = hashbitmap(hashbitmap(H0, local), arg) % uint32(tablesize) + + for { + j = table[h] + if j < 0 { + break + } + jlocal = lv.livepointers[j] + jarg = lv.argslivepointers[j] + if bvcmp(local, jlocal) == 0 && bvcmp(arg, jarg) == 0 { + remap[i] = j + goto Next + } + + h++ + if h == uint32(tablesize) { + h = 0 + } + } + + table[h] = uniq + remap[i] = uniq + lv.livepointers[uniq] = local + lv.argslivepointers[uniq] = arg + uniq++ + Next: + } + + // We've already reordered lv->livepointers[0:uniq] + // and lv->argslivepointers[0:uniq] and freed the bitmaps + // we don't need anymore. Clear the pointers later in the + // array so that we can tell where the coalesced bitmaps stop + // and so that we don't double-free when cleaning up. + for j := uniq; j < n; j++ { + lv.livepointers[j] = Bvec{} + lv.argslivepointers[j] = Bvec{} + } + + // Rewrite PCDATA instructions to use new numbering. + var i int + for p := lv.ptxt; p != nil; p = p.Link { + if p.As == obj.APCDATA && p.From.Offset == obj.PCDATA_StackMapIndex { + i = int(p.To.Offset) + if i >= 0 { + p.To.Offset = int64(remap[i]) + } + } + } +} + +func printbitset(printed int, name string, vars []*Node, bits Bvec) int { + started := 0 + for i, n := range vars { + if bvget(bits, int32(i)) == 0 { + continue + } + if started == 0 { + if printed == 0 { + fmt.Printf("\t") + } else { + fmt.Printf(" ") + } + started = 1 + printed = 1 + fmt.Printf("%s=", name) + } else { + fmt.Printf(",") + } + + fmt.Printf("%s", n.Sym.Name) + } + + return printed +} + +// Prints the computed liveness information and inputs, for debugging. +// This format synthesizes the information used during the multiple passes +// into a single presentation. +func livenessprintdebug(lv *Liveness) { + var j int + var printed int + var p *obj.Prog + var args Bvec + var locals Bvec + var n *Node + + fmt.Printf("liveness: %s\n", Curfn.Nname.Sym.Name) + + uevar := bvalloc(int32(len(lv.vars))) + varkill := bvalloc(int32(len(lv.vars))) + avarinit := bvalloc(int32(len(lv.vars))) + + pcdata := 0 + for i, bb := range lv.cfg { + if i > 0 { + fmt.Printf("\n") + } + + // bb#0 pred=1,2 succ=3,4 + fmt.Printf("bb#%d pred=", i) + + for j = 0; j < len(bb.pred); j++ { + if j > 0 { + fmt.Printf(",") + } + fmt.Printf("%d", (bb.pred[j]).rpo) + } + + fmt.Printf(" succ=") + for j = 0; j < len(bb.succ); j++ { + if j > 0 { + fmt.Printf(",") + } + fmt.Printf("%d", (bb.succ[j]).rpo) + } + + fmt.Printf("\n") + + // initial settings + printed = 0 + + printed = printbitset(printed, "uevar", lv.vars, bb.uevar) + printed = printbitset(printed, "livein", lv.vars, bb.livein) + if printed != 0 { + fmt.Printf("\n") + } + + // program listing, with individual effects listed + for p = bb.first; ; p = p.Link { + fmt.Printf("%v\n", p) + if p.As == obj.APCDATA && p.From.Offset == obj.PCDATA_StackMapIndex { + pcdata = int(p.To.Offset) + } + progeffects(p, lv.vars, uevar, varkill, avarinit) + printed = 0 + printed = printbitset(printed, "uevar", lv.vars, uevar) + printed = printbitset(printed, "varkill", lv.vars, varkill) + printed = printbitset(printed, "avarinit", lv.vars, avarinit) + if printed != 0 { + fmt.Printf("\n") + } + if issafepoint(p) { + args = lv.argslivepointers[pcdata] + locals = lv.livepointers[pcdata] + fmt.Printf("\tlive=") + printed = 0 + for j = 0; j < len(lv.vars); j++ { + n = lv.vars[j] + if islive(n, args, locals) { + tmp9 := printed + printed++ + if tmp9 != 0 { + fmt.Printf(",") + } + fmt.Printf("%v", n) + } + } + + fmt.Printf("\n") + } + + if p == bb.last { + break + } + } + + // bb bitsets + fmt.Printf("end\n") + + printed = printbitset(printed, "varkill", lv.vars, bb.varkill) + printed = printbitset(printed, "liveout", lv.vars, bb.liveout) + printed = printbitset(printed, "avarinit", lv.vars, bb.avarinit) + printed = printbitset(printed, "avarinitany", lv.vars, bb.avarinitany) + printed = printbitset(printed, "avarinitall", lv.vars, bb.avarinitall) + if printed != 0 { + fmt.Printf("\n") + } + } + + fmt.Printf("\n") +} + +// Dumps an array of bitmaps to a symbol as a sequence of uint32 values. The +// first word dumped is the total number of bitmaps. The second word is the +// length of the bitmaps. All bitmaps are assumed to be of equal length. The +// words that are followed are the raw bitmap words. The arr argument is an +// array of Node*s. +func onebitwritesymbol(arr []Bvec, sym *Sym) { + var i int + var j int + var word uint32 + + n := len(arr) + off := 0 + off += 4 // number of bitmaps, to fill in later + bv := arr[0] + off = duint32(sym, off, uint32(bv.n)) // number of bits in each bitmap + for i = 0; i < n; i++ { + // bitmap words + bv = arr[i] + + if bv.b == nil { + break + } + for j = 0; int32(j) < bv.n; j += 32 { + word = bv.b[j/32] + + // Runtime reads the bitmaps as byte arrays. Oblige. + off = duint8(sym, off, uint8(word)) + + off = duint8(sym, off, uint8(word>>8)) + off = duint8(sym, off, uint8(word>>16)) + off = duint8(sym, off, uint8(word>>24)) + } + } + + duint32(sym, 0, uint32(i)) // number of bitmaps + ggloblsym(sym, int32(off), obj.RODATA) +} + +func printprog(p *obj.Prog) { + for p != nil { + fmt.Printf("%v\n", p) + p = p.Link + } +} + +// Entry pointer for liveness analysis. Constructs a complete CFG, solves for +// the liveness of pointer variables in the function, and emits a runtime data +// structure read by the garbage collector. +func liveness(fn *Node, firstp *obj.Prog, argssym *Sym, livesym *Sym) { + // Change name to dump debugging information only for a specific function. + debugdelta := 0 + + if Curfn.Nname.Sym.Name == "!" { + debugdelta = 2 + } + + debuglive += debugdelta + if debuglive >= 3 { + fmt.Printf("liveness: %s\n", Curfn.Nname.Sym.Name) + printprog(firstp) + } + + checkptxt(fn, firstp) + + // Construct the global liveness state. + cfg := newcfg(firstp) + + if debuglive >= 3 { + printcfg([]*BasicBlock(cfg)) + } + vars := getvariables(fn) + lv := newliveness(fn, firstp, cfg, vars) + + // Run the dataflow framework. + livenessprologue(lv) + + if debuglive >= 3 { + livenessprintcfg(lv) + } + livenesssolve(lv) + if debuglive >= 3 { + livenessprintcfg(lv) + } + livenessepilogue(lv) + if debuglive >= 3 { + livenessprintcfg(lv) + } + livenesscompact(lv) + + if debuglive >= 2 { + livenessprintdebug(lv) + } + + // Emit the live pointer map data structures + onebitwritesymbol(lv.livepointers, livesym) + + onebitwritesymbol(lv.argslivepointers, argssym) + + // Free everything. + for l := fn.Func.Dcl; l != nil; l = l.Next { + if l.N != nil { + l.N.Opt = nil + } + } + freeliveness(lv) + + freecfg([]*BasicBlock(cfg)) + + debuglive -= debugdelta +} diff --git a/src/cmd/compile/internal/gc/popt.go b/src/cmd/compile/internal/gc/popt.go new file mode 100644 index 0000000000..ce904e155b --- /dev/null +++ b/src/cmd/compile/internal/gc/popt.go @@ -0,0 +1,1086 @@ +// Derived from Inferno utils/6c/gc.h +// http://code.google.com/p/inferno-os/source/browse/utils/6c/gc.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// "Portable" optimizations. + +package gc + +import ( + "cmd/internal/obj" + "fmt" + "sort" + "strings" +) + +type OptStats struct { + Ncvtreg int32 + Nspill int32 + Nreload int32 + Ndelmov int32 + Nvar int32 + Naddr int32 +} + +var Ostats OptStats + +var noreturn_symlist [10]*Sym + +// p is a call instruction. Does the call fail to return? +func Noreturn(p *obj.Prog) bool { + if noreturn_symlist[0] == nil { + noreturn_symlist[0] = Pkglookup("panicindex", Runtimepkg) + noreturn_symlist[1] = Pkglookup("panicslice", Runtimepkg) + noreturn_symlist[2] = Pkglookup("throwinit", Runtimepkg) + noreturn_symlist[3] = Pkglookup("gopanic", Runtimepkg) + noreturn_symlist[4] = Pkglookup("panicwrap", Runtimepkg) + noreturn_symlist[5] = Pkglookup("throwreturn", Runtimepkg) + noreturn_symlist[6] = Pkglookup("selectgo", Runtimepkg) + noreturn_symlist[7] = Pkglookup("block", Runtimepkg) + } + + if p.To.Node == nil { + return false + } + s := ((p.To.Node).(*Node)).Sym + if s == nil { + return false + } + for i := 0; noreturn_symlist[i] != nil; i++ { + if s == noreturn_symlist[i] { + return true + } + } + return false +} + +// JMP chasing and removal. +// +// The code generator depends on being able to write out jump +// instructions that it can jump to now but fill in later. +// the linker will resolve them nicely, but they make the code +// longer and more difficult to follow during debugging. +// Remove them. + +/* what instruction does a JMP to p eventually land on? */ +func chasejmp(p *obj.Prog, jmploop *int) *obj.Prog { + n := 0 + for p != nil && p.As == obj.AJMP && p.To.Type == obj.TYPE_BRANCH { + n++ + if n > 10 { + *jmploop = 1 + break + } + + p = p.To.Val.(*obj.Prog) + } + + return p +} + +/* + * reuse reg pointer for mark/sweep state. + * leave reg==nil at end because alive==nil. + */ +var alive interface{} = nil +var dead interface{} = 1 + +/* mark all code reachable from firstp as alive */ +func mark(firstp *obj.Prog) { + for p := firstp; p != nil; p = p.Link { + if p.Opt != dead { + break + } + p.Opt = alive + if p.As != obj.ACALL && p.To.Type == obj.TYPE_BRANCH && p.To.Val.(*obj.Prog) != nil { + mark(p.To.Val.(*obj.Prog)) + } + if p.As == obj.AJMP || p.As == obj.ARET || p.As == obj.AUNDEF { + break + } + } +} + +func fixjmp(firstp *obj.Prog) { + if Debug['R'] != 0 && Debug['v'] != 0 { + fmt.Printf("\nfixjmp\n") + } + + // pass 1: resolve jump to jump, mark all code as dead. + jmploop := 0 + + for p := firstp; p != nil; p = p.Link { + if Debug['R'] != 0 && Debug['v'] != 0 { + fmt.Printf("%v\n", p) + } + if p.As != obj.ACALL && p.To.Type == obj.TYPE_BRANCH && p.To.Val.(*obj.Prog) != nil && p.To.Val.(*obj.Prog).As == obj.AJMP { + p.To.Val = chasejmp(p.To.Val.(*obj.Prog), &jmploop) + if Debug['R'] != 0 && Debug['v'] != 0 { + fmt.Printf("->%v\n", p) + } + } + + p.Opt = dead + } + + if Debug['R'] != 0 && Debug['v'] != 0 { + fmt.Printf("\n") + } + + // pass 2: mark all reachable code alive + mark(firstp) + + // pass 3: delete dead code (mostly JMPs). + var last *obj.Prog + + for p := firstp; p != nil; p = p.Link { + if p.Opt == dead { + if p.Link == nil && p.As == obj.ARET && last != nil && last.As != obj.ARET { + // This is the final ARET, and the code so far doesn't have one. + // Let it stay. The register allocator assumes that all live code in + // the function can be traversed by starting at all the RET instructions + // and following predecessor links. If we remove the final RET, + // this assumption will not hold in the case of an infinite loop + // at the end of a function. + // Keep the RET but mark it dead for the liveness analysis. + p.Mode = 1 + } else { + if Debug['R'] != 0 && Debug['v'] != 0 { + fmt.Printf("del %v\n", p) + } + continue + } + } + + if last != nil { + last.Link = p + } + last = p + } + + last.Link = nil + + // pass 4: elide JMP to next instruction. + // only safe if there are no jumps to JMPs anymore. + if jmploop == 0 { + var last *obj.Prog + for p := firstp; p != nil; p = p.Link { + if p.As == obj.AJMP && p.To.Type == obj.TYPE_BRANCH && p.To.Val == p.Link { + if Debug['R'] != 0 && Debug['v'] != 0 { + fmt.Printf("del %v\n", p) + } + continue + } + + if last != nil { + last.Link = p + } + last = p + } + + last.Link = nil + } + + if Debug['R'] != 0 && Debug['v'] != 0 { + fmt.Printf("\n") + for p := firstp; p != nil; p = p.Link { + fmt.Printf("%v\n", p) + } + fmt.Printf("\n") + } +} + +// Control flow analysis. The Flow structures hold predecessor and successor +// information as well as basic loop analysis. +// +// graph = flowstart(firstp, 0); +// ... use flow graph ... +// flowend(graph); // free graph +// +// Typical uses of the flow graph are to iterate over all the flow-relevant instructions: +// +// for(f = graph->start; f != nil; f = f->link) +// +// or, given an instruction f, to iterate over all the predecessors, which is +// f->p1 and this list: +// +// for(f2 = f->p2; f2 != nil; f2 = f2->p2link) +// +// The size argument to flowstart specifies an amount of zeroed memory +// to allocate in every f->data field, for use by the client. +// If size == 0, f->data will be nil. + +var flowmark int + +// MaxFlowProg is the maximum size program (counted in instructions) +// for which the flow code will build a graph. Functions larger than this limit +// will not have flow graphs and consequently will not be optimized. +const MaxFlowProg = 50000 + +func Flowstart(firstp *obj.Prog, newData func() interface{}) *Graph { + // Count and mark instructions to annotate. + nf := 0 + + for p := firstp; p != nil; p = p.Link { + p.Opt = nil // should be already, but just in case + Thearch.Proginfo(p) + if p.Info.Flags&Skip != 0 { + continue + } + p.Opt = &flowmark + nf++ + } + + if nf == 0 { + return nil + } + + if nf >= MaxFlowProg { + if Debug['v'] != 0 { + Warn("%v is too big (%d instructions)", Curfn.Nname.Sym, nf) + } + return nil + } + + // Allocate annotations and assign to instructions. + graph := new(Graph) + ff := make([]Flow, nf) + start := &ff[0] + id := 0 + var last *Flow + for p := firstp; p != nil; p = p.Link { + if p.Opt == nil { + continue + } + f := &ff[0] + ff = ff[1:] + p.Opt = f + f.Prog = p + if last != nil { + last.Link = f + } + last = f + if newData != nil { + f.Data = newData() + } + f.Id = int32(id) + id++ + } + + // Fill in pred/succ information. + var f1 *Flow + var p *obj.Prog + for f := start; f != nil; f = f.Link { + p = f.Prog + if p.Info.Flags&Break == 0 { + f1 = f.Link + f.S1 = f1 + f1.P1 = f + } + + if p.To.Type == obj.TYPE_BRANCH { + if p.To.Val == nil { + Fatal("pnil %v", p) + } + f1 = p.To.Val.(*obj.Prog).Opt.(*Flow) + if f1 == nil { + Fatal("fnil %v / %v", p, p.To.Val.(*obj.Prog)) + } + if f1 == f { + //fatal("self loop %P", p); + continue + } + + f.S2 = f1 + f.P2link = f1.P2 + f1.P2 = f + } + } + + graph.Start = start + graph.Num = nf + return graph +} + +func Flowend(graph *Graph) { + for f := graph.Start; f != nil; f = f.Link { + f.Prog.Info.Flags = 0 // drop cached proginfo + f.Prog.Opt = nil + } +} + +/* + * find looping structure + * + * 1) find reverse postordering + * 2) find approximate dominators, + * the actual dominators if the flow graph is reducible + * otherwise, dominators plus some other non-dominators. + * See Matthew S. Hecht and Jeffrey D. Ullman, + * "Analysis of a Simple Algorithm for Global Data Flow Problems", + * Conf. Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts, + * Oct. 1-3, 1973, pp. 207-217. + * 3) find all nodes with a predecessor dominated by the current node. + * such a node is a loop head. + * recursively, all preds with a greater rpo number are in the loop + */ +func postorder(r *Flow, rpo2r []*Flow, n int32) int32 { + r.Rpo = 1 + r1 := r.S1 + if r1 != nil && r1.Rpo == 0 { + n = postorder(r1, rpo2r, n) + } + r1 = r.S2 + if r1 != nil && r1.Rpo == 0 { + n = postorder(r1, rpo2r, n) + } + rpo2r[n] = r + n++ + return n +} + +func rpolca(idom []int32, rpo1 int32, rpo2 int32) int32 { + if rpo1 == -1 { + return rpo2 + } + var t int32 + for rpo1 != rpo2 { + if rpo1 > rpo2 { + t = rpo2 + rpo2 = rpo1 + rpo1 = t + } + + for rpo1 < rpo2 { + t = idom[rpo2] + if t >= rpo2 { + Fatal("bad idom") + } + rpo2 = t + } + } + + return rpo1 +} + +func doms(idom []int32, r int32, s int32) bool { + for s > r { + s = idom[s] + } + return s == r +} + +func loophead(idom []int32, r *Flow) bool { + src := r.Rpo + if r.P1 != nil && doms(idom, src, r.P1.Rpo) { + return true + } + for r = r.P2; r != nil; r = r.P2link { + if doms(idom, src, r.Rpo) { + return true + } + } + return false +} + +func loopmark(rpo2r **Flow, head int32, r *Flow) { + if r.Rpo < head || r.Active == head { + return + } + r.Active = head + r.Loop += LOOP + if r.P1 != nil { + loopmark(rpo2r, head, r.P1) + } + for r = r.P2; r != nil; r = r.P2link { + loopmark(rpo2r, head, r) + } +} + +func flowrpo(g *Graph) { + g.Rpo = make([]*Flow, g.Num) + idom := make([]int32, g.Num) + + for r1 := g.Start; r1 != nil; r1 = r1.Link { + r1.Active = 0 + } + + rpo2r := g.Rpo + d := postorder(g.Start, rpo2r, 0) + nr := int32(g.Num) + if d > nr { + Fatal("too many reg nodes %d %d", d, nr) + } + nr = d + var r1 *Flow + for i := int32(0); i < nr/2; i++ { + r1 = rpo2r[i] + rpo2r[i] = rpo2r[nr-1-i] + rpo2r[nr-1-i] = r1 + } + + for i := int32(0); i < nr; i++ { + rpo2r[i].Rpo = i + } + + idom[0] = 0 + var me int32 + for i := int32(0); i < nr; i++ { + r1 = rpo2r[i] + me = r1.Rpo + d = -1 + + // rpo2r[r->rpo] == r protects against considering dead code, + // which has r->rpo == 0. + if r1.P1 != nil && rpo2r[r1.P1.Rpo] == r1.P1 && r1.P1.Rpo < me { + d = r1.P1.Rpo + } + for r1 = r1.P2; r1 != nil; r1 = r1.P2link { + if rpo2r[r1.Rpo] == r1 && r1.Rpo < me { + d = rpolca(idom, d, r1.Rpo) + } + } + idom[i] = d + } + + for i := int32(0); i < nr; i++ { + r1 = rpo2r[i] + r1.Loop++ + if r1.P2 != nil && loophead(idom, r1) { + loopmark(&rpo2r[0], i, r1) + } + } + + for r1 := g.Start; r1 != nil; r1 = r1.Link { + r1.Active = 0 + } +} + +func Uniqp(r *Flow) *Flow { + r1 := r.P1 + if r1 == nil { + r1 = r.P2 + if r1 == nil || r1.P2link != nil { + return nil + } + } else if r.P2 != nil { + return nil + } + return r1 +} + +func Uniqs(r *Flow) *Flow { + r1 := r.S1 + if r1 == nil { + r1 = r.S2 + if r1 == nil { + return nil + } + } else if r.S2 != nil { + return nil + } + return r1 +} + +// The compilers assume they can generate temporary variables +// as needed to preserve the right semantics or simplify code +// generation and the back end will still generate good code. +// This results in a large number of ephemeral temporary variables. +// Merge temps with non-overlapping lifetimes and equal types using the +// greedy algorithm in Poletto and Sarkar, "Linear Scan Register Allocation", +// ACM TOPLAS 1999. + +type TempVar struct { + node *Node + def *Flow // definition of temp var + use *Flow // use list, chained through Flow.data + merge *TempVar // merge var with this one + start int64 // smallest Prog.pc in live range + end int64 // largest Prog.pc in live range + addr uint8 // address taken - no accurate end + removed uint8 // removed from program +} + +type startcmp []*TempVar + +func (x startcmp) Len() int { + return len(x) +} + +func (x startcmp) Swap(i, j int) { + x[i], x[j] = x[j], x[i] +} + +func (x startcmp) Less(i, j int) bool { + a := x[i] + b := x[j] + + if a.start < b.start { + return true + } + if a.start > b.start { + return false + } + + // Order what's left by id or symbol name, + // just so that sort is forced into a specific ordering, + // so that the result of the sort does not depend on + // the sort implementation. + if a.def != b.def { + return int(a.def.Id-b.def.Id) < 0 + } + if a.node != b.node { + return stringsCompare(a.node.Sym.Name, b.node.Sym.Name) < 0 + } + return false +} + +// Is n available for merging? +func canmerge(n *Node) bool { + return n.Class == PAUTO && strings.HasPrefix(n.Sym.Name, "autotmp") +} + +func mergetemp(firstp *obj.Prog) { + const ( + debugmerge = 0 + ) + + g := Flowstart(firstp, nil) + if g == nil { + return + } + + // Build list of all mergeable variables. + nvar := 0 + for l := Curfn.Func.Dcl; l != nil; l = l.Next { + if canmerge(l.N) { + nvar++ + } + } + + var_ := make([]TempVar, nvar) + nvar = 0 + var n *Node + var v *TempVar + for l := Curfn.Func.Dcl; l != nil; l = l.Next { + n = l.N + if canmerge(n) { + v = &var_[nvar] + nvar++ + n.Opt = v + v.node = n + } + } + + // Build list of uses. + // We assume that the earliest reference to a temporary is its definition. + // This is not true of variables in general but our temporaries are all + // single-use (that's why we have so many!). + for f := g.Start; f != nil; f = f.Link { + p := f.Prog + if p.From.Node != nil && ((p.From.Node).(*Node)).Opt != nil && p.To.Node != nil && ((p.To.Node).(*Node)).Opt != nil { + Fatal("double node %v", p) + } + v = nil + n, _ = p.From.Node.(*Node) + if n != nil { + v, _ = n.Opt.(*TempVar) + } + if v == nil { + n, _ = p.To.Node.(*Node) + if n != nil { + v, _ = n.Opt.(*TempVar) + } + } + if v != nil { + if v.def == nil { + v.def = f + } + f.Data = v.use + v.use = f + if n == p.From.Node && (p.Info.Flags&LeftAddr != 0) { + v.addr = 1 + } + } + } + + if debugmerge > 1 && Debug['v'] != 0 { + Dumpit("before", g.Start, 0) + } + + nkill := 0 + + // Special case. + for i := 0; i < len(var_); i++ { + v = &var_[i] + if v.addr != 0 { + continue + } + + // Used in only one instruction, which had better be a write. + f := v.use + if f != nil && f.Data.(*Flow) == nil { + p := f.Prog + if p.To.Node == v.node && (p.Info.Flags&RightWrite != 0) && p.Info.Flags&RightRead == 0 { + p.As = obj.ANOP + p.To = obj.Addr{} + v.removed = 1 + if debugmerge > 0 && Debug['v'] != 0 { + fmt.Printf("drop write-only %v\n", v.node.Sym) + } + } else { + Fatal("temp used and not set: %v", p) + } + nkill++ + continue + } + + // Written in one instruction, read in the next, otherwise unused, + // no jumps to the next instruction. Happens mainly in 386 compiler. + f = v.use + if f != nil && f.Link == f.Data.(*Flow) && (f.Data.(*Flow)).Data.(*Flow) == nil && Uniqp(f.Link) == f { + p := f.Prog + p1 := f.Link.Prog + const ( + SizeAny = SizeB | SizeW | SizeL | SizeQ | SizeF | SizeD + ) + if p.From.Node == v.node && p1.To.Node == v.node && (p.Info.Flags&Move != 0) && (p.Info.Flags|p1.Info.Flags)&(LeftAddr|RightAddr) == 0 && p.Info.Flags&SizeAny == p1.Info.Flags&SizeAny { + p1.From = p.From + Thearch.Excise(f) + v.removed = 1 + if debugmerge > 0 && Debug['v'] != 0 { + fmt.Printf("drop immediate-use %v\n", v.node.Sym) + } + } + + nkill++ + continue + } + } + + // Traverse live range of each variable to set start, end. + // Each flood uses a new value of gen so that we don't have + // to clear all the r->active words after each variable. + gen := int32(0) + + for i := 0; i < len(var_); i++ { + v = &var_[i] + gen++ + for f := v.use; f != nil; f = f.Data.(*Flow) { + mergewalk(v, f, uint32(gen)) + } + if v.addr != 0 { + gen++ + for f := v.use; f != nil; f = f.Data.(*Flow) { + varkillwalk(v, f, uint32(gen)) + } + } + } + + // Sort variables by start. + bystart := make([]*TempVar, len(var_)) + + for i := 0; i < len(var_); i++ { + bystart[i] = &var_[i] + } + sort.Sort(startcmp(bystart[:len(var_)])) + + // List of in-use variables, sorted by end, so that the ones that + // will last the longest are the earliest ones in the array. + // The tail inuse[nfree:] holds no-longer-used variables. + // In theory we should use a sorted tree so that insertions are + // guaranteed O(log n) and then the loop is guaranteed O(n log n). + // In practice, it doesn't really matter. + inuse := make([]*TempVar, len(var_)) + + ninuse := 0 + nfree := len(var_) + var t *Type + var v1 *TempVar + var j int + for i := 0; i < len(var_); i++ { + v = bystart[i] + if debugmerge > 0 && Debug['v'] != 0 { + fmt.Printf("consider %v: removed=%d\n", Nconv(v.node, obj.FmtSharp), v.removed) + } + + if v.removed != 0 { + continue + } + + // Expire no longer in use. + for ninuse > 0 && inuse[ninuse-1].end < v.start { + ninuse-- + v1 = inuse[ninuse] + nfree-- + inuse[nfree] = v1 + } + + if debugmerge > 0 && Debug['v'] != 0 { + fmt.Printf("consider %v: removed=%d nfree=%d nvar=%d\n", Nconv(v.node, obj.FmtSharp), v.removed, nfree, len(var_)) + } + + // Find old temp to reuse if possible. + t = v.node.Type + + for j = nfree; j < len(var_); j++ { + v1 = inuse[j] + if debugmerge > 0 && Debug['v'] != 0 { + fmt.Printf("consider %v: maybe %v: type=%v,%v addrtaken=%v,%v\n", Nconv(v.node, obj.FmtSharp), Nconv(v1.node, obj.FmtSharp), t, v1.node.Type, v.node.Addrtaken, v1.node.Addrtaken) + } + + // Require the types to match but also require the addrtaken bits to match. + // If a variable's address is taken, that disables registerization for the individual + // words of the variable (for example, the base,len,cap of a slice). + // We don't want to merge a non-addressed var with an addressed one and + // inhibit registerization of the former. + if Eqtype(t, v1.node.Type) && v.node.Addrtaken == v1.node.Addrtaken { + inuse[j] = inuse[nfree] + nfree++ + if v1.merge != nil { + v.merge = v1.merge + } else { + v.merge = v1 + } + nkill++ + break + } + } + + // Sort v into inuse. + j = ninuse + ninuse++ + + for j > 0 && inuse[j-1].end < v.end { + inuse[j] = inuse[j-1] + j-- + } + + inuse[j] = v + } + + if debugmerge > 0 && Debug['v'] != 0 { + fmt.Printf("%v [%d - %d]\n", Curfn.Nname.Sym, len(var_), nkill) + var v *TempVar + for i := 0; i < len(var_); i++ { + v = &var_[i] + fmt.Printf("var %v %v %d-%d", Nconv(v.node, obj.FmtSharp), v.node.Type, v.start, v.end) + if v.addr != 0 { + fmt.Printf(" addr=1") + } + if v.removed != 0 { + fmt.Printf(" dead=1") + } + if v.merge != nil { + fmt.Printf(" merge %v", Nconv(v.merge.node, obj.FmtSharp)) + } + if v.start == v.end && v.def != nil { + fmt.Printf(" %v", v.def.Prog) + } + fmt.Printf("\n") + } + + if debugmerge > 1 && Debug['v'] != 0 { + Dumpit("after", g.Start, 0) + } + } + + // Update node references to use merged temporaries. + for f := g.Start; f != nil; f = f.Link { + p := f.Prog + n, _ = p.From.Node.(*Node) + if n != nil { + v, _ = n.Opt.(*TempVar) + if v != nil && v.merge != nil { + p.From.Node = v.merge.node + } + } + n, _ = p.To.Node.(*Node) + if n != nil { + v, _ = n.Opt.(*TempVar) + if v != nil && v.merge != nil { + p.To.Node = v.merge.node + } + } + } + + // Delete merged nodes from declaration list. + var l *NodeList + for lp := &Curfn.Func.Dcl; ; { + l = *lp + if l == nil { + break + } + + Curfn.Func.Dcl.End = l + n = l.N + v, _ = n.Opt.(*TempVar) + if v != nil && (v.merge != nil || v.removed != 0) { + *lp = l.Next + continue + } + + lp = &l.Next + } + + // Clear aux structures. + for i := 0; i < len(var_); i++ { + var_[i].node.Opt = nil + } + + Flowend(g) +} + +func mergewalk(v *TempVar, f0 *Flow, gen uint32) { + var p *obj.Prog + var f1 *Flow + + for f1 = f0; f1 != nil; f1 = f1.P1 { + if uint32(f1.Active) == gen { + break + } + f1.Active = int32(gen) + p = f1.Prog + if v.end < p.Pc { + v.end = p.Pc + } + if f1 == v.def { + v.start = p.Pc + break + } + } + + var f2 *Flow + for f := f0; f != f1; f = f.P1 { + for f2 = f.P2; f2 != nil; f2 = f2.P2link { + mergewalk(v, f2, gen) + } + } +} + +func varkillwalk(v *TempVar, f0 *Flow, gen uint32) { + var p *obj.Prog + var f1 *Flow + + for f1 = f0; f1 != nil; f1 = f1.S1 { + if uint32(f1.Active) == gen { + break + } + f1.Active = int32(gen) + p = f1.Prog + if v.end < p.Pc { + v.end = p.Pc + } + if v.start > p.Pc { + v.start = p.Pc + } + if p.As == obj.ARET || (p.As == obj.AVARKILL && p.To.Node == v.node) { + break + } + } + + for f := f0; f != f1; f = f.S1 { + varkillwalk(v, f.S2, gen) + } +} + +// Eliminate redundant nil pointer checks. +// +// The code generation pass emits a CHECKNIL for every possibly nil pointer. +// This pass removes a CHECKNIL if every predecessor path has already +// checked this value for nil. +// +// Simple backwards flood from check to definition. +// Run prog loop backward from end of program to beginning to avoid quadratic +// behavior removing a run of checks. +// +// Assume that stack variables with address not taken can be loaded multiple times +// from memory without being rechecked. Other variables need to be checked on +// each load. + +var killed int // f->data is either nil or &killed + +func nilopt(firstp *obj.Prog) { + g := Flowstart(firstp, nil) + if g == nil { + return + } + + if Debug_checknil > 1 { /* || strcmp(curfn->nname->sym->name, "f1") == 0 */ + Dumpit("nilopt", g.Start, 0) + } + + ncheck := 0 + nkill := 0 + var p *obj.Prog + for f := g.Start; f != nil; f = f.Link { + p = f.Prog + if p.As != obj.ACHECKNIL || !Thearch.Regtyp(&p.From) { + continue + } + ncheck++ + if Thearch.Stackaddr(&p.From) { + if Debug_checknil != 0 && p.Lineno > 1 { + Warnl(int(p.Lineno), "removed nil check of SP address") + } + f.Data = &killed + continue + } + + nilwalkfwd(f) + if f.Data != nil { + if Debug_checknil != 0 && p.Lineno > 1 { + Warnl(int(p.Lineno), "removed nil check before indirect") + } + continue + } + + nilwalkback(f) + if f.Data != nil { + if Debug_checknil != 0 && p.Lineno > 1 { + Warnl(int(p.Lineno), "removed repeated nil check") + } + continue + } + } + + for f := g.Start; f != nil; f = f.Link { + if f.Data != nil { + nkill++ + Thearch.Excise(f) + } + } + + Flowend(g) + + if Debug_checknil > 1 { + fmt.Printf("%v: removed %d of %d nil checks\n", Curfn.Nname.Sym, nkill, ncheck) + } +} + +func nilwalkback(fcheck *Flow) { + for f := fcheck; f != nil; f = Uniqp(f) { + p := f.Prog + if (p.Info.Flags&RightWrite != 0) && Thearch.Sameaddr(&p.To, &fcheck.Prog.From) { + // Found initialization of value we're checking for nil. + // without first finding the check, so this one is unchecked. + return + } + + if f != fcheck && p.As == obj.ACHECKNIL && Thearch.Sameaddr(&p.From, &fcheck.Prog.From) { + fcheck.Data = &killed + return + } + } +} + +// Here is a more complex version that scans backward across branches. +// It assumes fcheck->kill = 1 has been set on entry, and its job is to find a reason +// to keep the check (setting fcheck->kill = 0). +// It doesn't handle copying of aggregates as well as I would like, +// nor variables with their address taken, +// and it's too subtle to turn on this late in Go 1.2. Perhaps for Go 1.3. +/* +for(f1 = f0; f1 != nil; f1 = f1->p1) { + if(f1->active == gen) + break; + f1->active = gen; + p = f1->prog; + + // If same check, stop this loop but still check + // alternate predecessors up to this point. + if(f1 != fcheck && p->as == ACHECKNIL && thearch.sameaddr(&p->from, &fcheck->prog->from)) + break; + + if((p.Info.flags & RightWrite) && thearch.sameaddr(&p->to, &fcheck->prog->from)) { + // Found initialization of value we're checking for nil. + // without first finding the check, so this one is unchecked. + fcheck->kill = 0; + return; + } + + if(f1->p1 == nil && f1->p2 == nil) { + print("lost pred for %P\n", fcheck->prog); + for(f1=f0; f1!=nil; f1=f1->p1) { + thearch.proginfo(&info, f1->prog); + print("\t%P %d %d %D %D\n", r1->prog, info.flags&RightWrite, thearch.sameaddr(&f1->prog->to, &fcheck->prog->from), &f1->prog->to, &fcheck->prog->from); + } + fatal("lost pred trail"); + } +} + +for(f = f0; f != f1; f = f->p1) + for(f2 = f->p2; f2 != nil; f2 = f2->p2link) + nilwalkback(fcheck, f2, gen); +*/ + +func nilwalkfwd(fcheck *Flow) { + // If the path down from rcheck dereferences the address + // (possibly with a small offset) before writing to memory + // and before any subsequent checks, it's okay to wait for + // that implicit check. Only consider this basic block to + // avoid problems like: + // _ = *x // should panic + // for {} // no writes but infinite loop may be considered visible + + var last *Flow + for f := Uniqs(fcheck); f != nil; f = Uniqs(f) { + p := f.Prog + if (p.Info.Flags&LeftRead != 0) && Thearch.Smallindir(&p.From, &fcheck.Prog.From) { + fcheck.Data = &killed + return + } + + if (p.Info.Flags&(RightRead|RightWrite) != 0) && Thearch.Smallindir(&p.To, &fcheck.Prog.From) { + fcheck.Data = &killed + return + } + + // Stop if another nil check happens. + if p.As == obj.ACHECKNIL { + return + } + + // Stop if value is lost. + if (p.Info.Flags&RightWrite != 0) && Thearch.Sameaddr(&p.To, &fcheck.Prog.From) { + return + } + + // Stop if memory write. + if (p.Info.Flags&RightWrite != 0) && !Thearch.Regtyp(&p.To) { + return + } + + // Stop if we jump backward. + if last != nil && f.Id <= last.Id { + return + } + last = f + } +} diff --git a/src/cmd/compile/internal/gc/racewalk.go b/src/cmd/compile/internal/gc/racewalk.go new file mode 100644 index 0000000000..446ec038c8 --- /dev/null +++ b/src/cmd/compile/internal/gc/racewalk.go @@ -0,0 +1,639 @@ +// Copyright 2012 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 gc + +import ( + "fmt" + "strings" +) + +// The racewalk pass modifies the code tree for the function as follows: +// +// 1. It inserts a call to racefuncenter at the beginning of each function. +// 2. It inserts a call to racefuncexit at the end of each function. +// 3. It inserts a call to raceread before each memory read. +// 4. It inserts a call to racewrite before each memory write. +// +// The rewriting is not yet complete. Certain nodes are not rewritten +// but should be. + +// TODO(dvyukov): do not instrument initialization as writes: +// a := make([]int, 10) + +// Do not instrument the following packages at all, +// at best instrumentation would cause infinite recursion. +var omit_pkgs = []string{"runtime", "runtime/race"} + +// Only insert racefuncenter/racefuncexit into the following packages. +// Memory accesses in the packages are either uninteresting or will cause false positives. +var noinst_pkgs = []string{"sync", "sync/atomic"} + +func ispkgin(pkgs []string) bool { + if myimportpath != "" { + for i := 0; i < len(pkgs); i++ { + if myimportpath == pkgs[i] { + return true + } + } + } + + return false +} + +func isforkfunc(fn *Node) bool { + // Special case for syscall.forkAndExecInChild. + // In the child, this function must not acquire any locks, because + // they might have been locked at the time of the fork. This means + // no rescheduling, no malloc calls, and no new stack segments. + // Race instrumentation does all of the above. + return myimportpath != "" && myimportpath == "syscall" && fn.Nname.Sym.Name == "forkAndExecInChild" +} + +func racewalk(fn *Node) { + if ispkgin(omit_pkgs) || isforkfunc(fn) { + return + } + + if !ispkgin(noinst_pkgs) { + racewalklist(fn.Nbody, nil) + + // nothing interesting for race detector in fn->enter + racewalklist(fn.Func.Exit, nil) + } + + // nodpc is the PC of the caller as extracted by + // getcallerpc. We use -widthptr(FP) for x86. + // BUG: this will not work on arm. + nodpc := Nod(OXXX, nil, nil) + + *nodpc = *nodfp + nodpc.Type = Types[TUINTPTR] + nodpc.Xoffset = int64(-Widthptr) + nd := mkcall("racefuncenter", nil, nil, nodpc) + fn.Func.Enter = concat(list1(nd), fn.Func.Enter) + nd = mkcall("racefuncexit", nil, nil) + fn.Func.Exit = list(fn.Func.Exit, nd) + + if Debug['W'] != 0 { + s := fmt.Sprintf("after racewalk %v", fn.Nname.Sym) + dumplist(s, fn.Nbody) + s = fmt.Sprintf("enter %v", fn.Nname.Sym) + dumplist(s, fn.Func.Enter) + s = fmt.Sprintf("exit %v", fn.Nname.Sym) + dumplist(s, fn.Func.Exit) + } +} + +func racewalklist(l *NodeList, init **NodeList) { + var instr *NodeList + + for ; l != nil; l = l.Next { + instr = nil + racewalknode(&l.N, &instr, 0, 0) + if init == nil { + l.N.Ninit = concat(l.N.Ninit, instr) + } else { + *init = concat(*init, instr) + } + } +} + +// walkexpr and walkstmt combined +// walks the tree and adds calls to the +// instrumentation code to top-level (statement) nodes' init +func racewalknode(np **Node, init **NodeList, wr int, skip int) { + n := *np + + if n == nil { + return + } + + if Debug['w'] > 1 { + Dump("racewalk-before", n) + } + setlineno(n) + if init == nil { + Fatal("racewalk: bad init list") + } + if init == &n.Ninit { + // If init == &n->ninit and n->ninit is non-nil, + // racewalknode might append it to itself. + // nil it out and handle it separately before putting it back. + l := n.Ninit + + n.Ninit = nil + racewalklist(l, nil) + racewalknode(&n, &l, wr, skip) // recurse with nil n->ninit + appendinit(&n, l) + *np = n + return + } + + racewalklist(n.Ninit, nil) + + switch n.Op { + default: + Fatal("racewalk: unknown node type %v", Oconv(int(n.Op), 0)) + + case OAS, OASWB, OAS2FUNC: + racewalknode(&n.Left, init, 1, 0) + racewalknode(&n.Right, init, 0, 0) + goto ret + + // can't matter + case OCFUNC, OVARKILL: + goto ret + + case OBLOCK: + if n.List == nil { + goto ret + } + + switch n.List.N.Op { + // Blocks are used for multiple return function calls. + // x, y := f() becomes BLOCK{CALL f, AS x [SP+0], AS y [SP+n]} + // We don't want to instrument between the statements because it will + // smash the results. + case OCALLFUNC, OCALLMETH, OCALLINTER: + racewalknode(&n.List.N, &n.List.N.Ninit, 0, 0) + + var fini *NodeList + racewalklist(n.List.Next, &fini) + n.List = concat(n.List, fini) + + // Ordinary block, for loop initialization or inlined bodies. + default: + racewalklist(n.List, nil) + } + + goto ret + + case ODEFER: + racewalknode(&n.Left, init, 0, 0) + goto ret + + case OPROC: + racewalknode(&n.Left, init, 0, 0) + goto ret + + case OCALLINTER: + racewalknode(&n.Left, init, 0, 0) + goto ret + + // Instrument dst argument of runtime.writebarrier* calls + // 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 + + case ONOT, + OMINUS, + OPLUS, + OREAL, + OIMAG, + OCOM, + OSQRT: + racewalknode(&n.Left, init, wr, 0) + goto ret + + case ODOTINTER: + racewalknode(&n.Left, init, 0, 0) + goto ret + + case ODOT: + racewalknode(&n.Left, init, 0, 1) + callinstr(&n, init, wr, skip) + goto ret + + case ODOTPTR: // dst = (*x).f with implicit *; otherwise it's ODOT+OIND + racewalknode(&n.Left, init, 0, 0) + + callinstr(&n, init, wr, skip) + goto ret + + case OIND: // *p + racewalknode(&n.Left, init, 0, 0) + + callinstr(&n, init, wr, skip) + goto ret + + case OSPTR, OLEN, OCAP: + racewalknode(&n.Left, init, 0, 0) + if Istype(n.Left.Type, TMAP) { + n1 := Nod(OCONVNOP, n.Left, nil) + n1.Type = Ptrto(Types[TUINT8]) + n1 = Nod(OIND, n1, nil) + typecheck(&n1, Erv) + callinstr(&n1, init, 0, skip) + } + + goto ret + + case OLSH, + ORSH, + OLROT, + OAND, + OANDNOT, + OOR, + OXOR, + OSUB, + OMUL, + OHMUL, + OEQ, + ONE, + OLT, + OLE, + OGE, + OGT, + OADD, + OCOMPLEX: + racewalknode(&n.Left, init, wr, 0) + racewalknode(&n.Right, init, wr, 0) + goto ret + + case OANDAND, OOROR: + racewalknode(&n.Left, init, wr, 0) + + // walk has ensured the node has moved to a location where + // side effects are safe. + // n->right may not be executed, + // so instrumentation goes to n->right->ninit, not init. + racewalknode(&n.Right, &n.Right.Ninit, wr, 0) + + goto ret + + case ONAME: + callinstr(&n, init, wr, skip) + goto ret + + case OCONV: + racewalknode(&n.Left, init, wr, 0) + goto ret + + case OCONVNOP: + racewalknode(&n.Left, init, wr, 0) + goto ret + + case ODIV, OMOD: + racewalknode(&n.Left, init, wr, 0) + racewalknode(&n.Right, init, wr, 0) + goto ret + + case OINDEX: + if !Isfixedarray(n.Left.Type) { + racewalknode(&n.Left, init, 0, 0) + } else if !islvalue(n.Left) { + // index of unaddressable array, like Map[k][i]. + racewalknode(&n.Left, init, wr, 0) + + racewalknode(&n.Right, init, 0, 0) + goto ret + } + + racewalknode(&n.Right, init, 0, 0) + if n.Left.Type.Etype != TSTRING { + callinstr(&n, init, wr, skip) + } + goto ret + + case OSLICE, OSLICEARR, OSLICE3, OSLICE3ARR: + racewalknode(&n.Left, init, 0, 0) + goto ret + + case OADDR: + racewalknode(&n.Left, init, 0, 1) + goto ret + + // n->left is Type* which is not interesting. + case OEFACE: + racewalknode(&n.Right, init, 0, 0) + + goto ret + + case OITAB: + racewalknode(&n.Left, init, 0, 0) + goto ret + + // should not appear in AST by now + case OSEND, + ORECV, + OCLOSE, + ONEW, + OXCASE, + OXFALL, + OCASE, + OPANIC, + ORECOVER, + OCONVIFACE, + OCMPIFACE, + OMAKECHAN, + OMAKEMAP, + OMAKESLICE, + OCALL, + OCOPY, + OAPPEND, + ORUNESTR, + OARRAYBYTESTR, + OARRAYRUNESTR, + OSTRARRAYBYTE, + OSTRARRAYRUNE, + OINDEXMAP, + // lowered to call + OCMPSTR, + OADDSTR, + ODOTTYPE, + ODOTTYPE2, + OAS2DOTTYPE, + OCALLPART, + // lowered to PTRLIT + OCLOSURE, // lowered to PTRLIT + ORANGE, // lowered to ordinary for loop + OARRAYLIT, // lowered to assignments + OMAPLIT, + OSTRUCTLIT, + OAS2, + OAS2RECV, + OAS2MAPR, + OASOP: + Yyerror("racewalk: %v must be lowered by now", Oconv(int(n.Op), 0)) + + goto ret + + // impossible nodes: only appear in backend. + case ORROTC, OEXTEND: + Yyerror("racewalk: %v cannot exist now", Oconv(int(n.Op), 0)) + goto ret + + case OGETG: + Yyerror("racewalk: OGETG can happen only in runtime which we don't instrument") + goto ret + + // just do generic traversal + case OFOR, + OIF, + OCALLMETH, + ORETURN, + ORETJMP, + OSWITCH, + OSELECT, + OEMPTY, + OBREAK, + OCONTINUE, + OFALL, + OGOTO, + OLABEL: + goto ret + + // does not require instrumentation + case OPRINT, // don't bother instrumenting it + OPRINTN, // don't bother instrumenting it + OCHECKNIL, // always followed by a read. + OPARAM, // it appears only in fn->exit to copy heap params back + OCLOSUREVAR, // immutable pointer to captured variable + ODOTMETH, // either part of CALLMETH or CALLPART (lowered to PTRLIT) + OINDREG, // at this stage, only n(SP) nodes from nodarg + ODCL, // declarations (without value) cannot be races + ODCLCONST, + ODCLTYPE, + OTYPE, + ONONAME, + OLITERAL, + OSLICESTR, // always preceded by bounds checking, avoid double instrumentation. + OTYPESW: // ignored by code generation, do not instrument. + goto ret + } + +ret: + if n.Op != OBLOCK { // OBLOCK is handled above in a special way. + racewalklist(n.List, init) + } + if n.Ntest != nil { + racewalknode(&n.Ntest, &n.Ntest.Ninit, 0, 0) + } + if n.Nincr != nil { + racewalknode(&n.Nincr, &n.Nincr.Ninit, 0, 0) + } + racewalklist(n.Nbody, nil) + racewalklist(n.Nelse, nil) + racewalklist(n.Rlist, nil) + *np = n +} + +func isartificial(n *Node) bool { + // compiler-emitted artificial things that we do not want to instrument, + // cant' possibly participate in a data race. + if n.Op == ONAME && n.Sym != nil && n.Sym.Name != "" { + if n.Sym.Name == "_" { + return true + } + + // autotmp's are always local + if strings.HasPrefix(n.Sym.Name, "autotmp_") { + return true + } + + // statictmp's are read-only + if strings.HasPrefix(n.Sym.Name, "statictmp_") { + return true + } + + // go.itab is accessed only by the compiler and runtime (assume safe) + if n.Sym.Pkg != nil && n.Sym.Pkg.Name != "" && n.Sym.Pkg.Name == "go.itab" { + return true + } + } + + return false +} + +func callinstr(np **Node, init **NodeList, wr int, skip int) bool { + n := *np + + //print("callinstr for %+N [ %O ] etype=%E class=%d\n", + // n, n->op, n->type ? n->type->etype : -1, n->class); + + if skip != 0 || n.Type == nil || n.Type.Etype >= TIDEAL { + return false + } + t := n.Type + if isartificial(n) { + return false + } + + b := outervalue(n) + + // it skips e.g. stores to ... parameter array + if isartificial(b) { + return false + } + class := b.Class + + // BUG: we _may_ want to instrument PAUTO sometimes + // e.g. if we've got a local variable/method receiver + // that has got a pointer inside. Whether it points to + // the heap or not is impossible to know at compile time + if (class&PHEAP != 0) || class == PPARAMREF || class == PEXTERN || b.Op == OINDEX || b.Op == ODOTPTR || b.Op == OIND { + hascalls := 0 + foreach(n, hascallspred, &hascalls) + if hascalls != 0 { + n = detachexpr(n, init) + *np = n + } + + n = treecopy(n) + makeaddable(n) + var f *Node + if t.Etype == TSTRUCT || Isfixedarray(t) { + name := "racereadrange" + if wr != 0 { + name = "racewriterange" + } + f = mkcall(name, nil, init, uintptraddr(n), Nodintconst(t.Width)) + } else { + name := "raceread" + if wr != 0 { + name = "racewrite" + } + f = mkcall(name, nil, init, uintptraddr(n)) + } + + *init = list(*init, f) + return true + } + + return false +} + +// makeaddable returns a node whose memory location is the +// same as n, but which is addressable in the Go language +// sense. +// This is different from functions like cheapexpr that may make +// a copy of their argument. +func makeaddable(n *Node) { + // The arguments to uintptraddr technically have an address but + // may not be addressable in the Go sense: for example, in the case + // of T(v).Field where T is a struct type and v is + // an addressable value. + switch n.Op { + case OINDEX: + if Isfixedarray(n.Left.Type) { + makeaddable(n.Left) + } + + // Turn T(v).Field into v.Field + case ODOT, OXDOT: + if n.Left.Op == OCONVNOP { + n.Left = n.Left.Left + } + makeaddable(n.Left) + + // nothing to do + case ODOTPTR: + fallthrough + default: + break + } +} + +func uintptraddr(n *Node) *Node { + r := Nod(OADDR, n, nil) + r.Bounded = true + r = conv(r, Types[TUNSAFEPTR]) + r = conv(r, Types[TUINTPTR]) + return r +} + +func detachexpr(n *Node, init **NodeList) *Node { + addr := Nod(OADDR, n, nil) + l := temp(Ptrto(n.Type)) + as := Nod(OAS, l, addr) + typecheck(&as, Etop) + walkexpr(&as, init) + *init = list(*init, as) + ind := Nod(OIND, l, nil) + typecheck(&ind, Erv) + walkexpr(&ind, init) + return ind +} + +func foreachnode(n *Node, f func(*Node, interface{}), c interface{}) { + if n != nil { + f(n, c) + } +} + +func foreachlist(l *NodeList, f func(*Node, interface{}), c interface{}) { + for ; l != nil; l = l.Next { + foreachnode(l.N, f, c) + } +} + +func foreach(n *Node, f func(*Node, interface{}), c interface{}) { + foreachlist(n.Ninit, f, c) + foreachnode(n.Left, f, c) + foreachnode(n.Right, f, c) + foreachlist(n.List, f, c) + foreachnode(n.Ntest, f, c) + foreachnode(n.Nincr, f, c) + foreachlist(n.Nbody, f, c) + foreachlist(n.Nelse, f, c) + foreachlist(n.Rlist, f, c) +} + +func hascallspred(n *Node, c interface{}) { + switch n.Op { + case OCALL, OCALLFUNC, OCALLMETH, OCALLINTER: + (*c.(*int))++ + } +} + +// appendinit is like addinit in subr.go +// but appends rather than prepends. +func appendinit(np **Node, init *NodeList) { + if init == nil { + return + } + + n := *np + switch n.Op { + // There may be multiple refs to this node; + // introduce OCONVNOP to hold init list. + case ONAME, OLITERAL: + n = Nod(OCONVNOP, n, nil) + + n.Type = n.Left.Type + n.Typecheck = 1 + *np = n + } + + n.Ninit = concat(n.Ninit, init) + n.Ullman = UINF +} diff --git a/src/cmd/compile/internal/gc/range.go b/src/cmd/compile/internal/gc/range.go new file mode 100644 index 0000000000..03beb1c673 --- /dev/null +++ b/src/cmd/compile/internal/gc/range.go @@ -0,0 +1,406 @@ +// Copyright 2009 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 gc + +import "cmd/internal/obj" + +/* + * range + */ +func typecheckrange(n *Node) { + var toomany int + var why string + var t1 *Type + var t2 *Type + var v1 *Node + var v2 *Node + + // Typechecking order is important here: + // 0. first typecheck range expression (slice/map/chan), + // it is evaluated only once and so logically it is not part of the loop. + // 1. typcheck produced values, + // this part can declare new vars and so it must be typechecked before body, + // because body can contain a closure that captures the vars. + // 2. decldepth++ to denote loop body. + // 3. typecheck body. + // 4. decldepth--. + + typecheck(&n.Right, Erv) + + t := n.Right.Type + if t == nil { + goto out + } + + // delicate little dance. see typecheckas2 + for ll := n.List; ll != nil; ll = ll.Next { + if ll.N.Defn != n { + typecheck(&ll.N, Erv|Easgn) + } + } + + if Isptr[t.Etype] && Isfixedarray(t.Type) { + t = t.Type + } + n.Type = t + + toomany = 0 + switch t.Etype { + default: + Yyerror("cannot range over %v", Nconv(n.Right, obj.FmtLong)) + goto out + + case TARRAY: + t1 = Types[TINT] + t2 = t.Type + + case TMAP: + t1 = t.Down + t2 = t.Type + + case TCHAN: + if t.Chan&Crecv == 0 { + Yyerror("invalid operation: range %v (receive from send-only type %v)", n.Right, n.Right.Type) + goto out + } + + t1 = t.Type + t2 = nil + if count(n.List) == 2 { + toomany = 1 + } + + case TSTRING: + t1 = Types[TINT] + t2 = runetype + } + + if count(n.List) > 2 || toomany != 0 { + Yyerror("too many variables in range") + } + + v1 = nil + if n.List != nil { + v1 = n.List.N + } + v2 = nil + if n.List != nil && n.List.Next != nil { + v2 = n.List.Next.N + } + + // this is not only a optimization but also a requirement in the spec. + // "if the second iteration variable is the blank identifier, the range + // clause is equivalent to the same clause with only the first variable + // present." + if isblank(v2) { + if v1 != nil { + n.List = list1(v1) + } + v2 = nil + } + + if v1 != nil { + if v1.Defn == n { + v1.Type = t1 + } else if v1.Type != nil && assignop(t1, v1.Type, &why) == 0 { + Yyerror("cannot assign type %v to %v in range%s", t1, Nconv(v1, obj.FmtLong), why) + } + checkassign(n, v1) + } + + if v2 != nil { + if v2.Defn == n { + v2.Type = t2 + } else if v2.Type != nil && assignop(t2, v2.Type, &why) == 0 { + Yyerror("cannot assign type %v to %v in range%s", t2, Nconv(v2, obj.FmtLong), why) + } + checkassign(n, v2) + } + + // second half of dance +out: + n.Typecheck = 1 + + for ll := n.List; ll != nil; ll = ll.Next { + if ll.N.Typecheck == 0 { + typecheck(&ll.N, Erv|Easgn) + } + } + + decldepth++ + typechecklist(n.Nbody, Etop) + decldepth-- +} + +func walkrange(n *Node) { + // variable name conventions: + // ohv1, hv1, hv2: hidden (old) val 1, 2 + // ha, hit: hidden aggregate, iterator + // hn, hp: hidden len, pointer + // hb: hidden bool + // a, v1, v2: not hidden aggregate, val 1, 2 + + t := n.Type + + a := n.Right + lno := int(setlineno(a)) + + var v1 *Node + if n.List != nil { + v1 = n.List.N + } + var v2 *Node + if n.List != nil && n.List.Next != nil && !isblank(n.List.Next.N) { + v2 = n.List.Next.N + } + + // n->list has no meaning anymore, clear it + // to avoid erroneous processing by racewalk. + n.List = nil + + var body *NodeList + var init *NodeList + switch t.Etype { + default: + Fatal("walkrange") + + // Lower n into runtime·memclr if possible, for + // fast zeroing of slices and arrays (issue 5373). + // Look for instances of + // + // for i := range a { + // a[i] = zero + // } + // + // in which the evaluation of a is side-effect-free. + case TARRAY: + if Debug['N'] == 0 { + if flag_race == 0 { + if v1 != nil { + if v2 == nil { + if n.Nbody != nil { + if n.Nbody.N != nil { // at least one statement in body + if n.Nbody.Next == nil { // at most one statement in body + tmp := n.Nbody.N // first statement of body + if tmp.Op == OAS { + if tmp.Left.Op == OINDEX { + if samesafeexpr(tmp.Left.Left, a) { + if samesafeexpr(tmp.Left.Right, v1) { + if t.Type.Width > 0 { + if iszero(tmp.Right) { + // Convert to + // if len(a) != 0 { + // hp = &a[0] + // hn = len(a)*sizeof(elem(a)) + // memclr(hp, hn) + // i = len(a) - 1 + // } + n.Op = OIF + + n.Nbody = nil + n.Ntest = Nod(ONE, Nod(OLEN, a, nil), Nodintconst(0)) + n.Nincr = nil + + // hp = &a[0] + hp := temp(Ptrto(Types[TUINT8])) + + tmp := Nod(OINDEX, a, Nodintconst(0)) + tmp.Bounded = true + tmp = Nod(OADDR, tmp, nil) + tmp = Nod(OCONVNOP, tmp, nil) + tmp.Type = Ptrto(Types[TUINT8]) + n.Nbody = list(n.Nbody, Nod(OAS, hp, tmp)) + + // hn = len(a) * sizeof(elem(a)) + hn := temp(Types[TUINTPTR]) + + tmp = Nod(OLEN, a, nil) + tmp = Nod(OMUL, tmp, Nodintconst(t.Type.Width)) + tmp = conv(tmp, Types[TUINTPTR]) + n.Nbody = list(n.Nbody, Nod(OAS, hn, tmp)) + + // memclr(hp, hn) + fn := mkcall("memclr", nil, nil, hp, hn) + + n.Nbody = list(n.Nbody, fn) + + // i = len(a) - 1 + v1 = Nod(OAS, v1, Nod(OSUB, Nod(OLEN, a, nil), Nodintconst(1))) + + n.Nbody = list(n.Nbody, v1) + + typecheck(&n.Ntest, Erv) + typechecklist(n.Nbody, Etop) + walkstmt(&n) + lineno = int32(lno) + return + } + } + } + } + } + } + } + } + } + } + } + } + } + + // orderstmt arranged for a copy of the array/slice variable if needed. + ha := a + + hv1 := temp(Types[TINT]) + hn := temp(Types[TINT]) + var hp *Node + + init = list(init, Nod(OAS, hv1, nil)) + init = list(init, Nod(OAS, hn, Nod(OLEN, ha, nil))) + if v2 != nil { + hp = temp(Ptrto(n.Type.Type)) + tmp := Nod(OINDEX, ha, Nodintconst(0)) + tmp.Bounded = true + init = list(init, Nod(OAS, hp, Nod(OADDR, tmp, nil))) + } + + n.Ntest = Nod(OLT, hv1, hn) + n.Nincr = Nod(OAS, hv1, Nod(OADD, hv1, Nodintconst(1))) + if v1 == nil { + body = nil + } else if v2 == nil { + body = list1(Nod(OAS, v1, hv1)) + } else { + a := Nod(OAS2, nil, nil) + a.List = list(list1(v1), v2) + a.Rlist = list(list1(hv1), Nod(OIND, hp, nil)) + body = list1(a) + + // Advance pointer as part of increment. + // We used to advance the pointer before executing the loop body, + // but doing so would make the pointer point past the end of the + // array during the final iteration, possibly causing another unrelated + // piece of memory not to be garbage collected until the loop finished. + // Advancing during the increment ensures that the pointer p only points + // pass the end of the array during the final "p++; i++; if(i >= len(x)) break;", + // after which p is dead, so it cannot confuse the collector. + tmp := Nod(OADD, hp, Nodintconst(t.Type.Width)) + + tmp.Type = hp.Type + tmp.Typecheck = 1 + tmp.Right.Type = Types[Tptr] + tmp.Right.Typecheck = 1 + a = Nod(OAS, hp, tmp) + typecheck(&a, Etop) + n.Nincr.Ninit = list1(a) + } + + // orderstmt allocated the iterator for us. + // we only use a once, so no copy needed. + case TMAP: + ha := a + + th := hiter(t) + hit := n.Alloc + hit.Type = th + n.Left = nil + keyname := newname(th.Type.Sym) // depends on layout of iterator struct. See reflect.go:hiter + valname := newname(th.Type.Down.Sym) // ditto + + fn := syslook("mapiterinit", 1) + + substArgTypes(fn, t.Down, t.Type, th) + init = list(init, mkcall1(fn, nil, nil, typename(t), ha, Nod(OADDR, hit, nil))) + n.Ntest = Nod(ONE, Nod(ODOT, hit, keyname), nodnil()) + + fn = syslook("mapiternext", 1) + substArgTypes(fn, th) + n.Nincr = mkcall1(fn, nil, nil, Nod(OADDR, hit, nil)) + + key := Nod(ODOT, hit, keyname) + key = Nod(OIND, key, nil) + if v1 == nil { + body = nil + } else if v2 == nil { + body = list1(Nod(OAS, v1, key)) + } else { + val := Nod(ODOT, hit, valname) + val = Nod(OIND, val, nil) + a := Nod(OAS2, nil, nil) + a.List = list(list1(v1), v2) + a.Rlist = list(list1(key), val) + body = list1(a) + } + + // orderstmt arranged for a copy of the channel variable. + case TCHAN: + ha := a + + n.Ntest = nil + + hv1 := temp(t.Type) + hv1.Typecheck = 1 + if haspointers(t.Type) { + init = list(init, Nod(OAS, hv1, nil)) + } + hb := temp(Types[TBOOL]) + + n.Ntest = Nod(ONE, hb, Nodbool(false)) + a := Nod(OAS2RECV, nil, nil) + a.Typecheck = 1 + a.List = list(list1(hv1), hb) + a.Rlist = list1(Nod(ORECV, ha, nil)) + n.Ntest.Ninit = list1(a) + if v1 == nil { + body = nil + } else { + body = list1(Nod(OAS, v1, hv1)) + } + + // orderstmt arranged for a copy of the string variable. + case TSTRING: + ha := a + + ohv1 := temp(Types[TINT]) + + hv1 := temp(Types[TINT]) + init = list(init, Nod(OAS, hv1, nil)) + + var a *Node + var hv2 *Node + if v2 == nil { + a = Nod(OAS, hv1, mkcall("stringiter", Types[TINT], nil, ha, hv1)) + } else { + hv2 = temp(runetype) + a = Nod(OAS2, nil, nil) + a.List = list(list1(hv1), hv2) + fn := syslook("stringiter2", 0) + a.Rlist = list1(mkcall1(fn, getoutargx(fn.Type), nil, ha, hv1)) + } + + n.Ntest = Nod(ONE, hv1, Nodintconst(0)) + n.Ntest.Ninit = list(list1(Nod(OAS, ohv1, hv1)), a) + + body = nil + if v1 != nil { + body = list1(Nod(OAS, v1, ohv1)) + } + if v2 != nil { + body = list(body, Nod(OAS, v2, hv2)) + } + } + + n.Op = OFOR + typechecklist(init, Etop) + n.Ninit = concat(n.Ninit, init) + typechecklist(n.Ntest.Ninit, Etop) + typecheck(&n.Ntest, Erv) + typecheck(&n.Nincr, Etop) + typechecklist(body, Etop) + n.Nbody = concat(body, n.Nbody) + walkstmt(&n) + + lineno = int32(lno) +} diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go new file mode 100644 index 0000000000..6c0962f258 --- /dev/null +++ b/src/cmd/compile/internal/gc/reflect.go @@ -0,0 +1,1572 @@ +// Copyright 2009 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 gc + +import ( + "cmd/internal/gcprog" + "cmd/internal/obj" + "fmt" + "os" +) + +/* + * runtime interface and reflection data structures + */ +var signatlist *NodeList + +func sigcmp(a *Sig, b *Sig) int { + i := stringsCompare(a.name, b.name) + if i != 0 { + return i + } + if a.pkg == b.pkg { + return 0 + } + if a.pkg == nil { + return -1 + } + if b.pkg == nil { + return +1 + } + return stringsCompare(a.pkg.Path, b.pkg.Path) +} + +func lsort(l *Sig, f func(*Sig, *Sig) int) *Sig { + if l == nil || l.link == nil { + return l + } + + l1 := l + l2 := l + for { + l2 = l2.link + if l2 == nil { + break + } + l2 = l2.link + if l2 == nil { + break + } + l1 = l1.link + } + + l2 = l1.link + l1.link = nil + l1 = lsort(l, f) + l2 = lsort(l2, f) + + /* set up lead element */ + if f(l1, l2) < 0 { + l = l1 + l1 = l1.link + } else { + l = l2 + l2 = l2.link + } + + le := l + + for { + if l1 == nil { + for l2 != nil { + le.link = l2 + le = l2 + l2 = l2.link + } + + le.link = nil + break + } + + if l2 == nil { + for l1 != nil { + le.link = l1 + le = l1 + l1 = l1.link + } + + break + } + + if f(l1, l2) < 0 { + le.link = l1 + le = l1 + l1 = l1.link + } else { + le.link = l2 + le = l2 + l2 = l2.link + } + } + + le.link = nil + return l +} + +// Builds a type respresenting a Bucket structure for +// the given map type. This type is not visible to users - +// we include only enough information to generate a correct GC +// program for it. +// Make sure this stays in sync with ../../runtime/hashmap.go! +const ( + BUCKETSIZE = 8 + MAXKEYSIZE = 128 + MAXVALSIZE = 128 +) + +func makefield(name string, t *Type) *Type { + f := typ(TFIELD) + f.Type = t + f.Sym = new(Sym) + f.Sym.Name = name + return f +} + +func mapbucket(t *Type) *Type { + if t.Bucket != nil { + return t.Bucket + } + + bucket := typ(TSTRUCT) + keytype := t.Down + valtype := t.Type + dowidth(keytype) + dowidth(valtype) + if keytype.Width > MAXKEYSIZE { + keytype = Ptrto(keytype) + } + if valtype.Width > MAXVALSIZE { + valtype = Ptrto(valtype) + } + + // The first field is: uint8 topbits[BUCKETSIZE]. + arr := typ(TARRAY) + + arr.Type = Types[TUINT8] + arr.Bound = BUCKETSIZE + var field [4]*Type + field[0] = makefield("topbits", arr) + arr = typ(TARRAY) + arr.Type = keytype + arr.Bound = BUCKETSIZE + field[1] = makefield("keys", arr) + arr = typ(TARRAY) + arr.Type = valtype + arr.Bound = BUCKETSIZE + field[2] = makefield("values", arr) + field[3] = makefield("overflow", Ptrto(bucket)) + + // link up fields + bucket.Noalg = 1 + + bucket.Local = t.Local + bucket.Type = field[0] + for n := int32(0); n < int32(len(field)-1); n++ { + field[n].Down = field[n+1] + } + field[len(field)-1].Down = nil + dowidth(bucket) + + // Pad to the native integer alignment. + // This is usually the same as widthptr; the exception (as usual) is amd64p32. + if Widthreg > Widthptr { + bucket.Width += int64(Widthreg) - int64(Widthptr) + } + + // See comment on hmap.overflow in ../../runtime/hashmap.go. + if !haspointers(t.Type) && !haspointers(t.Down) && t.Type.Width <= MAXKEYSIZE && t.Down.Width <= MAXVALSIZE { + bucket.Haspointers = 1 // no pointers + } + + t.Bucket = bucket + + bucket.Map = t + return bucket +} + +// Builds a type representing a Hmap structure for the given map type. +// Make sure this stays in sync with ../../runtime/hashmap.go! +func hmap(t *Type) *Type { + if t.Hmap != nil { + return t.Hmap + } + + bucket := mapbucket(t) + var field [8]*Type + field[0] = makefield("count", Types[TINT]) + field[1] = makefield("flags", Types[TUINT8]) + field[2] = makefield("B", Types[TUINT8]) + field[3] = makefield("hash0", Types[TUINT32]) + field[4] = makefield("buckets", Ptrto(bucket)) + field[5] = makefield("oldbuckets", Ptrto(bucket)) + field[6] = makefield("nevacuate", Types[TUINTPTR]) + field[7] = makefield("overflow", Types[TUNSAFEPTR]) + + h := typ(TSTRUCT) + h.Noalg = 1 + h.Local = t.Local + h.Type = field[0] + for n := int32(0); n < int32(len(field)-1); n++ { + field[n].Down = field[n+1] + } + field[len(field)-1].Down = nil + dowidth(h) + t.Hmap = h + h.Map = t + return h +} + +func hiter(t *Type) *Type { + if t.Hiter != nil { + return t.Hiter + } + + // build a struct: + // hash_iter { + // key *Key + // val *Value + // t *MapType + // h *Hmap + // buckets *Bucket + // bptr *Bucket + // overflow0 unsafe.Pointer + // overflow1 unsafe.Pointer + // startBucket uintptr + // stuff uintptr + // bucket uintptr + // checkBucket uintptr + // } + // must match ../../runtime/hashmap.go:hash_iter. + var field [12]*Type + field[0] = makefield("key", Ptrto(t.Down)) + + field[1] = makefield("val", Ptrto(t.Type)) + field[2] = makefield("t", Ptrto(Types[TUINT8])) + field[3] = makefield("h", Ptrto(hmap(t))) + field[4] = makefield("buckets", Ptrto(mapbucket(t))) + field[5] = makefield("bptr", Ptrto(mapbucket(t))) + field[6] = makefield("overflow0", Types[TUNSAFEPTR]) + field[7] = makefield("overflow1", Types[TUNSAFEPTR]) + field[8] = makefield("startBucket", Types[TUINTPTR]) + field[9] = makefield("stuff", Types[TUINTPTR]) // offset+wrapped+B+I + field[10] = makefield("bucket", Types[TUINTPTR]) + field[11] = makefield("checkBucket", Types[TUINTPTR]) + + // build iterator struct holding the above fields + i := typ(TSTRUCT) + + i.Noalg = 1 + i.Type = field[0] + for n := int32(0); n < int32(len(field)-1); n++ { + field[n].Down = field[n+1] + } + field[len(field)-1].Down = nil + dowidth(i) + if i.Width != int64(12*Widthptr) { + Yyerror("hash_iter size not correct %d %d", i.Width, 12*Widthptr) + } + t.Hiter = i + i.Map = t + return i +} + +/* + * f is method type, with receiver. + * return function type, receiver as first argument (or not). + */ +func methodfunc(f *Type, receiver *Type) *Type { + var in *NodeList + if receiver != nil { + d := Nod(ODCLFIELD, nil, nil) + d.Type = receiver + in = list(in, d) + } + + var d *Node + for t := getinargx(f).Type; t != nil; t = t.Down { + d = Nod(ODCLFIELD, nil, nil) + d.Type = t.Type + d.Isddd = t.Isddd + in = list(in, d) + } + + var out *NodeList + for t := getoutargx(f).Type; t != nil; t = t.Down { + d = Nod(ODCLFIELD, nil, nil) + d.Type = t.Type + out = list(out, d) + } + + t := functype(nil, in, out) + if f.Nname != nil { + // Link to name of original method function. + t.Nname = f.Nname + } + + return t +} + +/* + * return methods of non-interface type t, sorted by name. + * generates stub functions as needed. + */ +func methods(t *Type) *Sig { + // method type + mt := methtype(t, 0) + + if mt == nil { + return nil + } + expandmeth(mt) + + // type stored in interface word + it := t + + if !isdirectiface(it) { + it = Ptrto(t) + } + + // make list of methods for t, + // generating code if necessary. + var a *Sig + + var this *Type + var b *Sig + var method *Sym + for f := mt.Xmethod; f != nil; f = f.Down { + if f.Etype != TFIELD { + Fatal("methods: not field %v", f) + } + if f.Type.Etype != TFUNC || f.Type.Thistuple == 0 { + Fatal("non-method on %v method %v %v\n", mt, f.Sym, f) + } + if getthisx(f.Type).Type == nil { + Fatal("receiver with no type on %v method %v %v\n", mt, f.Sym, f) + } + if f.Nointerface { + continue + } + + method = f.Sym + if method == nil { + continue + } + + // get receiver type for this particular method. + // if pointer receiver but non-pointer t and + // this is not an embedded pointer inside a struct, + // method does not apply. + this = getthisx(f.Type).Type.Type + + if Isptr[this.Etype] && this.Type == t { + continue + } + if Isptr[this.Etype] && !Isptr[t.Etype] && f.Embedded != 2 && !isifacemethod(f.Type) { + continue + } + + b = new(Sig) + b.link = a + a = b + + a.name = method.Name + if !exportname(method.Name) { + if method.Pkg == nil { + Fatal("methods: missing package") + } + a.pkg = method.Pkg + } + + a.isym = methodsym(method, it, 1) + a.tsym = methodsym(method, t, 0) + a.type_ = methodfunc(f.Type, t) + a.mtype = methodfunc(f.Type, nil) + + if a.isym.Flags&SymSiggen == 0 { + a.isym.Flags |= SymSiggen + if !Eqtype(this, it) || this.Width < Types[Tptr].Width { + compiling_wrappers = 1 + genwrapper(it, f, a.isym, 1) + compiling_wrappers = 0 + } + } + + if a.tsym.Flags&SymSiggen == 0 { + a.tsym.Flags |= SymSiggen + if !Eqtype(this, t) { + compiling_wrappers = 1 + genwrapper(t, f, a.tsym, 0) + compiling_wrappers = 0 + } + } + } + + return lsort(a, sigcmp) +} + +/* + * return methods of interface type t, sorted by name. + */ +func imethods(t *Type) *Sig { + var a *Sig + var method *Sym + var isym *Sym + + var all *Sig + var last *Sig + for f := t.Type; f != nil; f = f.Down { + if f.Etype != TFIELD { + Fatal("imethods: not field") + } + if f.Type.Etype != TFUNC || f.Sym == nil { + continue + } + method = f.Sym + a = new(Sig) + a.name = method.Name + if !exportname(method.Name) { + if method.Pkg == nil { + Fatal("imethods: missing package") + } + a.pkg = method.Pkg + } + + a.mtype = f.Type + a.offset = 0 + a.type_ = methodfunc(f.Type, nil) + + if last != nil && sigcmp(last, a) >= 0 { + Fatal("sigcmp vs sortinter %s %s", last.name, a.name) + } + if last == nil { + all = a + } else { + last.link = a + } + last = a + + // Compiler can only refer to wrappers for non-blank methods. + if isblanksym(method) { + continue + } + + // NOTE(rsc): Perhaps an oversight that + // IfaceType.Method is not in the reflect data. + // Generate the method body, so that compiled + // code can refer to it. + isym = methodsym(method, t, 0) + + if isym.Flags&SymSiggen == 0 { + isym.Flags |= SymSiggen + genwrapper(t, f, isym, 0) + } + } + + return all +} + +var dimportpath_gopkg *Pkg + +func dimportpath(p *Pkg) { + if p.Pathsym != nil { + return + } + + // If we are compiling the runtime package, there are two runtime packages around + // -- localpkg and Runtimepkg. We don't want to produce import path symbols for + // both of them, so just produce one for localpkg. + if myimportpath == "runtime" && p == Runtimepkg { + return + } + + if dimportpath_gopkg == nil { + dimportpath_gopkg = mkpkg("go") + dimportpath_gopkg.Name = "go" + } + + nam := "importpath." + p.Prefix + "." + + n := Nod(ONAME, nil, nil) + n.Sym = Pkglookup(nam, dimportpath_gopkg) + + n.Class = PEXTERN + n.Xoffset = 0 + p.Pathsym = n.Sym + + if p == localpkg { + // Note: myimportpath != "", or else dgopkgpath won't call dimportpath. + gdatastring(n, myimportpath) + } else { + gdatastring(n, p.Path) + } + ggloblsym(n.Sym, int32(Types[TSTRING].Width), obj.DUPOK|obj.RODATA) +} + +func dgopkgpath(s *Sym, ot int, pkg *Pkg) int { + if pkg == nil { + return dgostringptr(s, ot, "") + } + + if pkg == localpkg && myimportpath == "" { + // If we don't know the full path of the package being compiled (i.e. -p + // was not passed on the compiler command line), emit reference to + // go.importpath.""., which 6l will rewrite using the correct import path. + // Every package that imports this one directly defines the symbol. + var ns *Sym + + if ns == nil { + ns = Pkglookup("importpath.\"\".", mkpkg("go")) + } + return dsymptr(s, ot, ns, 0) + } + + dimportpath(pkg) + return dsymptr(s, ot, pkg.Pathsym, 0) +} + +/* + * uncommonType + * ../../runtime/type.go:/uncommonType + */ +func dextratype(sym *Sym, off int, t *Type, ptroff int) int { + m := methods(t) + if t.Sym == nil && m == nil { + return off + } + + // fill in *extraType pointer in header + off = int(Rnd(int64(off), int64(Widthptr))) + + dsymptr(sym, ptroff, sym, off) + + n := 0 + for a := m; a != nil; a = a.link { + dtypesym(a.type_) + n++ + } + + ot := off + s := sym + if t.Sym != nil { + ot = dgostringptr(s, ot, t.Sym.Name) + if t != Types[t.Etype] && t != errortype { + ot = dgopkgpath(s, ot, t.Sym.Pkg) + } else { + ot = dgostringptr(s, ot, "") + } + } else { + ot = dgostringptr(s, ot, "") + ot = dgostringptr(s, ot, "") + } + + // slice header + ot = dsymptr(s, ot, s, ot+Widthptr+2*Widthint) + + ot = duintxx(s, ot, uint64(n), Widthint) + ot = duintxx(s, ot, uint64(n), Widthint) + + // methods + for a := m; a != nil; a = a.link { + // method + // ../../runtime/type.go:/method + ot = dgostringptr(s, ot, a.name) + + ot = dgopkgpath(s, ot, a.pkg) + ot = dsymptr(s, ot, dtypesym(a.mtype), 0) + ot = dsymptr(s, ot, dtypesym(a.type_), 0) + if a.isym != nil { + ot = dsymptr(s, ot, a.isym, 0) + } else { + ot = duintptr(s, ot, 0) + } + if a.tsym != nil { + ot = dsymptr(s, ot, a.tsym, 0) + } else { + ot = duintptr(s, ot, 0) + } + } + + return ot +} + +var kinds = []int{ + TINT: obj.KindInt, + TUINT: obj.KindUint, + TINT8: obj.KindInt8, + TUINT8: obj.KindUint8, + TINT16: obj.KindInt16, + TUINT16: obj.KindUint16, + TINT32: obj.KindInt32, + TUINT32: obj.KindUint32, + TINT64: obj.KindInt64, + TUINT64: obj.KindUint64, + TUINTPTR: obj.KindUintptr, + TFLOAT32: obj.KindFloat32, + TFLOAT64: obj.KindFloat64, + TBOOL: obj.KindBool, + TSTRING: obj.KindString, + TPTR32: obj.KindPtr, + TPTR64: obj.KindPtr, + TSTRUCT: obj.KindStruct, + TINTER: obj.KindInterface, + TCHAN: obj.KindChan, + TMAP: obj.KindMap, + TARRAY: obj.KindArray, + TFUNC: obj.KindFunc, + TCOMPLEX64: obj.KindComplex64, + TCOMPLEX128: obj.KindComplex128, + TUNSAFEPTR: obj.KindUnsafePointer, +} + +func haspointers(t *Type) bool { + if t.Haspointers != 0 { + return t.Haspointers-1 != 0 + } + + var ret bool + switch t.Etype { + case TINT, + TUINT, + TINT8, + TUINT8, + TINT16, + TUINT16, + TINT32, + TUINT32, + TINT64, + TUINT64, + TUINTPTR, + TFLOAT32, + TFLOAT64, + TCOMPLEX64, + TCOMPLEX128, + TBOOL: + ret = false + + case TARRAY: + if t.Bound < 0 { // slice + ret = true + break + } + + if t.Bound == 0 { // empty array + ret = false + break + } + + ret = haspointers(t.Type) + + case TSTRUCT: + ret = false + for t1 := t.Type; t1 != nil; t1 = t1.Down { + if haspointers(t1.Type) { + ret = true + break + } + } + + case TSTRING, + TPTR32, + TPTR64, + TUNSAFEPTR, + TINTER, + TCHAN, + TMAP, + TFUNC: + fallthrough + default: + ret = true + + case TFIELD: + Fatal("haspointers: unexpected type, %v", t) + } + + t.Haspointers = 1 + uint8(obj.Bool2int(ret)) + return ret +} + +// 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) int64 { + if !haspointers(t) { + return 0 + } + + switch t.Etype { + case TPTR32, + TPTR64, + TUNSAFEPTR, + TFUNC, + TCHAN, + TMAP: + return int64(Widthptr) + + case TSTRING: + // struct { byte *str; intgo len; } + return int64(Widthptr) + + case TINTER: + // struct { Itab *tab; void *data; } or + // struct { Type *type; void *data; } + return 2 * int64(Widthptr) + + case TARRAY: + if Isslice(t) { + // struct { byte *array; uintgo len; uintgo cap; } + return int64(Widthptr) + } + // haspointers already eliminated t.Bound == 0. + return (t.Bound-1)*t.Type.Width + typeptrdata(t.Type) + + case TSTRUCT: + // Find the last field that has pointers. + var lastPtrField *Type + for t1 := t.Type; t1 != nil; t1 = t1.Down { + if haspointers(t1.Type) { + lastPtrField = t1 + } + } + return lastPtrField.Width + typeptrdata(lastPtrField.Type) + + default: + Fatal("typeptrdata: unexpected type, %v", t) + return 0 + } +} + +/* + * commonType + * ../../runtime/type.go:/commonType + */ + +var dcommontype_algarray *Sym + +func dcommontype(s *Sym, ot int, t *Type) int { + if ot != 0 { + Fatal("dcommontype %d", ot) + } + + sizeofAlg := 2 * Widthptr + if dcommontype_algarray == nil { + dcommontype_algarray = Pkglookup("algarray", Runtimepkg) + } + dowidth(t) + alg := algtype(t) + var algsym *Sym + if alg < 0 || alg == AMEM { + algsym = dalgsym(t) + } + + var sptr *Sym + if t.Sym != nil && !Isptr[t.Etype] { + sptr = dtypesym(Ptrto(t)) + } else { + sptr = weaktypesym(Ptrto(t)) + } + + // All (non-reflect-allocated) Types share the same zero object. + // Each place in the compiler where a pointer to the zero object + // might be returned by a runtime call (map access return value, + // 2-arg type cast) declares the size of the zerovalue it needs. + // 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 + // a read-only bss, but I don't think such a thing exists. + + // ../../pkg/reflect/type.go:/^type.commonType + // actual type structure + // type commonType struct { + // size uintptr + // ptrsize uintptr + // hash uint32 + // _ uint8 + // align uint8 + // fieldAlign uint8 + // kind uint8 + // alg 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(ptrdata)) + + ot = duint32(s, ot, typehash(t)) + ot = duint8(s, ot, 0) // unused + + // runtime (and common sense) expects alignment to be a power of two. + i := int(t.Align) + + if i == 0 { + i = 1 + } + if i&(i-1) != 0 { + Fatal("invalid alignment %d for %v", t.Align, t) + } + ot = duint8(s, ot, t.Align) // align + ot = duint8(s, ot, t.Align) // fieldAlign + + i = kinds[t.Etype] + if t.Etype == TARRAY && t.Bound < 0 { + i = obj.KindSlice + } + if !haspointers(t) { + i |= obj.KindNoPointers + } + if isdirectiface(t) { + i |= obj.KindDirectIface + } + if useGCProg { + i |= obj.KindGCProg + } + ot = duint8(s, ot, uint8(i)) // kind + if algsym == nil { + ot = dsymptr(s, ot, dcommontype_algarray, alg*sizeofAlg) + } else { + ot = dsymptr(s, ot, algsym, 0) + } + ot = dsymptr(s, ot, gcsym, 0) + + p := Tconv(t, obj.FmtLeft|obj.FmtUnsigned) + + //print("dcommontype: %s\n", p); + ot = dgostringptr(s, ot, p) // string + + // skip pointer to extraType, + // which follows the rest of this type structure. + // caller will fill in if needed. + // otherwise linker will assume 0. + ot += Widthptr + + ot = dsymptr(s, ot, sptr, 0) // ptrto type + ot = dsymptr(s, ot, zero, 0) // ptr to zero value + return ot +} + +func typesym(t *Type) *Sym { + return Pkglookup(Tconv(t, obj.FmtLeft), typepkg) +} + +func tracksym(t *Type) *Sym { + return Pkglookup(Tconv(t.Outer, obj.FmtLeft)+"."+t.Sym.Name, trackpkg) +} + +func typelinksym(t *Type) *Sym { + // %-uT is what the generated Type's string field says. + // It uses (ambiguous) package names instead of import paths. + // %-T is the complete, unambiguous type name. + // We want the types to end up sorted by string field, + // so use that first in the name, and then add :%-T to + // disambiguate. We use a tab character as the separator to + // ensure the types appear sorted by their string field. The + // names are a little long but they are discarded by the linker + // and do not end up in the symbol table of the final binary. + p := Tconv(t, obj.FmtLeft|obj.FmtUnsigned) + "\t" + Tconv(t, obj.FmtLeft) + + s := Pkglookup(p, typelinkpkg) + + //print("typelinksym: %s -> %+S\n", p, s); + + return s +} + +func typesymprefix(prefix string, t *Type) *Sym { + p := prefix + "." + Tconv(t, obj.FmtLeft) + s := Pkglookup(p, typepkg) + + //print("algsym: %s -> %+S\n", p, s); + + return s +} + +func typenamesym(t *Type) *Sym { + if t == nil || (Isptr[t.Etype] && t.Type == nil) || isideal(t) { + Fatal("typename %v", t) + } + s := typesym(t) + if s.Def == nil { + n := Nod(ONAME, nil, nil) + n.Sym = s + n.Type = Types[TUINT8] + n.Addable = true + n.Ullman = 1 + n.Class = PEXTERN + n.Xoffset = 0 + n.Typecheck = 1 + s.Def = n + + signatlist = list(signatlist, typenod(t)) + } + + return s.Def.Sym +} + +func typename(t *Type) *Node { + s := typenamesym(t) + n := Nod(OADDR, s.Def, nil) + n.Type = Ptrto(s.Def.Type) + n.Addable = true + n.Ullman = 2 + n.Typecheck = 1 + return n +} + +func weaktypesym(t *Type) *Sym { + p := Tconv(t, obj.FmtLeft) + s := Pkglookup(p, weaktypepkg) + + //print("weaktypesym: %s -> %+S\n", p, s); + + return s +} + +/* + * Returns 1 if t has a reflexive equality operator. + * That is, if x==x for all x of type t. + */ +func isreflexive(t *Type) bool { + switch t.Etype { + case TBOOL, + TINT, + TUINT, + TINT8, + TUINT8, + TINT16, + TUINT16, + TINT32, + TUINT32, + TINT64, + TUINT64, + TUINTPTR, + TPTR32, + TPTR64, + TUNSAFEPTR, + TSTRING, + TCHAN: + return true + + case TFLOAT32, + TFLOAT64, + TCOMPLEX64, + TCOMPLEX128, + TINTER: + return false + + case TARRAY: + if Isslice(t) { + Fatal("slice can't be a map key: %v", t) + } + return isreflexive(t.Type) + + case TSTRUCT: + for t1 := t.Type; t1 != nil; t1 = t1.Down { + if !isreflexive(t1.Type) { + return false + } + } + + return true + + default: + Fatal("bad type for map key: %v", t) + return false + } +} + +func dtypesym(t *Type) *Sym { + // Replace byte, rune aliases with real type. + // They've been separate internally to make error messages + // better, but we have to merge them in the reflect tables. + if t == bytetype || t == runetype { + t = Types[t.Etype] + } + + if isideal(t) { + Fatal("dtypesym %v", t) + } + + s := typesym(t) + if s.Flags&SymSiggen != 0 { + return s + } + s.Flags |= SymSiggen + + // special case (look for runtime below): + // when compiling package runtime, + // emit the type structures for int, float, etc. + tbase := t + + if Isptr[t.Etype] && t.Sym == nil && t.Type.Sym != nil { + tbase = t.Type + } + dupok := 0 + if tbase.Sym == nil { + dupok = obj.DUPOK + } + + if compiling_runtime != 0 && (tbase == Types[tbase.Etype] || tbase == bytetype || tbase == runetype || tbase == errortype) { // int, float, etc + goto ok + } + + // named types from other files are defined only by those files + if tbase.Sym != nil && !tbase.Local { + return s + } + if isforw[tbase.Etype] { + return s + } + +ok: + ot := 0 + xt := 0 + switch t.Etype { + default: + ot = dcommontype(s, ot, t) + xt = ot - 3*Widthptr + + case TARRAY: + if t.Bound >= 0 { + // ../../runtime/type.go:/ArrayType + s1 := dtypesym(t.Type) + + t2 := typ(TARRAY) + t2.Type = t.Type + t2.Bound = -1 // slice + s2 := dtypesym(t2) + ot = dcommontype(s, ot, t) + xt = ot - 3*Widthptr + ot = dsymptr(s, ot, s1, 0) + ot = dsymptr(s, ot, s2, 0) + ot = duintptr(s, ot, uint64(t.Bound)) + } else { + // ../../runtime/type.go:/SliceType + s1 := dtypesym(t.Type) + + ot = dcommontype(s, ot, t) + xt = ot - 3*Widthptr + ot = dsymptr(s, ot, s1, 0) + } + + // ../../runtime/type.go:/ChanType + case TCHAN: + s1 := dtypesym(t.Type) + + ot = dcommontype(s, ot, t) + xt = ot - 3*Widthptr + ot = dsymptr(s, ot, s1, 0) + ot = duintptr(s, ot, uint64(t.Chan)) + + case TFUNC: + for t1 := getthisx(t).Type; t1 != nil; t1 = t1.Down { + dtypesym(t1.Type) + } + isddd := false + for t1 := getinargx(t).Type; t1 != nil; t1 = t1.Down { + isddd = t1.Isddd + dtypesym(t1.Type) + } + + for t1 := getoutargx(t).Type; t1 != nil; t1 = t1.Down { + dtypesym(t1.Type) + } + + ot = dcommontype(s, ot, t) + xt = ot - 3*Widthptr + ot = duint8(s, ot, uint8(obj.Bool2int(isddd))) + + // two slice headers: in and out. + ot = int(Rnd(int64(ot), int64(Widthptr))) + + ot = dsymptr(s, ot, s, ot+2*(Widthptr+2*Widthint)) + n := t.Thistuple + t.Intuple + ot = duintxx(s, ot, uint64(n), Widthint) + ot = duintxx(s, ot, uint64(n), Widthint) + ot = dsymptr(s, ot, s, ot+1*(Widthptr+2*Widthint)+n*Widthptr) + ot = duintxx(s, ot, uint64(t.Outtuple), Widthint) + ot = duintxx(s, ot, uint64(t.Outtuple), Widthint) + + // slice data + for t1 := getthisx(t).Type; t1 != nil; t1 = t1.Down { + ot = dsymptr(s, ot, dtypesym(t1.Type), 0) + n++ + } + for t1 := getinargx(t).Type; t1 != nil; t1 = t1.Down { + ot = dsymptr(s, ot, dtypesym(t1.Type), 0) + n++ + } + for t1 := getoutargx(t).Type; t1 != nil; t1 = t1.Down { + ot = dsymptr(s, ot, dtypesym(t1.Type), 0) + n++ + } + + case TINTER: + m := imethods(t) + n := 0 + for a := m; a != nil; a = a.link { + dtypesym(a.type_) + n++ + } + + // ../../runtime/type.go:/InterfaceType + ot = dcommontype(s, ot, t) + + xt = ot - 3*Widthptr + ot = dsymptr(s, ot, s, ot+Widthptr+2*Widthint) + ot = duintxx(s, ot, uint64(n), Widthint) + ot = duintxx(s, ot, uint64(n), Widthint) + for a := m; a != nil; a = a.link { + // ../../runtime/type.go:/imethod + ot = dgostringptr(s, ot, a.name) + + ot = dgopkgpath(s, ot, a.pkg) + ot = dsymptr(s, ot, dtypesym(a.type_), 0) + } + + // ../../runtime/type.go:/MapType + case TMAP: + s1 := dtypesym(t.Down) + + s2 := dtypesym(t.Type) + s3 := dtypesym(mapbucket(t)) + s4 := dtypesym(hmap(t)) + ot = dcommontype(s, ot, t) + xt = ot - 3*Widthptr + ot = dsymptr(s, ot, s1, 0) + ot = dsymptr(s, ot, s2, 0) + ot = dsymptr(s, ot, s3, 0) + ot = dsymptr(s, ot, s4, 0) + if t.Down.Width > MAXKEYSIZE { + ot = duint8(s, ot, uint8(Widthptr)) + ot = duint8(s, ot, 1) // indirect + } else { + ot = duint8(s, ot, uint8(t.Down.Width)) + ot = duint8(s, ot, 0) // not indirect + } + + if t.Type.Width > MAXVALSIZE { + ot = duint8(s, ot, uint8(Widthptr)) + ot = duint8(s, ot, 1) // indirect + } else { + ot = duint8(s, ot, uint8(t.Type.Width)) + ot = duint8(s, ot, 0) // not indirect + } + + ot = duint16(s, ot, uint16(mapbucket(t).Width)) + ot = duint8(s, ot, uint8(obj.Bool2int(isreflexive(t.Down)))) + + case TPTR32, TPTR64: + if t.Type.Etype == TANY { + // ../../runtime/type.go:/UnsafePointerType + ot = dcommontype(s, ot, t) + + break + } + + // ../../runtime/type.go:/PtrType + s1 := dtypesym(t.Type) + + ot = dcommontype(s, ot, t) + xt = ot - 3*Widthptr + ot = dsymptr(s, ot, s1, 0) + + // ../../runtime/type.go:/StructType + // for security, only the exported fields. + case TSTRUCT: + n := 0 + + for t1 := t.Type; t1 != nil; t1 = t1.Down { + dtypesym(t1.Type) + n++ + } + + ot = dcommontype(s, ot, t) + xt = ot - 3*Widthptr + ot = dsymptr(s, ot, s, ot+Widthptr+2*Widthint) + ot = duintxx(s, ot, uint64(n), Widthint) + ot = duintxx(s, ot, uint64(n), Widthint) + for t1 := t.Type; t1 != nil; t1 = t1.Down { + // ../../runtime/type.go:/structField + if t1.Sym != nil && t1.Embedded == 0 { + ot = dgostringptr(s, ot, t1.Sym.Name) + if exportname(t1.Sym.Name) { + ot = dgostringptr(s, ot, "") + } else { + ot = dgopkgpath(s, ot, t1.Sym.Pkg) + } + } else { + ot = dgostringptr(s, ot, "") + if t1.Type.Sym != nil && t1.Type.Sym.Pkg == builtinpkg { + ot = dgopkgpath(s, ot, localpkg) + } else { + ot = dgostringptr(s, ot, "") + } + } + + ot = dsymptr(s, ot, dtypesym(t1.Type), 0) + ot = dgostrlitptr(s, ot, t1.Note) + ot = duintptr(s, ot, uint64(t1.Width)) // field offset + } + } + + ot = dextratype(s, ot, t, xt) + ggloblsym(s, int32(ot), int16(dupok|obj.RODATA)) + + // generate typelink.foo pointing at s = type.foo. + // The linker will leave a table of all the typelinks for + // types in the binary, so reflect can find them. + // We only need the link for unnamed composites that + // we want be able to find. + if t.Sym == nil { + switch t.Etype { + case TPTR32, TPTR64: + // The ptrto field of the type data cannot be relied on when + // dynamic linking: a type T may be defined in a module that makes + // no use of pointers to that type, but another module can contain + // a package that imports the first one and does use *T pointers. + // The second module will end up defining type data for *T and a + // type.*T symbol pointing at it. It's important that calling + // .PtrTo() on the refect.Type for T returns this type data and + // not some synthesized object, so we need reflect to be able to + // find it! + if !Ctxt.Flag_dynlink { + break + } + fallthrough + case TARRAY, TCHAN, TFUNC, TMAP: + slink := typelinksym(t) + dsymptr(slink, 0, s, 0) + ggloblsym(slink, int32(Widthptr), int16(dupok|obj.RODATA)) + } + } + + return s +} + +func dumptypestructs() { + var n *Node + + // copy types from externdcl list to signatlist + for l := externdcl; l != nil; l = l.Next { + n = l.N + if n.Op != OTYPE { + continue + } + signatlist = list(signatlist, n) + } + + // process signatlist + var t *Type + for l := signatlist; l != nil; l = l.Next { + n = l.N + if n.Op != OTYPE { + continue + } + t = n.Type + dtypesym(t) + if t.Sym != nil { + dtypesym(Ptrto(t)) + } + } + + // generate import strings for imported packages + for _, p := range pkgs { + if p.Direct != 0 { + dimportpath(p) + } + } + + // do basic types if compiling package runtime. + // they have to be in at least one package, + // and runtime is always loaded implicitly, + // so this is as good as any. + // another possible choice would be package main, + // but using runtime means fewer copies in .6 files. + if compiling_runtime != 0 { + for i := 1; i <= TBOOL; i++ { + dtypesym(Ptrto(Types[i])) + } + dtypesym(Ptrto(Types[TSTRING])) + dtypesym(Ptrto(Types[TUNSAFEPTR])) + + // emit type structs for error and func(error) string. + // The latter is the type of an auto-generated wrapper. + dtypesym(Ptrto(errortype)) + + dtypesym(functype(nil, list1(Nod(ODCLFIELD, nil, typenod(errortype))), list1(Nod(ODCLFIELD, nil, typenod(Types[TSTRING]))))) + + // add paths for runtime and main, which 6l imports implicitly. + dimportpath(Runtimepkg) + + if flag_race != 0 { + dimportpath(racepkg) + } + dimportpath(mkpkg("main")) + } +} + +func dalgsym(t *Type) *Sym { + var s *Sym + var hashfunc *Sym + var eqfunc *Sym + + // dalgsym is only called for a type that needs an algorithm table, + // which implies that the type is comparable (or else it would use ANOEQ). + + if algtype(t) == AMEM { + // we use one algorithm table for all AMEM types of a given size + p := fmt.Sprintf(".alg%d", t.Width) + + s = Pkglookup(p, typepkg) + + if s.Flags&SymAlgGen != 0 { + return s + } + s.Flags |= SymAlgGen + + // make hash closure + p = fmt.Sprintf(".hashfunc%d", t.Width) + + hashfunc = Pkglookup(p, typepkg) + + ot := 0 + ot = dsymptr(hashfunc, ot, Pkglookup("memhash_varlen", Runtimepkg), 0) + ot = duintxx(hashfunc, ot, uint64(t.Width), Widthptr) // size encoded in closure + ggloblsym(hashfunc, int32(ot), obj.DUPOK|obj.RODATA) + + // make equality closure + p = fmt.Sprintf(".eqfunc%d", t.Width) + + eqfunc = Pkglookup(p, typepkg) + + ot = 0 + ot = dsymptr(eqfunc, ot, Pkglookup("memequal_varlen", Runtimepkg), 0) + ot = duintxx(eqfunc, ot, uint64(t.Width), Widthptr) + ggloblsym(eqfunc, int32(ot), obj.DUPOK|obj.RODATA) + } else { + // generate an alg table specific to this type + s = typesymprefix(".alg", t) + + hash := typesymprefix(".hash", t) + eq := typesymprefix(".eq", t) + hashfunc = typesymprefix(".hashfunc", t) + eqfunc = typesymprefix(".eqfunc", t) + + genhash(hash, t) + geneq(eq, t) + + // make Go funcs (closures) for calling hash and equal from Go + dsymptr(hashfunc, 0, hash, 0) + + ggloblsym(hashfunc, int32(Widthptr), obj.DUPOK|obj.RODATA) + dsymptr(eqfunc, 0, eq, 0) + ggloblsym(eqfunc, int32(Widthptr), obj.DUPOK|obj.RODATA) + } + + // ../../runtime/alg.go:/typeAlg + ot := 0 + + ot = dsymptr(s, ot, hashfunc, 0) + ot = dsymptr(s, ot, eqfunc, 0) + ggloblsym(s, int32(ot), obj.DUPOK|obj.RODATA) + return s +} + +// 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 + } + + useGCProg = true + sym, ptrdata = dgcprog(t) + return +} + +// 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(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 { + ptrmask[i/8] |= 1 << (uint(i) % 8) + } + } +} + +// 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 +} + +type GCProg struct { + sym *Sym + symoff int + w gcprog.Writer +} + +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) + } +} + +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 + } + if t.Width == int64(Widthptr) { + p.w.Ptr(offset / int64(Widthptr)) + return + } + switch t.Etype { + default: + Fatal("GCProg.emit: unexpected type %v", t) + + case TSTRING: + p.w.Ptr(offset / int64(Widthptr)) + + case TINTER: + p.w.Ptr(offset / int64(Widthptr)) + p.w.Ptr(offset/int64(Widthptr) + 1) + + case TARRAY: + if Isslice(t) { + 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: + for t1 := t.Type; t1 != nil; t1 = t1.Down { + p.emit(t1.Type, offset+t1.Width) + } + } +} diff --git a/src/cmd/compile/internal/gc/reg.go b/src/cmd/compile/internal/gc/reg.go new file mode 100644 index 0000000000..afe95230c7 --- /dev/null +++ b/src/cmd/compile/internal/gc/reg.go @@ -0,0 +1,1559 @@ +// Derived from Inferno utils/6c/reg.c +// http://code.google.com/p/inferno-os/source/browse/utils/6c/reg.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package gc + +import ( + "bytes" + "cmd/internal/obj" + "fmt" + "sort" + "strings" +) + +// A Var represents a single variable that may be stored in a register. +// That variable may itself correspond to a hardware register, +// to represent the use of registers in the unoptimized instruction stream. +type Var struct { + offset int64 + node *Node + nextinnode *Var + width int + id int // index in vars + name int8 + etype int8 + addr int8 +} + +// Bits represents a set of Vars, stored as a bit set of var numbers +// (the index in vars, or equivalently v.id). +type Bits struct { + b [BITS]uint64 +} + +const ( + BITS = 3 + NVAR = BITS * 64 +) + +var ( + vars [NVAR]Var // variables under consideration + nvar int // number of vars + + regbits uint64 // bits for hardware registers + + zbits Bits // zero + externs Bits // global variables + params Bits // function parameters and results + ivar Bits // function parameters (inputs) + ovar Bits // function results (outputs) + consts Bits // constant values + addrs Bits // variables with address taken +) + +// A Reg is a wrapper around a single Prog (one instruction) that holds +// register optimization information while the optimizer runs. +// r->prog is the instruction. +type Reg struct { + set Bits // regopt variables written by this instruction. + use1 Bits // regopt variables read by prog->from. + use2 Bits // regopt variables read by prog->to. + + // refahead/refbehind are the regopt variables whose current + // value may be used in the following/preceding instructions + // up to a CALL (or the value is clobbered). + refbehind Bits + refahead Bits + + // calahead/calbehind are similar, but for variables in + // instructions that are reachable after hitting at least one + // CALL. + calbehind Bits + calahead Bits + + regdiff Bits + act Bits + regu uint64 // register used bitmap +} + +// A Rgn represents a single regopt variable over a region of code +// where a register could potentially be dedicated to that variable. +// The code encompassed by a Rgn is defined by the flow graph, +// starting at enter, flood-filling forward while varno is refahead +// and backward while varno is refbehind, and following branches. +// A single variable may be represented by multiple disjoint Rgns and +// each Rgn may choose a different register for that variable. +// Registers are allocated to regions greedily in order of descending +// cost. +type Rgn struct { + enter *Flow + cost int16 + varno int16 + regno int16 +} + +// The Plan 9 C compilers used a limit of 600 regions, +// but the yacc-generated parser in y.go has 3100 regions. +// We set MaxRgn large enough to handle that. +// There's not a huge cost to having too many regions: +// the main processing traces the live area for each variable, +// which is limited by the number of variables times the area, +// not the raw region count. If there are many regions, they +// are almost certainly small and easy to trace. +// The only operation that scales with region count is the +// sorting by cost, which uses sort.Sort and is therefore +// guaranteed n log n. +const MaxRgn = 6000 + +var ( + region []Rgn + nregion int +) + +type rcmp []Rgn + +func (x rcmp) Len() int { + return len(x) +} + +func (x rcmp) Swap(i, j int) { + x[i], x[j] = x[j], x[i] +} + +func (x rcmp) Less(i, j int) bool { + p1 := &x[i] + p2 := &x[j] + if p1.cost != p2.cost { + return int(p2.cost)-int(p1.cost) < 0 + } + if p1.varno != p2.varno { + return int(p2.varno)-int(p1.varno) < 0 + } + if p1.enter != p2.enter { + return int(p2.enter.Id-p1.enter.Id) < 0 + } + return false +} + +func setaddrs(bit Bits) { + var i int + var n int + var v *Var + var node *Node + + for bany(&bit) { + // convert each bit to a variable + i = bnum(bit) + + node = vars[i].node + n = int(vars[i].name) + biclr(&bit, uint(i)) + + // disable all pieces of that variable + for i = 0; i < nvar; i++ { + v = &vars[i] + if v.node == node && int(v.name) == n { + v.addr = 2 + } + } + } +} + +var regnodes [64]*Node + +func walkvardef(n *Node, f *Flow, active int) { + var f1 *Flow + var bn int + var v *Var + + for f1 = f; f1 != nil; f1 = f1.S1 { + if f1.Active == int32(active) { + break + } + f1.Active = int32(active) + if f1.Prog.As == obj.AVARKILL && f1.Prog.To.Node == n { + break + } + for v, _ = n.Opt.(*Var); v != nil; v = v.nextinnode { + bn = v.id + biset(&(f1.Data.(*Reg)).act, uint(bn)) + } + + if f1.Prog.As == obj.ACALL { + break + } + } + + for f2 := f; f2 != f1; f2 = f2.S1 { + if f2.S2 != nil { + walkvardef(n, f2.S2, active) + } + } +} + +/* + * add mov b,rn + * just after r + */ +func addmove(r *Flow, bn int, rn int, f int) { + p1 := Ctxt.NewProg() + Clearp(p1) + p1.Pc = 9999 + + p := r.Prog + p1.Link = p.Link + p.Link = p1 + p1.Lineno = p.Lineno + + v := &vars[bn] + + a := &p1.To + a.Offset = v.offset + a.Etype = uint8(v.etype) + a.Type = obj.TYPE_MEM + a.Name = v.name + a.Node = v.node + a.Sym = Linksym(v.node.Sym) + + /* NOTE(rsc): 9g did + if(a->etype == TARRAY) + a->type = TYPE_ADDR; + else if(a->sym == nil) + a->type = TYPE_CONST; + */ + p1.As = int16(Thearch.Optoas(OAS, Types[uint8(v.etype)])) + + // TODO(rsc): Remove special case here. + if (Thearch.Thechar == '5' || Thearch.Thechar == '7' || Thearch.Thechar == '9') && v.etype == TBOOL { + p1.As = int16(Thearch.Optoas(OAS, Types[TUINT8])) + } + p1.From.Type = obj.TYPE_REG + p1.From.Reg = int16(rn) + p1.From.Name = obj.NAME_NONE + if f == 0 { + p1.From = *a + *a = obj.Addr{} + a.Type = obj.TYPE_REG + a.Reg = int16(rn) + } + + if Debug['R'] != 0 && Debug['v'] != 0 { + fmt.Printf("%v ===add=== %v\n", p, p1) + } + Ostats.Nspill++ +} + +func overlap_reg(o1 int64, w1 int, o2 int64, w2 int) bool { + t1 := o1 + int64(w1) + t2 := o2 + int64(w2) + + if t1 <= o2 || t2 <= o1 { + return false + } + + return true +} + +func mkvar(f *Flow, a *obj.Addr) Bits { + /* + * mark registers used + */ + if a.Type == obj.TYPE_NONE { + return zbits + } + + r := f.Data.(*Reg) + r.use1.b[0] |= Thearch.Doregbits(int(a.Index)) // TODO: Use RtoB + + var n int + switch a.Type { + default: + regu := Thearch.Doregbits(int(a.Reg)) | Thearch.RtoB(int(a.Reg)) // TODO: Use RtoB + if regu == 0 { + return zbits + } + bit := zbits + bit.b[0] = regu + return bit + + // TODO(rsc): Remove special case here. + case obj.TYPE_ADDR: + var bit Bits + if Thearch.Thechar == '5' || Thearch.Thechar == '7' || Thearch.Thechar == '9' { + goto memcase + } + a.Type = obj.TYPE_MEM + bit = mkvar(f, a) + setaddrs(bit) + a.Type = obj.TYPE_ADDR + Ostats.Naddr++ + return zbits + + memcase: + fallthrough + + case obj.TYPE_MEM: + if r != nil { + r.use1.b[0] |= Thearch.RtoB(int(a.Reg)) + } + + /* NOTE: 5g did + if(r->f.prog->scond & (C_PBIT|C_WBIT)) + r->set.b[0] |= RtoB(a->reg); + */ + switch a.Name { + default: + // Note: This case handles NAME_EXTERN and NAME_STATIC. + // We treat these as requiring eager writes to memory, due to + // the possibility of a fault handler looking at them, so there is + // not much point in registerizing the loads. + // If we later choose the set of candidate variables from a + // larger list, these cases could be deprioritized instead of + // removed entirely. + return zbits + + case obj.NAME_PARAM, + obj.NAME_AUTO: + n = int(a.Name) + } + } + + node, _ := a.Node.(*Node) + if node == nil || node.Op != ONAME || node.Orig == nil { + return zbits + } + node = node.Orig + if node.Orig != node { + Fatal("%v: bad node", Ctxt.Dconv(a)) + } + if node.Sym == nil || node.Sym.Name[0] == '.' { + return zbits + } + et := int(a.Etype) + o := a.Offset + w := a.Width + if w < 0 { + Fatal("bad width %d for %v", w, Ctxt.Dconv(a)) + } + + flag := 0 + var v *Var + for i := 0; i < nvar; i++ { + v = &vars[i] + if v.node == node && int(v.name) == n { + if v.offset == o { + if int(v.etype) == et { + if int64(v.width) == w { + // TODO(rsc): Remove special case for arm here. + if flag == 0 || Thearch.Thechar != '5' { + return blsh(uint(i)) + } + } + } + } + + // if they overlap, disable both + if overlap_reg(v.offset, v.width, o, int(w)) { + // print("disable overlap %s %d %d %d %d, %E != %E\n", s->name, v->offset, v->width, o, w, v->etype, et); + v.addr = 1 + + flag = 1 + } + } + } + + switch et { + case 0, TFUNC: + return zbits + } + + if nvar >= NVAR { + if Debug['w'] > 1 && node != nil { + Fatal("variable not optimized: %v", Nconv(node, obj.FmtSharp)) + } + if Debug['v'] > 0 { + Warn("variable not optimized: %v", Nconv(node, obj.FmtSharp)) + } + + // If we're not tracking a word in a variable, mark the rest as + // having its address taken, so that we keep the whole thing + // live at all calls. otherwise we might optimize away part of + // a variable but not all of it. + var v *Var + for i := 0; i < nvar; i++ { + v = &vars[i] + if v.node == node { + v.addr = 1 + } + } + + return zbits + } + + i := nvar + nvar++ + v = &vars[i] + v.id = i + v.offset = o + v.name = int8(n) + v.etype = int8(et) + v.width = int(w) + v.addr = int8(flag) // funny punning + v.node = node + + // node->opt is the head of a linked list + // of Vars within the given Node, so that + // we can start at a Var and find all the other + // Vars in the same Go variable. + v.nextinnode, _ = node.Opt.(*Var) + + node.Opt = v + + bit := blsh(uint(i)) + if n == obj.NAME_EXTERN || n == obj.NAME_STATIC { + for z := 0; z < BITS; z++ { + externs.b[z] |= bit.b[z] + } + } + if n == obj.NAME_PARAM { + for z := 0; z < BITS; z++ { + params.b[z] |= bit.b[z] + } + } + + if node.Class == PPARAM { + for z := 0; z < BITS; z++ { + ivar.b[z] |= bit.b[z] + } + } + if node.Class == PPARAMOUT { + for z := 0; z < BITS; z++ { + ovar.b[z] |= bit.b[z] + } + } + + // Treat values with their address taken as live at calls, + // because the garbage collector's liveness analysis in ../gc/plive.c does. + // These must be consistent or else we will elide stores and the garbage + // collector will see uninitialized data. + // The typical case where our own analysis is out of sync is when the + // node appears to have its address taken but that code doesn't actually + // get generated and therefore doesn't show up as an address being + // taken when we analyze the instruction stream. + // One instance of this case is when a closure uses the same name as + // an outer variable for one of its own variables declared with :=. + // The parser flags the outer variable as possibly shared, and therefore + // sets addrtaken, even though it ends up not being actually shared. + // If we were better about _ elision, _ = &x would suffice too. + // The broader := in a closure problem is mentioned in a comment in + // closure.c:/^typecheckclosure and dcl.c:/^oldname. + if node.Addrtaken { + v.addr = 1 + } + + // Disable registerization for globals, because: + // (1) we might panic at any time and we want the recovery code + // to see the latest values (issue 1304). + // (2) we don't know what pointers might point at them and we want + // loads via those pointers to see updated values and vice versa (issue 7995). + // + // Disable registerization for results if using defer, because the deferred func + // might recover and return, causing the current values to be used. + if node.Class == PEXTERN || (Hasdefer != 0 && node.Class == PPARAMOUT) { + v.addr = 1 + } + + if Debug['R'] != 0 { + fmt.Printf("bit=%2d et=%v w=%d+%d %v %v flag=%d\n", i, Econv(int(et), 0), o, w, Nconv(node, obj.FmtSharp), Ctxt.Dconv(a), v.addr) + } + Ostats.Nvar++ + + return bit +} + +var change int + +func prop(f *Flow, ref Bits, cal Bits) { + var f1 *Flow + var r1 *Reg + var z int + var i int + var v *Var + var v1 *Var + + for f1 = f; f1 != nil; f1 = f1.P1 { + r1 = f1.Data.(*Reg) + for z = 0; z < BITS; z++ { + ref.b[z] |= r1.refahead.b[z] + if ref.b[z] != r1.refahead.b[z] { + r1.refahead.b[z] = ref.b[z] + change = 1 + } + + cal.b[z] |= r1.calahead.b[z] + if cal.b[z] != r1.calahead.b[z] { + r1.calahead.b[z] = cal.b[z] + change = 1 + } + } + + switch f1.Prog.As { + case obj.ACALL: + if Noreturn(f1.Prog) { + break + } + + // Mark all input variables (ivar) as used, because that's what the + // liveness bitmaps say. The liveness bitmaps say that so that a + // panic will not show stale values in the parameter dump. + // Mark variables with a recent VARDEF (r1->act) as used, + // so that the optimizer flushes initializations to memory, + // so that if a garbage collection happens during this CALL, + // the collector will see initialized memory. Again this is to + // match what the liveness bitmaps say. + for z = 0; z < BITS; z++ { + cal.b[z] |= ref.b[z] | externs.b[z] | ivar.b[z] | r1.act.b[z] + ref.b[z] = 0 + } + + // cal.b is the current approximation of what's live across the call. + // Every bit in cal.b is a single stack word. For each such word, + // find all the other tracked stack words in the same Go variable + // (struct/slice/string/interface) and mark them live too. + // This is necessary because the liveness analysis for the garbage + // collector works at variable granularity, not at word granularity. + // It is fundamental for slice/string/interface: the garbage collector + // needs the whole value, not just some of the words, in order to + // interpret the other bits correctly. Specifically, slice needs a consistent + // ptr and cap, string needs a consistent ptr and len, and interface + // needs a consistent type word and data word. + for z = 0; z < BITS; z++ { + if cal.b[z] == 0 { + continue + } + for i = 0; i < 64; i++ { + if z*64+i >= nvar || (cal.b[z]>>uint(i))&1 == 0 { + continue + } + v = &vars[z*64+i] + if v.node.Opt == nil { // v represents fixed register, not Go variable + continue + } + + // v->node->opt is the head of a linked list of Vars + // corresponding to tracked words from the Go variable v->node. + // Walk the list and set all the bits. + // For a large struct this could end up being quadratic: + // after the first setting, the outer loop (for z, i) would see a 1 bit + // for all of the remaining words in the struct, and for each such + // word would go through and turn on all the bits again. + // To avoid the quadratic behavior, we only turn on the bits if + // v is the head of the list or if the head's bit is not yet turned on. + // This will set the bits at most twice, keeping the overall loop linear. + v1, _ = v.node.Opt.(*Var) + + if v == v1 || !btest(&cal, uint(v1.id)) { + for ; v1 != nil; v1 = v1.nextinnode { + biset(&cal, uint(v1.id)) + } + } + } + } + + case obj.ATEXT: + for z = 0; z < BITS; z++ { + cal.b[z] = 0 + ref.b[z] = 0 + } + + case obj.ARET: + for z = 0; z < BITS; z++ { + cal.b[z] = externs.b[z] | ovar.b[z] + ref.b[z] = 0 + } + } + + for z = 0; z < BITS; z++ { + ref.b[z] = ref.b[z]&^r1.set.b[z] | r1.use1.b[z] | r1.use2.b[z] + cal.b[z] &^= (r1.set.b[z] | r1.use1.b[z] | r1.use2.b[z]) + r1.refbehind.b[z] = ref.b[z] + r1.calbehind.b[z] = cal.b[z] + } + + if f1.Active != 0 { + break + } + f1.Active = 1 + } + + var r *Reg + var f2 *Flow + for ; f != f1; f = f.P1 { + r = f.Data.(*Reg) + for f2 = f.P2; f2 != nil; f2 = f2.P2link { + prop(f2, r.refbehind, r.calbehind) + } + } +} + +func synch(f *Flow, dif Bits) { + var r1 *Reg + var z int + + for f1 := f; f1 != nil; f1 = f1.S1 { + r1 = f1.Data.(*Reg) + for z = 0; z < BITS; z++ { + dif.b[z] = dif.b[z]&^(^r1.refbehind.b[z]&r1.refahead.b[z]) | r1.set.b[z] | r1.regdiff.b[z] + if dif.b[z] != r1.regdiff.b[z] { + r1.regdiff.b[z] = dif.b[z] + change = 1 + } + } + + if f1.Active != 0 { + break + } + f1.Active = 1 + for z = 0; z < BITS; z++ { + dif.b[z] &^= (^r1.calbehind.b[z] & r1.calahead.b[z]) + } + if f1.S2 != nil { + synch(f1.S2, dif) + } + } +} + +func allreg(b uint64, r *Rgn) uint64 { + v := &vars[r.varno] + r.regno = 0 + switch v.etype { + default: + Fatal("unknown etype %d/%v", Bitno(b), Econv(int(v.etype), 0)) + + case TINT8, + TUINT8, + TINT16, + TUINT16, + TINT32, + TUINT32, + TINT64, + TUINT64, + TINT, + TUINT, + TUINTPTR, + TBOOL, + TPTR32, + TPTR64: + i := Thearch.BtoR(^b) + if i != 0 && r.cost > 0 { + r.regno = int16(i) + return Thearch.RtoB(i) + } + + case TFLOAT32, TFLOAT64: + i := Thearch.BtoF(^b) + if i != 0 && r.cost > 0 { + r.regno = int16(i) + return Thearch.FtoB(i) + } + } + + return 0 +} + +func LOAD(r *Reg, z int) uint64 { + return ^r.refbehind.b[z] & r.refahead.b[z] +} + +func STORE(r *Reg, z int) uint64 { + return ^r.calbehind.b[z] & r.calahead.b[z] +} + +// Cost parameters +const ( + CLOAD = 5 // cost of load + CREF = 5 // cost of reference if not registerized + LOOP = 3 // loop execution count (applied in popt.go) +) + +func paint1(f *Flow, bn int) { + z := bn / 64 + bb := uint64(1 << uint(bn%64)) + r := f.Data.(*Reg) + if r.act.b[z]&bb != 0 { + return + } + var f1 *Flow + var r1 *Reg + for { + if r.refbehind.b[z]&bb == 0 { + break + } + f1 = f.P1 + if f1 == nil { + break + } + r1 = f1.Data.(*Reg) + if r1.refahead.b[z]&bb == 0 { + break + } + if r1.act.b[z]&bb != 0 { + break + } + f = f1 + r = r1 + } + + if LOAD(r, z)&^(r.set.b[z]&^(r.use1.b[z]|r.use2.b[z]))&bb != 0 { + change -= CLOAD * int(f.Loop) + } + + for { + r.act.b[z] |= bb + + if f.Prog.As != obj.ANOP { // don't give credit for NOPs + if r.use1.b[z]&bb != 0 { + change += CREF * int(f.Loop) + } + if (r.use2.b[z]|r.set.b[z])&bb != 0 { + change += CREF * int(f.Loop) + } + } + + if STORE(r, z)&r.regdiff.b[z]&bb != 0 { + change -= CLOAD * int(f.Loop) + } + + if r.refbehind.b[z]&bb != 0 { + for f1 = f.P2; f1 != nil; f1 = f1.P2link { + if (f1.Data.(*Reg)).refahead.b[z]&bb != 0 { + paint1(f1, bn) + } + } + } + + if r.refahead.b[z]&bb == 0 { + break + } + f1 = f.S2 + if f1 != nil { + if (f1.Data.(*Reg)).refbehind.b[z]&bb != 0 { + paint1(f1, bn) + } + } + f = f.S1 + if f == nil { + break + } + r = f.Data.(*Reg) + if r.act.b[z]&bb != 0 { + break + } + if r.refbehind.b[z]&bb == 0 { + break + } + } +} + +func paint2(f *Flow, bn int, depth int) uint64 { + z := bn / 64 + bb := uint64(1 << uint(bn%64)) + vreg := regbits + r := f.Data.(*Reg) + if r.act.b[z]&bb == 0 { + return vreg + } + var r1 *Reg + var f1 *Flow + for { + if r.refbehind.b[z]&bb == 0 { + break + } + f1 = f.P1 + if f1 == nil { + break + } + r1 = f1.Data.(*Reg) + if r1.refahead.b[z]&bb == 0 { + break + } + if r1.act.b[z]&bb == 0 { + break + } + f = f1 + r = r1 + } + + for { + if Debug['R'] != 0 && Debug['v'] != 0 { + fmt.Printf(" paint2 %d %v\n", depth, f.Prog) + } + + r.act.b[z] &^= bb + + vreg |= r.regu + + if r.refbehind.b[z]&bb != 0 { + for f1 = f.P2; f1 != nil; f1 = f1.P2link { + if (f1.Data.(*Reg)).refahead.b[z]&bb != 0 { + vreg |= paint2(f1, bn, depth+1) + } + } + } + + if r.refahead.b[z]&bb == 0 { + break + } + f1 = f.S2 + if f1 != nil { + if (f1.Data.(*Reg)).refbehind.b[z]&bb != 0 { + vreg |= paint2(f1, bn, depth+1) + } + } + f = f.S1 + if f == nil { + break + } + r = f.Data.(*Reg) + if r.act.b[z]&bb == 0 { + break + } + if r.refbehind.b[z]&bb == 0 { + break + } + } + + return vreg +} + +func paint3(f *Flow, bn int, rb uint64, rn int) { + z := bn / 64 + bb := uint64(1 << uint(bn%64)) + r := f.Data.(*Reg) + if r.act.b[z]&bb != 0 { + return + } + var r1 *Reg + var f1 *Flow + for { + if r.refbehind.b[z]&bb == 0 { + break + } + f1 = f.P1 + if f1 == nil { + break + } + r1 = f1.Data.(*Reg) + if r1.refahead.b[z]&bb == 0 { + break + } + if r1.act.b[z]&bb != 0 { + break + } + f = f1 + r = r1 + } + + if LOAD(r, z)&^(r.set.b[z]&^(r.use1.b[z]|r.use2.b[z]))&bb != 0 { + addmove(f, bn, rn, 0) + } + var p *obj.Prog + for { + r.act.b[z] |= bb + p = f.Prog + + if r.use1.b[z]&bb != 0 { + if Debug['R'] != 0 && Debug['v'] != 0 { + fmt.Printf("%v", p) + } + addreg(&p.From, rn) + if Debug['R'] != 0 && Debug['v'] != 0 { + fmt.Printf(" ===change== %v\n", p) + } + } + + if (r.use2.b[z]|r.set.b[z])&bb != 0 { + if Debug['R'] != 0 && Debug['v'] != 0 { + fmt.Printf("%v", p) + } + addreg(&p.To, rn) + if Debug['R'] != 0 && Debug['v'] != 0 { + fmt.Printf(" ===change== %v\n", p) + } + } + + if STORE(r, z)&r.regdiff.b[z]&bb != 0 { + addmove(f, bn, rn, 1) + } + r.regu |= rb + + if r.refbehind.b[z]&bb != 0 { + for f1 = f.P2; f1 != nil; f1 = f1.P2link { + if (f1.Data.(*Reg)).refahead.b[z]&bb != 0 { + paint3(f1, bn, rb, rn) + } + } + } + + if r.refahead.b[z]&bb == 0 { + break + } + f1 = f.S2 + if f1 != nil { + if (f1.Data.(*Reg)).refbehind.b[z]&bb != 0 { + paint3(f1, bn, rb, rn) + } + } + f = f.S1 + if f == nil { + break + } + r = f.Data.(*Reg) + if r.act.b[z]&bb != 0 { + break + } + if r.refbehind.b[z]&bb == 0 { + break + } + } +} + +func addreg(a *obj.Addr, rn int) { + a.Sym = nil + a.Node = nil + a.Offset = 0 + a.Type = obj.TYPE_REG + a.Reg = int16(rn) + a.Name = 0 + + Ostats.Ncvtreg++ +} + +func dumpone(f *Flow, isreg int) { + fmt.Printf("%d:%v", f.Loop, f.Prog) + if isreg != 0 { + r := f.Data.(*Reg) + var bit Bits + for z := 0; z < BITS; z++ { + bit.b[z] = r.set.b[z] | r.use1.b[z] | r.use2.b[z] | r.refbehind.b[z] | r.refahead.b[z] | r.calbehind.b[z] | r.calahead.b[z] | r.regdiff.b[z] | r.act.b[z] | 0 + } + if bany(&bit) { + fmt.Printf("\t") + if bany(&r.set) { + fmt.Printf(" s:%v", &r.set) + } + if bany(&r.use1) { + fmt.Printf(" u1:%v", &r.use1) + } + if bany(&r.use2) { + fmt.Printf(" u2:%v", &r.use2) + } + if bany(&r.refbehind) { + fmt.Printf(" rb:%v ", &r.refbehind) + } + if bany(&r.refahead) { + fmt.Printf(" ra:%v ", &r.refahead) + } + if bany(&r.calbehind) { + fmt.Printf(" cb:%v ", &r.calbehind) + } + if bany(&r.calahead) { + fmt.Printf(" ca:%v ", &r.calahead) + } + if bany(&r.regdiff) { + fmt.Printf(" d:%v ", &r.regdiff) + } + if bany(&r.act) { + fmt.Printf(" a:%v ", &r.act) + } + } + } + + fmt.Printf("\n") +} + +func Dumpit(str string, r0 *Flow, isreg int) { + var r1 *Flow + + fmt.Printf("\n%s\n", str) + for r := r0; r != nil; r = r.Link { + dumpone(r, isreg) + r1 = r.P2 + if r1 != nil { + fmt.Printf("\tpred:") + for ; r1 != nil; r1 = r1.P2link { + fmt.Printf(" %.4d", uint(int(r1.Prog.Pc))) + } + if r.P1 != nil { + fmt.Printf(" (and %.4d)", uint(int(r.P1.Prog.Pc))) + } else { + fmt.Printf(" (only)") + } + fmt.Printf("\n") + } + + // Print successors if it's not just the next one + if r.S1 != r.Link || r.S2 != nil { + fmt.Printf("\tsucc:") + if r.S1 != nil { + fmt.Printf(" %.4d", uint(int(r.S1.Prog.Pc))) + } + if r.S2 != nil { + fmt.Printf(" %.4d", uint(int(r.S2.Prog.Pc))) + } + fmt.Printf("\n") + } + } +} + +func regopt(firstp *obj.Prog) { + mergetemp(firstp) + + /* + * control flow is more complicated in generated go code + * than in generated c code. define pseudo-variables for + * registers, so we have complete register usage information. + */ + var nreg int + regnames := Thearch.Regnames(&nreg) + + nvar = nreg + for i := 0; i < nreg; i++ { + vars[i] = Var{} + } + for i := 0; i < nreg; i++ { + if regnodes[i] == nil { + regnodes[i] = newname(Lookup(regnames[i])) + } + vars[i].node = regnodes[i] + } + + regbits = Thearch.Excludedregs() + externs = zbits + params = zbits + consts = zbits + addrs = zbits + ivar = zbits + ovar = zbits + + /* + * pass 1 + * build aux data structure + * allocate pcs + * find use and set of variables + */ + g := Flowstart(firstp, func() interface{} { return new(Reg) }) + if g == nil { + for i := 0; i < nvar; i++ { + vars[i].node.Opt = nil + } + return + } + + firstf := g.Start + + for f := firstf; f != nil; f = f.Link { + p := f.Prog + if p.As == obj.AVARDEF || p.As == obj.AVARKILL { + continue + } + + // Avoid making variables for direct-called functions. + if p.As == obj.ACALL && p.To.Type == obj.TYPE_MEM && p.To.Name == obj.NAME_EXTERN { + continue + } + + // from vs to doesn't matter for registers. + r := f.Data.(*Reg) + r.use1.b[0] |= p.Info.Reguse | p.Info.Regindex + r.set.b[0] |= p.Info.Regset + + bit := mkvar(f, &p.From) + if bany(&bit) { + if p.Info.Flags&LeftAddr != 0 { + setaddrs(bit) + } + if p.Info.Flags&LeftRead != 0 { + for z := 0; z < BITS; z++ { + r.use1.b[z] |= bit.b[z] + } + } + if p.Info.Flags&LeftWrite != 0 { + for z := 0; z < BITS; z++ { + r.set.b[z] |= bit.b[z] + } + } + } + + // Compute used register for reg + if p.Info.Flags&RegRead != 0 { + r.use1.b[0] |= Thearch.RtoB(int(p.Reg)) + } + + // Currently we never generate three register forms. + // If we do, this will need to change. + if p.From3.Type != obj.TYPE_NONE { + Fatal("regopt not implemented for from3") + } + + bit = mkvar(f, &p.To) + if bany(&bit) { + if p.Info.Flags&RightAddr != 0 { + setaddrs(bit) + } + if p.Info.Flags&RightRead != 0 { + for z := 0; z < BITS; z++ { + r.use2.b[z] |= bit.b[z] + } + } + if p.Info.Flags&RightWrite != 0 { + for z := 0; z < BITS; z++ { + r.set.b[z] |= bit.b[z] + } + } + } + } + + for i := 0; i < nvar; i++ { + v := &vars[i] + if v.addr != 0 { + bit := blsh(uint(i)) + for z := 0; z < BITS; z++ { + addrs.b[z] |= bit.b[z] + } + } + + if Debug['R'] != 0 && Debug['v'] != 0 { + fmt.Printf("bit=%2d addr=%d et=%v w=%-2d s=%v + %d\n", i, v.addr, Econv(int(v.etype), 0), v.width, v.node, v.offset) + } + } + + if Debug['R'] != 0 && Debug['v'] != 0 { + Dumpit("pass1", firstf, 1) + } + + /* + * pass 2 + * find looping structure + */ + flowrpo(g) + + if Debug['R'] != 0 && Debug['v'] != 0 { + Dumpit("pass2", firstf, 1) + } + + /* + * pass 2.5 + * iterate propagating fat vardef covering forward + * r->act records vars with a VARDEF since the last CALL. + * (r->act will be reused in pass 5 for something else, + * but we'll be done with it by then.) + */ + active := 0 + + for f := firstf; f != nil; f = f.Link { + f.Active = 0 + r := f.Data.(*Reg) + r.act = zbits + } + + for f := firstf; f != nil; f = f.Link { + p := f.Prog + if p.As == obj.AVARDEF && Isfat(((p.To.Node).(*Node)).Type) && ((p.To.Node).(*Node)).Opt != nil { + active++ + walkvardef(p.To.Node.(*Node), f, active) + } + } + + /* + * pass 3 + * iterate propagating usage + * back until flow graph is complete + */ + var f1 *Flow + var i int + var f *Flow +loop1: + change = 0 + + for f = firstf; f != nil; f = f.Link { + f.Active = 0 + } + for f = firstf; f != nil; f = f.Link { + if f.Prog.As == obj.ARET { + prop(f, zbits, zbits) + } + } + + /* pick up unreachable code */ +loop11: + i = 0 + + for f = firstf; f != nil; f = f1 { + f1 = f.Link + if f1 != nil && f1.Active != 0 && f.Active == 0 { + prop(f, zbits, zbits) + i = 1 + } + } + + if i != 0 { + goto loop11 + } + if change != 0 { + goto loop1 + } + + if Debug['R'] != 0 && Debug['v'] != 0 { + Dumpit("pass3", firstf, 1) + } + + /* + * pass 4 + * iterate propagating register/variable synchrony + * forward until graph is complete + */ +loop2: + change = 0 + + for f = firstf; f != nil; f = f.Link { + f.Active = 0 + } + synch(firstf, zbits) + if change != 0 { + goto loop2 + } + + if Debug['R'] != 0 && Debug['v'] != 0 { + Dumpit("pass4", firstf, 1) + } + + /* + * pass 4.5 + * move register pseudo-variables into regu. + */ + mask := uint64((1 << uint(nreg)) - 1) + for f := firstf; f != nil; f = f.Link { + r := f.Data.(*Reg) + r.regu = (r.refbehind.b[0] | r.set.b[0]) & mask + r.set.b[0] &^= mask + r.use1.b[0] &^= mask + r.use2.b[0] &^= mask + r.refbehind.b[0] &^= mask + r.refahead.b[0] &^= mask + r.calbehind.b[0] &^= mask + r.calahead.b[0] &^= mask + r.regdiff.b[0] &^= mask + r.act.b[0] &^= mask + } + + if Debug['R'] != 0 && Debug['v'] != 0 { + Dumpit("pass4.5", firstf, 1) + } + + /* + * pass 5 + * isolate regions + * calculate costs (paint1) + */ + var bit Bits + if f := firstf; f != nil { + r := f.Data.(*Reg) + for z := 0; z < BITS; z++ { + bit.b[z] = (r.refahead.b[z] | r.calahead.b[z]) &^ (externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]) + } + if bany(&bit) && f.Refset == 0 { + // should never happen - all variables are preset + if Debug['w'] != 0 { + fmt.Printf("%v: used and not set: %v\n", f.Prog.Line(), &bit) + } + f.Refset = 1 + } + } + + for f := firstf; f != nil; f = f.Link { + (f.Data.(*Reg)).act = zbits + } + nregion = 0 + region = region[:0] + var rgp *Rgn + for f := firstf; f != nil; f = f.Link { + r := f.Data.(*Reg) + for z := 0; z < BITS; z++ { + bit.b[z] = r.set.b[z] &^ (r.refahead.b[z] | r.calahead.b[z] | addrs.b[z]) + } + if bany(&bit) && f.Refset == 0 { + if Debug['w'] != 0 { + fmt.Printf("%v: set and not used: %v\n", f.Prog.Line(), &bit) + } + f.Refset = 1 + Thearch.Excise(f) + } + + for z := 0; z < BITS; z++ { + bit.b[z] = LOAD(r, z) &^ (r.act.b[z] | addrs.b[z]) + } + for bany(&bit) { + i = bnum(bit) + change = 0 + paint1(f, i) + biclr(&bit, uint(i)) + if change <= 0 { + continue + } + if nregion >= MaxRgn { + nregion++ + continue + } + + region = append(region, Rgn{ + enter: f, + cost: int16(change), + varno: int16(i), + }) + nregion++ + } + } + + if false && Debug['v'] != 0 && strings.Contains(Curfn.Nname.Sym.Name, "Parse") { + Warn("regions: %d\n", nregion) + } + if nregion >= MaxRgn { + if Debug['v'] != 0 { + Warn("too many regions: %d\n", nregion) + } + nregion = MaxRgn + } + + sort.Sort(rcmp(region[:nregion])) + + if Debug['R'] != 0 && Debug['v'] != 0 { + Dumpit("pass5", firstf, 1) + } + + /* + * pass 6 + * determine used registers (paint2) + * replace code (paint3) + */ + if Debug['R'] != 0 && Debug['v'] != 0 { + fmt.Printf("\nregisterizing\n") + } + var usedreg uint64 + var vreg uint64 + for i := 0; i < nregion; i++ { + rgp = ®ion[i] + if Debug['R'] != 0 && Debug['v'] != 0 { + fmt.Printf("region %d: cost %d varno %d enter %d\n", i, rgp.cost, rgp.varno, rgp.enter.Prog.Pc) + } + bit = blsh(uint(rgp.varno)) + usedreg = paint2(rgp.enter, int(rgp.varno), 0) + vreg = allreg(usedreg, rgp) + if rgp.regno != 0 { + if Debug['R'] != 0 && Debug['v'] != 0 { + v := &vars[rgp.varno] + fmt.Printf("registerize %v+%d (bit=%2d et=%v) in %v usedreg=%#x vreg=%#x\n", v.node, v.offset, rgp.varno, Econv(int(v.etype), 0), obj.Rconv(int(rgp.regno)), usedreg, vreg) + } + + paint3(rgp.enter, int(rgp.varno), vreg, int(rgp.regno)) + } + } + + /* + * free aux structures. peep allocates new ones. + */ + for i := 0; i < nvar; i++ { + vars[i].node.Opt = nil + } + Flowend(g) + firstf = nil + + if Debug['R'] != 0 && Debug['v'] != 0 { + // Rebuild flow graph, since we inserted instructions + g := Flowstart(firstp, nil) + firstf = g.Start + Dumpit("pass6", firstf, 0) + Flowend(g) + firstf = nil + } + + /* + * pass 7 + * peep-hole on basic block + */ + if Debug['R'] == 0 || Debug['P'] != 0 { + Thearch.Peep(firstp) + } + + /* + * eliminate nops + */ + for p := firstp; p != nil; p = p.Link { + for p.Link != nil && p.Link.As == obj.ANOP { + p.Link = p.Link.Link + } + if p.To.Type == obj.TYPE_BRANCH { + for p.To.Val.(*obj.Prog) != nil && p.To.Val.(*obj.Prog).As == obj.ANOP { + p.To.Val = p.To.Val.(*obj.Prog).Link + } + } + } + + if Debug['R'] != 0 { + if Ostats.Ncvtreg != 0 || Ostats.Nspill != 0 || Ostats.Nreload != 0 || Ostats.Ndelmov != 0 || Ostats.Nvar != 0 || Ostats.Naddr != 0 || false { + fmt.Printf("\nstats\n") + } + + if Ostats.Ncvtreg != 0 { + fmt.Printf("\t%4d cvtreg\n", Ostats.Ncvtreg) + } + if Ostats.Nspill != 0 { + fmt.Printf("\t%4d spill\n", Ostats.Nspill) + } + if Ostats.Nreload != 0 { + fmt.Printf("\t%4d reload\n", Ostats.Nreload) + } + if Ostats.Ndelmov != 0 { + fmt.Printf("\t%4d delmov\n", Ostats.Ndelmov) + } + if Ostats.Nvar != 0 { + fmt.Printf("\t%4d var\n", Ostats.Nvar) + } + if Ostats.Naddr != 0 { + fmt.Printf("\t%4d addr\n", Ostats.Naddr) + } + + Ostats = OptStats{} + } +} + +// bany reports whether any bits in a are set. +func bany(a *Bits) bool { + for _, x := range &a.b { // & to avoid making a copy of a.b + if x != 0 { + return true + } + } + return false +} + +// bnum reports the lowest index of a 1 bit in a. +func bnum(a Bits) int { + for i, x := range &a.b { // & to avoid making a copy of a.b + if x != 0 { + return 64*i + Bitno(x) + } + } + + Fatal("bad in bnum") + return 0 +} + +// blsh returns a Bits with 1 at index n, 0 elsewhere (1<>= 32 + } + if b&(1<<16-1) == 0 { + n += 16 + b >>= 16 + } + if b&(1<<8-1) == 0 { + n += 8 + b >>= 8 + } + if b&(1<<4-1) == 0 { + n += 4 + b >>= 4 + } + if b&(1<<2-1) == 0 { + n += 2 + b >>= 2 + } + if b&1 == 0 { + n++ + } + return n +} + +// String returns a space-separated list of the variables represented by bits. +func (bits Bits) String() string { + // Note: This method takes a value receiver, both for convenience + // and to make it safe to modify the bits as we process them. + // Even so, most prints above use &bits, because then the value + // being stored in the interface{} is a pointer and does not require + // an allocation and copy to create the interface{}. + var buf bytes.Buffer + sep := "" + for bany(&bits) { + i := bnum(bits) + buf.WriteString(sep) + sep = " " + v := &vars[i] + if v.node == nil || v.node.Sym == nil { + fmt.Fprintf(&buf, "$%d", i) + } else { + fmt.Fprintf(&buf, "%s(%d)", v.node.Sym.Name, i) + if v.offset != 0 { + fmt.Fprintf(&buf, "%+d", int64(v.offset)) + } + } + biclr(&bits, uint(i)) + } + return buf.String() +} diff --git a/src/cmd/compile/internal/gc/select.go b/src/cmd/compile/internal/gc/select.go new file mode 100644 index 0000000000..3a28ea3c77 --- /dev/null +++ b/src/cmd/compile/internal/gc/select.go @@ -0,0 +1,371 @@ +// Copyright 2009 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 gc + +/* + * select + */ +func typecheckselect(sel *Node) { + var ncase *Node + var n *Node + + var def *Node + lno := int(setlineno(sel)) + count := 0 + typechecklist(sel.Ninit, Etop) + for l := sel.List; l != nil; l = l.Next { + count++ + ncase = l.N + setlineno(ncase) + if ncase.Op != OXCASE { + Fatal("typecheckselect %v", Oconv(int(ncase.Op), 0)) + } + + if ncase.List == nil { + // default + if def != nil { + Yyerror("multiple defaults in select (first at %v)", def.Line()) + } else { + def = ncase + } + } else if ncase.List.Next != nil { + Yyerror("select cases cannot be lists") + } else { + n = typecheck(&ncase.List.N, Etop) + ncase.Left = n + ncase.List = nil + setlineno(n) + switch n.Op { + default: + Yyerror("select case must be receive, send or assign recv") + + // convert x = <-c into OSELRECV(x, <-c). + // remove implicit conversions; the eventual assignment + // will reintroduce them. + case OAS: + if (n.Right.Op == OCONVNOP || n.Right.Op == OCONVIFACE) && n.Right.Implicit { + n.Right = n.Right.Left + } + + if n.Right.Op != ORECV { + Yyerror("select assignment must have receive on right hand side") + break + } + + n.Op = OSELRECV + + // convert x, ok = <-c into OSELRECV2(x, <-c) with ntest=ok + case OAS2RECV: + if n.Rlist.N.Op != ORECV { + Yyerror("select assignment must have receive on right hand side") + break + } + + n.Op = OSELRECV2 + n.Left = n.List.N + n.Ntest = n.List.Next.N + n.List = nil + n.Right = n.Rlist.N + n.Rlist = nil + + // convert <-c into OSELRECV(N, <-c) + case ORECV: + n = Nod(OSELRECV, nil, n) + + n.Typecheck = 1 + ncase.Left = n + + case OSEND: + break + } + } + + typechecklist(ncase.Nbody, Etop) + } + + sel.Xoffset = int64(count) + lineno = int32(lno) +} + +func walkselect(sel *Node) { + if sel.List == nil && sel.Xoffset != 0 { + Fatal("double walkselect") // already rewrote + } + + lno := int(setlineno(sel)) + i := count(sel.List) + + // optimization: zero-case select + var init *NodeList + var r *Node + var n *Node + var var_ *Node + var selv *Node + var cas *Node + if i == 0 { + sel.Nbody = list1(mkcall("block", nil, nil)) + goto out + } + + // optimization: one-case select: single op. + // TODO(rsc): Reenable optimization once order.c can handle it. + // golang.org/issue/7672. + if i == 1 { + cas := sel.List.N + setlineno(cas) + l := cas.Ninit + if cas.Left != nil { // not default: + n := cas.Left + l = concat(l, n.Ninit) + n.Ninit = nil + var ch *Node + switch n.Op { + default: + Fatal("select %v", Oconv(int(n.Op), 0)) + + // ok already + case OSEND: + ch = n.Left + + case OSELRECV, OSELRECV2: + ch = n.Right.Left + if n.Op == OSELRECV || n.Ntest == nil { + if n.Left == nil { + n = n.Right + } else { + n.Op = OAS + } + break + } + + if n.Left == nil { + typecheck(&nblank, Erv|Easgn) + n.Left = nblank + } + + n.Op = OAS2 + n.List = list(list1(n.Left), n.Ntest) + n.Rlist = list1(n.Right) + n.Right = nil + n.Left = nil + n.Ntest = nil + n.Typecheck = 0 + typecheck(&n, Etop) + } + + // if ch == nil { block() }; n; + a := Nod(OIF, nil, nil) + + a.Ntest = Nod(OEQ, ch, nodnil()) + a.Nbody = list1(mkcall("block", nil, &l)) + typecheck(&a, Etop) + l = list(l, a) + l = list(l, n) + } + + l = concat(l, cas.Nbody) + sel.Nbody = l + goto out + } + + // convert case value arguments to addresses. + // this rewrite is used by both the general code and the next optimization. + for l := sel.List; l != nil; l = l.Next { + cas = l.N + setlineno(cas) + n = cas.Left + if n == nil { + continue + } + switch n.Op { + case OSEND: + n.Right = Nod(OADDR, n.Right, nil) + typecheck(&n.Right, Erv) + + case OSELRECV, OSELRECV2: + if n.Op == OSELRECV2 && n.Ntest == nil { + n.Op = OSELRECV + } + if n.Op == OSELRECV2 { + n.Ntest = Nod(OADDR, n.Ntest, nil) + typecheck(&n.Ntest, Erv) + } + + if n.Left == nil { + n.Left = nodnil() + } else { + n.Left = Nod(OADDR, n.Left, nil) + typecheck(&n.Left, Erv) + } + } + } + + // optimization: two-case select but one is default: single non-blocking op. + if i == 2 && (sel.List.N.Left == nil || sel.List.Next.N.Left == nil) { + var cas *Node + var dflt *Node + if sel.List.N.Left == nil { + cas = sel.List.Next.N + dflt = sel.List.N + } else { + dflt = sel.List.Next.N + cas = sel.List.N + } + + n := cas.Left + setlineno(n) + r := Nod(OIF, nil, nil) + r.Ninit = cas.Ninit + switch n.Op { + default: + Fatal("select %v", Oconv(int(n.Op), 0)) + + // if selectnbsend(c, v) { body } else { default body } + case OSEND: + ch := n.Left + + r.Ntest = mkcall1(chanfn("selectnbsend", 2, ch.Type), Types[TBOOL], &r.Ninit, typename(ch.Type), ch, n.Right) + + // if c != nil && selectnbrecv(&v, c) { body } else { default body } + case OSELRECV: + r = Nod(OIF, nil, nil) + + r.Ninit = cas.Ninit + ch := n.Right.Left + r.Ntest = mkcall1(chanfn("selectnbrecv", 2, ch.Type), Types[TBOOL], &r.Ninit, typename(ch.Type), n.Left, ch) + + // if c != nil && selectnbrecv2(&v, c) { body } else { default body } + case OSELRECV2: + r = Nod(OIF, nil, nil) + + r.Ninit = cas.Ninit + ch := n.Right.Left + r.Ntest = mkcall1(chanfn("selectnbrecv2", 2, ch.Type), Types[TBOOL], &r.Ninit, typename(ch.Type), n.Left, n.Ntest, ch) + } + + typecheck(&r.Ntest, Erv) + r.Nbody = cas.Nbody + r.Nelse = concat(dflt.Ninit, dflt.Nbody) + sel.Nbody = list1(r) + goto out + } + + init = sel.Ninit + sel.Ninit = nil + + // generate sel-struct + setlineno(sel) + + selv = temp(selecttype(int32(sel.Xoffset))) + r = Nod(OAS, selv, nil) + typecheck(&r, Etop) + init = list(init, r) + var_ = conv(conv(Nod(OADDR, selv, nil), Types[TUNSAFEPTR]), Ptrto(Types[TUINT8])) + r = mkcall("newselect", nil, nil, var_, Nodintconst(selv.Type.Width), Nodintconst(sel.Xoffset)) + typecheck(&r, Etop) + init = list(init, r) + + // register cases + for l := sel.List; l != nil; l = l.Next { + cas = l.N + setlineno(cas) + n = cas.Left + r = Nod(OIF, nil, nil) + r.Ninit = cas.Ninit + cas.Ninit = nil + if n != nil { + r.Ninit = concat(r.Ninit, n.Ninit) + n.Ninit = nil + } + + if n == nil { + // selectdefault(sel *byte); + r.Ntest = mkcall("selectdefault", Types[TBOOL], &r.Ninit, var_) + } else { + switch n.Op { + default: + Fatal("select %v", Oconv(int(n.Op), 0)) + + // selectsend(sel *byte, hchan *chan any, elem *any) (selected bool); + case OSEND: + r.Ntest = mkcall1(chanfn("selectsend", 2, n.Left.Type), Types[TBOOL], &r.Ninit, var_, n.Left, n.Right) + + // selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool); + case OSELRECV: + r.Ntest = mkcall1(chanfn("selectrecv", 2, n.Right.Left.Type), Types[TBOOL], &r.Ninit, var_, n.Right.Left, n.Left) + + // selectrecv2(sel *byte, hchan *chan any, elem *any, received *bool) (selected bool); + case OSELRECV2: + r.Ntest = mkcall1(chanfn("selectrecv2", 2, n.Right.Left.Type), Types[TBOOL], &r.Ninit, var_, n.Right.Left, n.Left, n.Ntest) + } + } + + // selv is no longer alive after use. + r.Nbody = list(r.Nbody, Nod(OVARKILL, selv, nil)) + + r.Nbody = concat(r.Nbody, cas.Nbody) + r.Nbody = list(r.Nbody, Nod(OBREAK, nil, nil)) + init = list(init, r) + } + + // run the select + setlineno(sel) + + init = list(init, mkcall("selectgo", nil, nil, var_)) + sel.Nbody = init + +out: + sel.List = nil + walkstmtlist(sel.Nbody) + lineno = int32(lno) +} + +// Keep in sync with src/runtime/runtime2.go and src/runtime/select.go. +func selecttype(size int32) *Type { + // TODO(dvyukov): it's possible to generate SudoG and Scase only once + // and then cache; and also cache Select per size. + sudog := Nod(OTSTRUCT, nil, nil) + + sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("g")), typenod(Ptrto(Types[TUINT8])))) + sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("selectdone")), typenod(Ptrto(Types[TUINT8])))) + sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("next")), typenod(Ptrto(Types[TUINT8])))) + sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("prev")), typenod(Ptrto(Types[TUINT8])))) + sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("elem")), typenod(Ptrto(Types[TUINT8])))) + sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("releasetime")), typenod(Types[TUINT64]))) + sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("nrelease")), typenod(Types[TINT32]))) + sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("waitlink")), typenod(Ptrto(Types[TUINT8])))) + typecheck(&sudog, Etype) + sudog.Type.Noalg = 1 + sudog.Type.Local = true + + scase := Nod(OTSTRUCT, nil, nil) + scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("elem")), typenod(Ptrto(Types[TUINT8])))) + scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("chan")), typenod(Ptrto(Types[TUINT8])))) + scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("pc")), typenod(Types[TUINTPTR]))) + scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("kind")), typenod(Types[TUINT16]))) + scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("so")), typenod(Types[TUINT16]))) + scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("receivedp")), typenod(Ptrto(Types[TUINT8])))) + scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("releasetime")), typenod(Types[TUINT64]))) + typecheck(&scase, Etype) + scase.Type.Noalg = 1 + scase.Type.Local = true + + sel := Nod(OTSTRUCT, nil, nil) + sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("tcase")), typenod(Types[TUINT16]))) + sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("ncase")), typenod(Types[TUINT16]))) + sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("pollorder")), typenod(Ptrto(Types[TUINT8])))) + sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("lockorder")), typenod(Ptrto(Types[TUINT8])))) + arr := Nod(OTARRAY, Nodintconst(int64(size)), scase) + sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("scase")), arr)) + arr = Nod(OTARRAY, Nodintconst(int64(size)), typenod(Ptrto(Types[TUINT8]))) + sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("lockorderarr")), arr)) + arr = Nod(OTARRAY, Nodintconst(int64(size)), typenod(Types[TUINT16])) + sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("pollorderarr")), arr)) + typecheck(&sel, Etype) + sel.Type.Noalg = 1 + sel.Type.Local = true + + return sel.Type +} diff --git a/src/cmd/compile/internal/gc/sinit.go b/src/cmd/compile/internal/gc/sinit.go new file mode 100644 index 0000000000..dfaec74de2 --- /dev/null +++ b/src/cmd/compile/internal/gc/sinit.go @@ -0,0 +1,1528 @@ +// Copyright 2009 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 gc + +import ( + "cmd/internal/obj" + "fmt" +) + +/* + * static initialization + */ +const ( + InitNotStarted = 0 + InitDone = 1 + InitPending = 2 +) + +var initlist *NodeList + +// init1 walks the AST starting at n, and accumulates in out +// the list of definitions needing init code in dependency order. +func init1(n *Node, out **NodeList) { + if n == nil { + return + } + init1(n.Left, out) + init1(n.Right, out) + for l := n.List; l != nil; l = l.Next { + init1(l.N, out) + } + + if n.Left != nil && n.Type != nil && n.Left.Op == OTYPE && n.Class == PFUNC { + // Methods called as Type.Method(receiver, ...). + // Definitions for method expressions are stored in type->nname. + init1(n.Type.Nname, out) + } + + if n.Op != ONAME { + return + } + switch n.Class { + case PEXTERN, PFUNC: + break + + default: + if isblank(n) && n.Curfn == nil && n.Defn != nil && n.Defn.Initorder == InitNotStarted { + // blank names initialization is part of init() but not + // when they are inside a function. + break + } + + return + } + + if n.Initorder == InitDone { + return + } + if n.Initorder == InitPending { + // Since mutually recursive sets of functions are allowed, + // we don't necessarily raise an error if n depends on a node + // which is already waiting for its dependencies to be visited. + // + // initlist contains a cycle of identifiers referring to each other. + // If this cycle contains a variable, then this variable refers to itself. + // Conversely, if there exists an initialization cycle involving + // a variable in the program, the tree walk will reach a cycle + // involving that variable. + var nv *Node + if n.Class != PFUNC { + nv = n + goto foundinitloop + } + + for l := initlist; l.N != n; l = l.Next { + if l.N.Class != PFUNC { + nv = l.N + goto foundinitloop + } + } + + // The loop involves only functions, ok. + return + + // if there have already been errors printed, + // those errors probably confused us and + // there might not be a loop. let the user + // fix those first. + foundinitloop: + Flusherrors() + + if nerrors > 0 { + errorexit() + } + + // There is a loop involving nv. We know about + // n and initlist = n1 <- ... <- nv <- ... <- n <- ... + fmt.Printf("%v: initialization loop:\n", nv.Line()) + + // Build back pointers in initlist. + for l := initlist; l != nil; l = l.Next { + if l.Next != nil { + l.Next.End = l + } + } + + // Print nv -> ... -> n1 -> n. + var l *NodeList + for l = initlist; l.N != nv; l = l.Next { + } + for ; l != nil; l = l.End { + fmt.Printf("\t%v %v refers to\n", l.N.Line(), l.N.Sym) + } + + // Print n -> ... -> nv. + for l = initlist; l.N != n; l = l.Next { + } + for ; l.N != nv; l = l.End { + fmt.Printf("\t%v %v refers to\n", l.N.Line(), l.N.Sym) + } + fmt.Printf("\t%v %v\n", nv.Line(), nv.Sym) + errorexit() + } + + // reached a new unvisited node. + n.Initorder = InitPending + + l := new(NodeList) + if l == nil { + Flusherrors() + Yyerror("out of memory") + errorexit() + } + + l.Next = initlist + l.N = n + l.End = nil + initlist = l + + // make sure that everything n depends on is initialized. + // n->defn is an assignment to n + if n.Defn != nil { + switch n.Defn.Op { + default: + goto bad + + case ODCLFUNC: + init2list(n.Defn.Nbody, out) + + case OAS: + if n.Defn.Left != n { + goto bad + } + if isblank(n.Defn.Left) && candiscard(n.Defn.Right) { + n.Defn.Op = OEMPTY + n.Defn.Left = nil + n.Defn.Right = nil + break + } + + init2(n.Defn.Right, out) + if Debug['j'] != 0 { + fmt.Printf("%v\n", n.Sym) + } + if isblank(n) || !staticinit(n, out) { + if Debug['%'] != 0 { + Dump("nonstatic", n.Defn) + } + *out = list(*out, n.Defn) + } + + case OAS2FUNC, OAS2MAPR, OAS2DOTTYPE, OAS2RECV: + if n.Defn.Initorder != InitNotStarted { + break + } + n.Defn.Initorder = InitDone + for l := n.Defn.Rlist; l != nil; l = l.Next { + init1(l.N, out) + } + if Debug['%'] != 0 { + Dump("nonstatic", n.Defn) + } + *out = list(*out, n.Defn) + } + } + + l = initlist + initlist = l.Next + if l.N != n { + Fatal("bad initlist") + } + + n.Initorder = InitDone + return + +bad: + Dump("defn", n.Defn) + Fatal("init1: bad defn") +} + +// recurse over n, doing init1 everywhere. +func init2(n *Node, out **NodeList) { + if n == nil || n.Initorder == InitDone { + return + } + + if n.Op == ONAME && n.Ninit != nil { + Fatal("name %v with ninit: %v\n", n.Sym, Nconv(n, obj.FmtSign)) + } + + init1(n, out) + init2(n.Left, out) + init2(n.Right, out) + init2(n.Ntest, out) + init2list(n.Ninit, out) + init2list(n.List, out) + init2list(n.Rlist, out) + init2list(n.Nbody, out) + init2list(n.Nelse, out) + + if n.Op == OCLOSURE { + init2list(n.Closure.Nbody, out) + } + if n.Op == ODOTMETH || n.Op == OCALLPART { + init2(n.Type.Nname, out) + } +} + +func init2list(l *NodeList, out **NodeList) { + for ; l != nil; l = l.Next { + init2(l.N, out) + } +} + +func initreorder(l *NodeList, out **NodeList) { + var n *Node + + for ; l != nil; l = l.Next { + n = l.N + switch n.Op { + case ODCLFUNC, ODCLCONST, ODCLTYPE: + continue + } + + initreorder(n.Ninit, out) + n.Ninit = nil + init1(n, out) + } +} + +// initfix computes initialization order for a list l of top-level +// declarations and outputs the corresponding list of statements +// to include in the init() function body. +func initfix(l *NodeList) *NodeList { + var lout *NodeList + lno := int(lineno) + initreorder(l, &lout) + lineno = int32(lno) + return lout +} + +/* + * compilation of top-level (static) assignments + * into DATA statements if at all possible. + */ +func staticinit(n *Node, out **NodeList) bool { + if n.Op != ONAME || n.Class != PEXTERN || n.Defn == nil || n.Defn.Op != OAS { + Fatal("staticinit") + } + + lineno = n.Lineno + l := n.Defn.Left + r := n.Defn.Right + return staticassign(l, r, out) +} + +// like staticassign but we are copying an already +// initialized value r. +func staticcopy(l *Node, r *Node, out **NodeList) bool { + if r.Op != ONAME { + return false + } + if r.Class == PFUNC { + gdata(l, r, Widthptr) + return true + } + if r.Class != PEXTERN || r.Sym.Pkg != localpkg { + return false + } + if r.Defn == nil { // probably zeroed but perhaps supplied externally and of unknown value + return false + } + if r.Defn.Op != OAS { + return false + } + orig := r + r = r.Defn.Right + + switch r.Op { + case ONAME: + if staticcopy(l, r, out) { + return true + } + *out = list(*out, Nod(OAS, l, r)) + return true + + case OLITERAL: + if iszero(r) { + return true + } + gdata(l, r, int(l.Type.Width)) + return true + + case OADDR: + switch r.Left.Op { + case ONAME: + gdata(l, r, int(l.Type.Width)) + return true + } + + case OPTRLIT: + switch r.Left.Op { + //dump("not static addr", r); + default: + break + + // copy pointer + case OARRAYLIT, OSTRUCTLIT, OMAPLIT: + gdata(l, Nod(OADDR, r.Nname, nil), int(l.Type.Width)) + + return true + } + + case OARRAYLIT: + if Isslice(r.Type) { + // copy slice + a := r.Nname + + n1 := *l + n1.Xoffset = l.Xoffset + int64(Array_array) + gdata(&n1, Nod(OADDR, a, nil), Widthptr) + n1.Xoffset = l.Xoffset + int64(Array_nel) + gdata(&n1, r.Right, Widthint) + n1.Xoffset = l.Xoffset + int64(Array_cap) + gdata(&n1, r.Right, Widthint) + return true + } + fallthrough + + // fall through + case OSTRUCTLIT: + p := r.Initplan + + n1 := *l + var e *InitEntry + var ll *Node + var rr *Node + for i := 0; i < len(p.E); i++ { + e = &p.E[i] + n1.Xoffset = l.Xoffset + e.Xoffset + n1.Type = e.Expr.Type + if e.Expr.Op == OLITERAL { + gdata(&n1, e.Expr, int(n1.Type.Width)) + } else { + ll = Nod(OXXX, nil, nil) + *ll = n1 + ll.Orig = ll // completely separate copy + if !staticassign(ll, e.Expr, out) { + // Requires computation, but we're + // copying someone else's computation. + rr = Nod(OXXX, nil, nil) + + *rr = *orig + rr.Orig = rr // completely separate copy + rr.Type = ll.Type + rr.Xoffset += e.Xoffset + *out = list(*out, Nod(OAS, ll, rr)) + } + } + } + + return true + } + + return false +} + +func staticassign(l *Node, r *Node, out **NodeList) bool { + var n1 Node + + switch r.Op { + //dump("not static", r); + default: + break + + case ONAME: + return staticcopy(l, r, out) + + case OLITERAL: + if iszero(r) { + return true + } + gdata(l, r, int(l.Type.Width)) + return true + + case OADDR: + var nam Node + if stataddr(&nam, r.Left) { + n1 := *r + n1.Left = &nam + gdata(l, &n1, int(l.Type.Width)) + return true + } + fallthrough + + case OPTRLIT: + switch r.Left.Op { + //dump("not static ptrlit", r); + default: + break + + // Init pointer. + case OARRAYLIT, OMAPLIT, OSTRUCTLIT: + a := staticname(r.Left.Type, 1) + + r.Nname = a + gdata(l, Nod(OADDR, a, nil), int(l.Type.Width)) + + // Init underlying literal. + if !staticassign(a, r.Left, out) { + *out = list(*out, Nod(OAS, a, r.Left)) + } + return true + } + + case OSTRARRAYBYTE: + if l.Class == PEXTERN && r.Left.Op == OLITERAL { + sval := r.Left.Val.U.(string) + slicebytes(l, sval, len(sval)) + return true + } + + case OARRAYLIT: + initplan(r) + if Isslice(r.Type) { + // Init slice. + ta := typ(TARRAY) + + ta.Type = r.Type.Type + ta.Bound = Mpgetfix(r.Right.Val.U.(*Mpint)) + a := staticname(ta, 1) + r.Nname = a + n1 = *l + n1.Xoffset = l.Xoffset + int64(Array_array) + gdata(&n1, Nod(OADDR, a, nil), Widthptr) + n1.Xoffset = l.Xoffset + int64(Array_nel) + gdata(&n1, r.Right, Widthint) + n1.Xoffset = l.Xoffset + int64(Array_cap) + gdata(&n1, r.Right, Widthint) + + // Fall through to init underlying array. + l = a + } + fallthrough + + // fall through + case OSTRUCTLIT: + initplan(r) + + p := r.Initplan + n1 = *l + var e *InitEntry + var a *Node + for i := 0; i < len(p.E); i++ { + e = &p.E[i] + n1.Xoffset = l.Xoffset + e.Xoffset + n1.Type = e.Expr.Type + if e.Expr.Op == OLITERAL { + gdata(&n1, e.Expr, int(n1.Type.Width)) + } else { + a = Nod(OXXX, nil, nil) + *a = n1 + a.Orig = a // completely separate copy + if !staticassign(a, e.Expr, out) { + *out = list(*out, Nod(OAS, a, e.Expr)) + } + } + } + + return true + + // TODO: Table-driven map insert. + case OMAPLIT: + break + } + + return false +} + +/* + * from here down is the walk analysis + * of composite literals. + * most of the work is to generate + * data statements for the constant + * part of the composite literal. + */ +func staticname(t *Type, ctxt int) *Node { + n := newname(Lookupf("statictmp_%.4d", statuniqgen)) + statuniqgen++ + if ctxt == 0 { + n.Name.Readonly = true + } + addvar(n, t, PEXTERN) + return n +} + +func isliteral(n *Node) bool { + if n.Op == OLITERAL { + if n.Val.Ctype != CTNIL { + return true + } + } + return false +} + +func simplename(n *Node) bool { + if n.Op != ONAME { + return false + } + if !n.Addable { + return false + } + if n.Class&PHEAP != 0 { + return false + } + if n.Class == PPARAMREF { + return false + } + return true +} + +func litas(l *Node, r *Node, init **NodeList) { + a := Nod(OAS, l, r) + typecheck(&a, Etop) + walkexpr(&a, init) + *init = list(*init, a) +} + +const ( + MODEDYNAM = 1 + MODECONST = 2 +) + +func getdyn(n *Node, top int) int { + mode := 0 + switch n.Op { + default: + if isliteral(n) { + return MODECONST + } + return MODEDYNAM + + case OARRAYLIT: + if top == 0 && n.Type.Bound < 0 { + return MODEDYNAM + } + fallthrough + + case OSTRUCTLIT: + break + } + + var value *Node + for nl := n.List; nl != nil; nl = nl.Next { + value = nl.N.Right + mode |= getdyn(value, 0) + if mode == MODEDYNAM|MODECONST { + break + } + } + + return mode +} + +func structlit(ctxt int, pass int, n *Node, var_ *Node, init **NodeList) { + var r *Node + var a *Node + var index *Node + var value *Node + + for nl := n.List; nl != nil; nl = nl.Next { + r = nl.N + if r.Op != OKEY { + Fatal("structlit: rhs not OKEY: %v", r) + } + index = r.Left + value = r.Right + + switch value.Op { + case OARRAYLIT: + if value.Type.Bound < 0 { + if pass == 1 && ctxt != 0 { + a = Nod(ODOT, var_, newname(index.Sym)) + slicelit(ctxt, value, a, init) + } else if pass == 2 && ctxt == 0 { + a = Nod(ODOT, var_, newname(index.Sym)) + slicelit(ctxt, value, a, init) + } else if pass == 3 { + break + } + continue + } + + a = Nod(ODOT, var_, newname(index.Sym)) + arraylit(ctxt, pass, value, a, init) + continue + + case OSTRUCTLIT: + a = Nod(ODOT, var_, newname(index.Sym)) + structlit(ctxt, pass, value, a, init) + continue + } + + if isliteral(value) { + if pass == 2 { + continue + } + } else if pass == 1 { + continue + } + + // build list of var.field = expr + a = Nod(ODOT, var_, newname(index.Sym)) + + a = Nod(OAS, a, value) + typecheck(&a, Etop) + if pass == 1 { + walkexpr(&a, init) // add any assignments in r to top + if a.Op != OAS { + Fatal("structlit: not as") + } + a.Dodata = 2 + } else { + orderstmtinplace(&a) + walkstmt(&a) + } + + *init = list(*init, a) + } +} + +func arraylit(ctxt int, pass int, n *Node, var_ *Node, init **NodeList) { + var r *Node + var a *Node + var index *Node + var value *Node + + for l := n.List; l != nil; l = l.Next { + r = l.N + if r.Op != OKEY { + Fatal("arraylit: rhs not OKEY: %v", r) + } + index = r.Left + value = r.Right + + switch value.Op { + case OARRAYLIT: + if value.Type.Bound < 0 { + if pass == 1 && ctxt != 0 { + a = Nod(OINDEX, var_, index) + slicelit(ctxt, value, a, init) + } else if pass == 2 && ctxt == 0 { + a = Nod(OINDEX, var_, index) + slicelit(ctxt, value, a, init) + } else if pass == 3 { + break + } + continue + } + + a = Nod(OINDEX, var_, index) + arraylit(ctxt, pass, value, a, init) + continue + + case OSTRUCTLIT: + a = Nod(OINDEX, var_, index) + structlit(ctxt, pass, value, a, init) + continue + } + + if isliteral(index) && isliteral(value) { + if pass == 2 { + continue + } + } else if pass == 1 { + continue + } + + // build list of var[index] = value + a = Nod(OINDEX, var_, index) + + a = Nod(OAS, a, value) + typecheck(&a, Etop) + if pass == 1 { + walkexpr(&a, init) + if a.Op != OAS { + Fatal("arraylit: not as") + } + a.Dodata = 2 + } else { + orderstmtinplace(&a) + walkstmt(&a) + } + + *init = list(*init, a) + } +} + +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.(*Mpint)) + t.Width = 0 + t.Sym = nil + t.Haspointers = 0 + dowidth(t) + + if ctxt != 0 { + // put everything into static array + vstat := staticname(t, ctxt) + + arraylit(ctxt, 1, n, vstat, init) + arraylit(ctxt, 2, n, vstat, init) + + // copy static to slice + a := Nod(OSLICE, vstat, Nod(OKEY, nil, nil)) + + a = Nod(OAS, var_, a) + typecheck(&a, Etop) + a.Dodata = 2 + *init = list(*init, a) + return + } + + // recipe for var = []t{...} + // 1. make a static array + // var vstat [...]t + // 2. assign (data statements) the constant part + // vstat = constpart{} + // 3. make an auto pointer to array and allocate heap to it + // var vauto *[...]t = new([...]t) + // 4. copy the static array to the auto array + // *vauto = vstat + // 5. assign slice of allocated heap to var + // var = [0:]*auto + // 6. for each dynamic part assign to the slice + // var[i] = dynamic part + // + // an optimization is done if there is no constant part + // 3. var vauto *[...]t = new([...]t) + // 5. var = [0:]*auto + // 6. var[i] = dynamic part + + // if the literal contains constants, + // make static initialized array (1),(2) + var vstat *Node + + mode := getdyn(n, 1) + if mode&MODECONST != 0 { + vstat = staticname(t, ctxt) + arraylit(ctxt, 1, n, vstat, init) + } + + // make new auto *array (3 declare) + vauto := temp(Ptrto(t)) + + // set auto to point at new temp or heap (3 assign) + var a *Node + if n.Alloc != nil { + // temp allocated during order.c for dddarg + n.Alloc.Type = t + + if vstat == nil { + a = Nod(OAS, n.Alloc, nil) + typecheck(&a, Etop) + *init = list(*init, a) // zero new temp + } + + a = Nod(OADDR, n.Alloc, nil) + } else if n.Esc == EscNone { + a = temp(t) + if vstat == nil { + a = Nod(OAS, temp(t), nil) + typecheck(&a, Etop) + *init = list(*init, a) // zero new temp + a = a.Left + } + + a = Nod(OADDR, a, nil) + } else { + a = Nod(ONEW, nil, nil) + a.List = list1(typenod(t)) + } + + a = Nod(OAS, vauto, a) + typecheck(&a, Etop) + walkexpr(&a, init) + *init = list(*init, a) + + if vstat != nil { + // copy static to heap (4) + a = Nod(OIND, vauto, nil) + + a = Nod(OAS, a, vstat) + typecheck(&a, Etop) + walkexpr(&a, init) + *init = list(*init, a) + } + + // make slice out of heap (5) + a = Nod(OAS, var_, Nod(OSLICE, vauto, Nod(OKEY, nil, nil))) + + typecheck(&a, Etop) + orderstmtinplace(&a) + walkstmt(&a) + *init = list(*init, a) + + // put dynamics into slice (6) + var value *Node + var r *Node + var index *Node + for l := n.List; l != nil; l = l.Next { + r = l.N + if r.Op != OKEY { + Fatal("slicelit: rhs not OKEY: %v", r) + } + index = r.Left + value = r.Right + a = Nod(OINDEX, var_, index) + a.Bounded = true + + // TODO need to check bounds? + + switch value.Op { + case OARRAYLIT: + if value.Type.Bound < 0 { + break + } + arraylit(ctxt, 2, value, a, init) + continue + + case OSTRUCTLIT: + structlit(ctxt, 2, value, a, init) + continue + } + + if isliteral(index) && isliteral(value) { + continue + } + + // build list of var[c] = expr + a = Nod(OAS, a, value) + + typecheck(&a, Etop) + orderstmtinplace(&a) + walkstmt(&a) + *init = list(*init, a) + } +} + +func maplit(ctxt int, n *Node, var_ *Node, init **NodeList) { + var r *Node + var index *Node + var value *Node + + ctxt = 0 + + // make the map var + nerr := nerrors + + a := Nod(OMAKE, nil, nil) + a.List = list1(typenod(n.Type)) + litas(var_, a, init) + + // count the initializers + b := int64(0) + + for l := n.List; l != nil; l = l.Next { + r = l.N + + if r.Op != OKEY { + Fatal("maplit: rhs not OKEY: %v", r) + } + index = r.Left + value = r.Right + + if isliteral(index) && isliteral(value) { + b++ + } + } + + if b != 0 { + // build type [count]struct { a Tindex, b Tvalue } + t := n.Type + + tk := t.Down + tv := t.Type + + symb := Lookup("b") + t = typ(TFIELD) + t.Type = tv + t.Sym = symb + + syma := Lookup("a") + t1 := t + t = typ(TFIELD) + t.Type = tk + t.Sym = syma + t.Down = t1 + + t1 = t + t = typ(TSTRUCT) + t.Type = t1 + + t1 = t + t = typ(TARRAY) + t.Bound = b + t.Type = t1 + + dowidth(t) + + // make and initialize static array + vstat := staticname(t, ctxt) + + b := int64(0) + var index *Node + var r *Node + var value *Node + for l := n.List; l != nil; l = l.Next { + r = l.N + + if r.Op != OKEY { + Fatal("maplit: rhs not OKEY: %v", r) + } + index = r.Left + value = r.Right + + if isliteral(index) && isliteral(value) { + // build vstat[b].a = key; + a = Nodintconst(b) + + a = Nod(OINDEX, vstat, a) + a = Nod(ODOT, a, newname(syma)) + a = Nod(OAS, a, index) + typecheck(&a, Etop) + walkexpr(&a, init) + a.Dodata = 2 + *init = list(*init, a) + + // build vstat[b].b = value; + a = Nodintconst(b) + + a = Nod(OINDEX, vstat, a) + a = Nod(ODOT, a, newname(symb)) + a = Nod(OAS, a, value) + typecheck(&a, Etop) + walkexpr(&a, init) + a.Dodata = 2 + *init = list(*init, a) + + b++ + } + } + + // loop adding structure elements to map + // for i = 0; i < len(vstat); i++ { + // map[vstat[i].a] = vstat[i].b + // } + index = temp(Types[TINT]) + + a = Nod(OINDEX, vstat, index) + a.Bounded = true + a = Nod(ODOT, a, newname(symb)) + + r = Nod(OINDEX, vstat, index) + r.Bounded = true + r = Nod(ODOT, r, newname(syma)) + r = Nod(OINDEX, var_, r) + + r = Nod(OAS, r, a) + + a = Nod(OFOR, nil, nil) + a.Nbody = list1(r) + + a.Ninit = list1(Nod(OAS, index, Nodintconst(0))) + a.Ntest = Nod(OLT, index, Nodintconst(t.Bound)) + a.Nincr = Nod(OAS, index, Nod(OADD, index, Nodintconst(1))) + + typecheck(&a, Etop) + walkstmt(&a) + *init = list(*init, a) + } + + // put in dynamic entries one-at-a-time + var key *Node + + var val *Node + for l := n.List; l != nil; l = l.Next { + r = l.N + + if r.Op != OKEY { + Fatal("maplit: rhs not OKEY: %v", r) + } + index = r.Left + value = r.Right + + if isliteral(index) && isliteral(value) { + continue + } + + // build list of var[c] = expr. + // use temporary so that mapassign1 can have addressable key, val. + if key == nil { + key = temp(var_.Type.Down) + val = temp(var_.Type.Type) + } + + a = Nod(OAS, key, r.Left) + typecheck(&a, Etop) + walkstmt(&a) + *init = list(*init, a) + a = Nod(OAS, val, r.Right) + typecheck(&a, Etop) + walkstmt(&a) + *init = list(*init, a) + + a = Nod(OAS, Nod(OINDEX, var_, key), val) + typecheck(&a, Etop) + walkstmt(&a) + *init = list(*init, a) + + if nerr != nerrors { + break + } + } + + if key != nil { + a = Nod(OVARKILL, key, nil) + typecheck(&a, Etop) + *init = list(*init, a) + a = Nod(OVARKILL, val, nil) + typecheck(&a, Etop) + *init = list(*init, a) + } +} + +func anylit(ctxt int, n *Node, var_ *Node, init **NodeList) { + t := n.Type + switch n.Op { + default: + Fatal("anylit: not lit") + + case OPTRLIT: + if !Isptr[t.Etype] { + Fatal("anylit: not ptr") + } + + var r *Node + if n.Right != nil { + r = Nod(OADDR, n.Right, nil) + typecheck(&r, Erv) + } else { + r = Nod(ONEW, nil, nil) + r.Typecheck = 1 + r.Type = t + r.Esc = n.Esc + } + + walkexpr(&r, init) + a := Nod(OAS, var_, r) + + typecheck(&a, Etop) + *init = list(*init, a) + + var_ = Nod(OIND, var_, nil) + typecheck(&var_, Erv|Easgn) + anylit(ctxt, n.Left, var_, init) + + case OSTRUCTLIT: + if t.Etype != TSTRUCT { + Fatal("anylit: not struct") + } + + if simplename(var_) && count(n.List) > 4 { + if ctxt == 0 { + // lay out static data + vstat := staticname(t, ctxt) + + structlit(ctxt, 1, n, vstat, init) + + // copy static to var + a := Nod(OAS, var_, vstat) + + typecheck(&a, Etop) + walkexpr(&a, init) + *init = list(*init, a) + + // add expressions to automatic + structlit(ctxt, 2, n, var_, init) + + break + } + + structlit(ctxt, 1, n, var_, init) + structlit(ctxt, 2, n, var_, init) + break + } + + // initialize of not completely specified + if simplename(var_) || count(n.List) < structcount(t) { + a := Nod(OAS, var_, nil) + typecheck(&a, Etop) + walkexpr(&a, init) + *init = list(*init, a) + } + + structlit(ctxt, 3, n, var_, init) + + case OARRAYLIT: + if t.Etype != TARRAY { + Fatal("anylit: not array") + } + if t.Bound < 0 { + slicelit(ctxt, n, var_, init) + break + } + + if simplename(var_) && count(n.List) > 4 { + if ctxt == 0 { + // lay out static data + vstat := staticname(t, ctxt) + + arraylit(1, 1, n, vstat, init) + + // copy static to automatic + a := Nod(OAS, var_, vstat) + + typecheck(&a, Etop) + walkexpr(&a, init) + *init = list(*init, a) + + // add expressions to automatic + arraylit(ctxt, 2, n, var_, init) + + break + } + + arraylit(ctxt, 1, n, var_, init) + arraylit(ctxt, 2, n, var_, init) + break + } + + // initialize of not completely specified + if simplename(var_) || int64(count(n.List)) < t.Bound { + a := Nod(OAS, var_, nil) + typecheck(&a, Etop) + walkexpr(&a, init) + *init = list(*init, a) + } + + arraylit(ctxt, 3, n, var_, init) + + case OMAPLIT: + if t.Etype != TMAP { + Fatal("anylit: not map") + } + maplit(ctxt, n, var_, init) + } +} + +func oaslit(n *Node, init **NodeList) bool { + if n.Left == nil || n.Right == nil { + // not a special composit literal assignment + return false + } + if n.Left.Type == nil || n.Right.Type == nil { + // not a special composit literal assignment + return false + } + if !simplename(n.Left) { + // not a special composit literal assignment + return false + } + if !Eqtype(n.Left.Type, n.Right.Type) { + // not a special composit literal assignment + return false + } + + // context is init() function. + // implies generated data executed + // exactly once and not subject to races. + ctxt := 0 + + // if(n->dodata == 1) + // ctxt = 1; + + switch n.Right.Op { + default: + // not a special composit literal assignment + return false + + case OSTRUCTLIT, OARRAYLIT, OMAPLIT: + if vmatch1(n.Left, n.Right) { + // not a special composit literal assignment + return false + } + anylit(ctxt, n.Right, n.Left, init) + } + + n.Op = OEMPTY + return true +} + +func getlit(lit *Node) int { + if Smallintconst(lit) { + return int(Mpgetfix(lit.Val.U.(*Mpint))) + } + return -1 +} + +func stataddr(nam *Node, n *Node) bool { + if n == nil { + return false + } + + switch n.Op { + case ONAME: + *nam = *n + return n.Addable + + case ODOT: + if !stataddr(nam, n.Left) { + break + } + nam.Xoffset += n.Xoffset + nam.Type = n.Type + return true + + case OINDEX: + if n.Left.Type.Bound < 0 { + break + } + if !stataddr(nam, n.Left) { + break + } + l := getlit(n.Right) + if l < 0 { + break + } + + // Check for overflow. + if n.Type.Width != 0 && Thearch.MAXWIDTH/n.Type.Width <= int64(l) { + break + } + nam.Xoffset += int64(l) * n.Type.Width + nam.Type = n.Type + return true + } + + return false +} + +func initplan(n *Node) { + if n.Initplan != nil { + return + } + p := new(InitPlan) + n.Initplan = p + switch n.Op { + default: + Fatal("initplan") + + case OARRAYLIT: + var a *Node + for l := n.List; l != nil; l = l.Next { + a = l.N + if a.Op != OKEY || !Smallintconst(a.Left) { + Fatal("initplan arraylit") + } + addvalue(p, n.Type.Type.Width*Mpgetfix(a.Left.Val.U.(*Mpint)), nil, a.Right) + } + + case OSTRUCTLIT: + var a *Node + for l := n.List; l != nil; l = l.Next { + a = l.N + if a.Op != OKEY || a.Left.Type == nil { + Fatal("initplan structlit") + } + addvalue(p, a.Left.Type.Width, nil, a.Right) + } + + case OMAPLIT: + var a *Node + for l := n.List; l != nil; l = l.Next { + a = l.N + if a.Op != OKEY { + Fatal("initplan maplit") + } + addvalue(p, -1, a.Left, a.Right) + } + } +} + +func addvalue(p *InitPlan, xoffset int64, key *Node, n *Node) { + // special case: zero can be dropped entirely + if iszero(n) { + p.Zero += n.Type.Width + return + } + + // special case: inline struct and array (not slice) literals + if isvaluelit(n) { + initplan(n) + q := n.Initplan + var e *InitEntry + for i := 0; i < len(q.E); i++ { + e = entry(p) + *e = q.E[i] + e.Xoffset += xoffset + } + + return + } + + // add to plan + if n.Op == OLITERAL { + p.Lit += n.Type.Width + } else { + p.Expr += n.Type.Width + } + + e := entry(p) + e.Xoffset = xoffset + e.Expr = n +} + +func iszero(n *Node) bool { + switch n.Op { + case OLITERAL: + switch n.Val.Ctype { + default: + Dump("unexpected literal", n) + Fatal("iszero") + + case CTNIL: + return true + + case CTSTR: + return n.Val.U.(string) == "" + + case CTBOOL: + return !n.Val.U.(bool) + + case CTINT, CTRUNE: + return mpcmpfixc(n.Val.U.(*Mpint), 0) == 0 + + case CTFLT: + return mpcmpfltc(n.Val.U.(*Mpflt), 0) == 0 + + case CTCPLX: + return mpcmpfltc(&n.Val.U.(*Mpcplx).Real, 0) == 0 && mpcmpfltc(&n.Val.U.(*Mpcplx).Imag, 0) == 0 + } + + case OARRAYLIT: + if Isslice(n.Type) { + break + } + fallthrough + + // fall through + case OSTRUCTLIT: + for l := n.List; l != nil; l = l.Next { + if !iszero(l.N.Right) { + return false + } + } + return true + } + + return false +} + +func isvaluelit(n *Node) bool { + return (n.Op == OARRAYLIT && Isfixedarray(n.Type)) || n.Op == OSTRUCTLIT +} + +func entry(p *InitPlan) *InitEntry { + p.E = append(p.E, InitEntry{}) + return &p.E[len(p.E)-1] +} + +func gen_as_init(n *Node) bool { + var nr *Node + var nl *Node + var nam Node + + if n.Dodata == 0 { + goto no + } + + nr = n.Right + nl = n.Left + if nr == nil { + var nam Node + if !stataddr(&nam, nl) { + goto no + } + if nam.Class != PEXTERN { + goto no + } + return true + } + + if nr.Type == nil || !Eqtype(nl.Type, nr.Type) { + goto no + } + + if !stataddr(&nam, nl) { + goto no + } + + if nam.Class != PEXTERN { + goto no + } + + switch nr.Op { + default: + goto no + + case OCONVNOP: + nr = nr.Left + if nr == nil || nr.Op != OSLICEARR { + goto no + } + fallthrough + + // fall through + case OSLICEARR: + if nr.Right.Op == OKEY && nr.Right.Left == nil && nr.Right.Right == nil { + nr = nr.Left + gused(nil) // in case the data is the dest of a goto + nl := nr + if nr == nil || nr.Op != OADDR { + goto no + } + nr = nr.Left + if nr == nil || nr.Op != ONAME { + goto no + } + + // nr is the array being converted to a slice + if nr.Type == nil || nr.Type.Etype != TARRAY || nr.Type.Bound < 0 { + goto no + } + + nam.Xoffset += int64(Array_array) + gdata(&nam, nl, int(Types[Tptr].Width)) + + nam.Xoffset += int64(Array_nel) - int64(Array_array) + var nod1 Node + Nodconst(&nod1, Types[TINT], nr.Type.Bound) + gdata(&nam, &nod1, Widthint) + + nam.Xoffset += int64(Array_cap) - int64(Array_nel) + gdata(&nam, &nod1, Widthint) + + return true + } + + goto no + + case OLITERAL: + break + } + + switch nr.Type.Etype { + default: + goto no + + case TBOOL, + TINT8, + TUINT8, + TINT16, + TUINT16, + TINT32, + TUINT32, + TINT64, + TUINT64, + TINT, + TUINT, + TUINTPTR, + TPTR32, + TPTR64, + TFLOAT32, + TFLOAT64: + gdata(&nam, nr, int(nr.Type.Width)) + + case TCOMPLEX64, TCOMPLEX128: + gdatacomplex(&nam, nr.Val.U.(*Mpcplx)) + + case TSTRING: + gdatastring(&nam, nr.Val.U.(string)) + } + + return true + +no: + if n.Dodata == 2 { + Dump("\ngen_as_init", n) + Fatal("gen_as_init couldnt make data statement") + } + + return false +} diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go new file mode 100644 index 0000000000..b10a6b3d3d --- /dev/null +++ b/src/cmd/compile/internal/gc/subr.go @@ -0,0 +1,3571 @@ +// Copyright 2009 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 gc + +import ( + "bytes" + "cmd/internal/obj" + "crypto/md5" + "encoding/binary" + "fmt" + "os" + "sort" + "strings" + "unicode" + "unicode/utf8" +) + +type Error struct { + lineno int + seq int + msg string +} + +var errors []Error + +func errorexit() { + Flusherrors() + if outfile != "" { + os.Remove(outfile) + } + os.Exit(2) +} + +func parserline() int { + if parsing && theparser.Lookahead() > 0 { + // parser has one symbol lookahead + return int(prevlineno) + } + return int(lineno) +} + +func adderrorname(n *Node) { + if n.Op != ODOT { + return + } + old := fmt.Sprintf("%v: undefined: %v\n", n.Line(), n.Left) + if len(errors) > 0 && int32(errors[len(errors)-1].lineno) == n.Lineno && errors[len(errors)-1].msg == old { + errors[len(errors)-1].msg = fmt.Sprintf("%v: undefined: %v in %v\n", n.Line(), n.Left, n) + } +} + +func adderr(line int, format string, args ...interface{}) { + errors = append(errors, Error{ + seq: len(errors), + lineno: line, + msg: fmt.Sprintf("%v: %s\n", Ctxt.Line(line), fmt.Sprintf(format, args...)), + }) +} + +type errcmp []Error + +func (x errcmp) Len() int { + return len(x) +} + +func (x errcmp) Swap(i, j int) { + x[i], x[j] = x[j], x[i] +} + +func (x errcmp) Less(i, j int) bool { + a := &x[i] + b := &x[j] + if a.lineno != b.lineno { + return a.lineno-b.lineno < 0 + } + if a.seq != b.seq { + return a.seq-b.seq < 0 + } + return stringsCompare(a.msg, b.msg) < 0 +} + +func Flusherrors() { + bstdout.Flush() + if len(errors) == 0 { + return + } + sort.Sort(errcmp(errors[:len(errors)])) + for i := 0; i < len(errors); i++ { + if i == 0 || errors[i].msg != errors[i-1].msg { + fmt.Printf("%s", errors[i].msg) + } + } + errors = errors[:0] +} + +func hcrash() { + if Debug['h'] != 0 { + Flusherrors() + if outfile != "" { + os.Remove(outfile) + } + var x *int + *x = 0 + } +} + +func yyerrorl(line int, format string, args ...interface{}) { + adderr(line, format, args...) + + hcrash() + nerrors++ + if nsavederrors+nerrors >= 10 && Debug['e'] == 0 { + Flusherrors() + fmt.Printf("%v: too many errors\n", Ctxt.Line(line)) + errorexit() + } +} + +var yyerror_lastsyntax int + +func Yyerror(format string, args ...interface{}) { + msg := fmt.Sprintf(format, args...) + if strings.HasPrefix(msg, "syntax error") { + nsyntaxerrors++ + + // An unexpected EOF caused a syntax error. Use the previous + // line number since getc generated a fake newline character. + if curio.eofnl != 0 { + lexlineno = prevlineno + } + + // only one syntax error per line + if int32(yyerror_lastsyntax) == lexlineno { + return + } + yyerror_lastsyntax = int(lexlineno) + + // plain "syntax error" gets "near foo" added + if msg == "syntax error" { + yyerrorl(int(lexlineno), "syntax error near %s", lexbuf.String()) + return + } + + // The grammar has { and LBRACE but both show up as {. + // Rewrite syntax error referring to "{ or {" to say just "{". + // The grammar has ? and @ but only for reading imports. + // Silence them in ordinary errors. + msg = strings.Replace(msg, "{ or {", "{", -1) + msg = strings.Replace(msg, " or ?", "", -1) + msg = strings.Replace(msg, " or @", "", -1) + + msg = strings.Replace(msg, "LLITERAL", litbuf, -1) + + yyerrorl(int(lexlineno), "%s", msg) + return + } + + adderr(parserline(), "%s", msg) + + hcrash() + nerrors++ + if nsavederrors+nerrors >= 10 && Debug['e'] == 0 { + Flusherrors() + fmt.Printf("%v: too many errors\n", Ctxt.Line(parserline())) + errorexit() + } +} + +func Warn(fmt_ string, args ...interface{}) { + adderr(parserline(), fmt_, args...) + + hcrash() +} + +func Warnl(line int, fmt_ string, args ...interface{}) { + adderr(line, fmt_, args...) + if Debug['m'] != 0 { + Flusherrors() + } +} + +func Fatal(fmt_ string, args ...interface{}) { + Flusherrors() + + fmt.Printf("%v: internal compiler error: ", Ctxt.Line(int(lineno))) + fmt.Printf(fmt_, args...) + fmt.Printf("\n") + + // If this is a released compiler version, ask for a bug report. + if strings.HasPrefix(obj.Getgoversion(), "release") { + fmt.Printf("\n") + fmt.Printf("Please file a bug report including a short program that triggers the error.\n") + fmt.Printf("https://golang.org/issue/new\n") + } + + hcrash() + errorexit() +} + +func linehistpragma(file string) { + if Debug['i'] != 0 { + fmt.Printf("pragma %s at line %v\n", file, Ctxt.Line(int(lexlineno))) + } + Ctxt.AddImport(file) +} + +func linehistpush(file string) { + if Debug['i'] != 0 { + fmt.Printf("import %s at line %v\n", file, Ctxt.Line(int(lexlineno))) + } + 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 { + lno := lineno + if n != nil { + switch n.Op { + case ONAME, OTYPE, OPACK, OLITERAL: + break + + default: + lineno = n.Lineno + if lineno == 0 { + if Debug['K'] != 0 { + Warn("setlineno: line 0") + } + lineno = lno + } + } + } + + return lno +} + +func Lookup(name string) *Sym { + return localpkg.Lookup(name) +} + +func Lookupf(format string, a ...interface{}) *Sym { + return Lookup(fmt.Sprintf(format, a...)) +} + +func LookupBytes(name []byte) *Sym { + return localpkg.LookupBytes(name) +} + +var initSyms []*Sym + +var nopkg = &Pkg{ + Syms: make(map[string]*Sym), +} + +func (pkg *Pkg) Lookup(name string) *Sym { + if pkg == nil { + pkg = nopkg + } + if s := pkg.Syms[name]; s != nil { + return s + } + + s := &Sym{ + Name: name, + Pkg: pkg, + Lexical: LNAME, + } + if name == "init" { + initSyms = append(initSyms, s) + } + pkg.Syms[name] = s + return s +} + +func (pkg *Pkg) LookupBytes(name []byte) *Sym { + if pkg == nil { + pkg = nopkg + } + if s := pkg.Syms[string(name)]; s != nil { + return s + } + str := internString(name) + return pkg.Lookup(str) +} + +func Pkglookup(name string, pkg *Pkg) *Sym { + return pkg.Lookup(name) +} + +func restrictlookup(name string, pkg *Pkg) *Sym { + if !exportname(name) && pkg != localpkg { + Yyerror("cannot refer to unexported name %s.%s", pkg.Name, name) + } + return Pkglookup(name, pkg) +} + +// find all the exported symbols in package opkg +// and make them available in the current package +func importdot(opkg *Pkg, pack *Node) { + var s1 *Sym + var pkgerror string + + n := 0 + for _, s := range opkg.Syms { + if s.Def == nil { + continue + } + if !exportname(s.Name) || strings.ContainsRune(s.Name, 0xb7) { // 0xb7 = center dot + continue + } + s1 = Lookup(s.Name) + if s1.Def != nil { + pkgerror = fmt.Sprintf("during import %q", opkg.Path) + redeclare(s1, pkgerror) + continue + } + + s1.Def = s.Def + s1.Block = s.Block + s1.Def.Pack = pack + s1.Origpkg = opkg + n++ + } + + if n == 0 { + // can't possibly be used - there were no symbols + yyerrorl(int(pack.Lineno), "imported and not used: %q", opkg.Path) + } +} + +func gethunk() { + nh := int32(NHUNK) + if thunk >= 10*NHUNK { + nh = 10 * NHUNK + } + h := string(make([]byte, nh)) + if h == "" { + Flusherrors() + Yyerror("out of memory") + errorexit() + } + + hunk = h + nhunk = nh + thunk += nh +} + +func Nod(op int, nleft *Node, nright *Node) *Node { + n := new(Node) + n.Op = uint8(op) + n.Left = nleft + n.Right = nright + n.Lineno = int32(parserline()) + n.Xoffset = BADWIDTH + n.Orig = n + n.Curfn = Curfn + 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 +} + +func saveorignode(n *Node) { + if n.Orig != nil { + return + } + norig := Nod(int(n.Op), nil, nil) + *norig = *n + n.Orig = norig +} + +// ispaddedfield reports whether the given field +// is followed by padding. For the case where t is +// the last field, total gives the size of the enclosing struct. +func ispaddedfield(t *Type, total int64) bool { + if t.Etype != TFIELD { + Fatal("ispaddedfield called non-field %v", t) + } + if t.Down == nil { + return t.Width+t.Type.Width != total + } + return t.Width+t.Type.Width != t.Down.Width +} + +func algtype1(t *Type, bad **Type) int { + if bad != nil { + *bad = nil + } + if t.Broke != 0 { + return AMEM + } + if t.Noalg != 0 { + return ANOEQ + } + + switch t.Etype { + // will be defined later. + case TANY, TFORW: + *bad = t + + return -1 + + case TINT8, + TUINT8, + TINT16, + TUINT16, + TINT32, + TUINT32, + TINT64, + TUINT64, + TINT, + TUINT, + TUINTPTR, + TBOOL, + TPTR32, + TPTR64, + TCHAN, + TUNSAFEPTR: + return AMEM + + case TFUNC, TMAP: + if bad != nil { + *bad = t + } + return ANOEQ + + case TFLOAT32: + return AFLOAT32 + + case TFLOAT64: + return AFLOAT64 + + case TCOMPLEX64: + return ACPLX64 + + case TCOMPLEX128: + return ACPLX128 + + case TSTRING: + return ASTRING + + case TINTER: + if isnilinter(t) { + return ANILINTER + } + return AINTER + + case TARRAY: + if Isslice(t) { + if bad != nil { + *bad = t + } + return ANOEQ + } + + a := algtype1(t.Type, bad) + if a == ANOEQ || a == AMEM { + if a == ANOEQ && bad != nil { + *bad = t + } + return a + } + + return -1 // needs special compare + + case TSTRUCT: + if t.Type != nil && t.Type.Down == nil && !isblanksym(t.Type.Sym) { + // One-field struct is same as that one field alone. + return algtype1(t.Type.Type, bad) + } + + ret := AMEM + var a int + for t1 := t.Type; t1 != nil; t1 = t1.Down { + // All fields must be comparable. + a = algtype1(t1.Type, bad) + + if a == ANOEQ { + return ANOEQ + } + + // Blank fields, padded fields, fields with non-memory + // equality need special compare. + if a != AMEM || isblanksym(t1.Sym) || ispaddedfield(t1, t.Width) { + ret = -1 + continue + } + } + + return ret + } + + Fatal("algtype1: unexpected type %v", t) + return 0 +} + +func algtype(t *Type) int { + a := algtype1(t, nil) + if a == AMEM || a == ANOEQ { + if Isslice(t) { + return ASLICE + } + switch t.Width { + case 0: + return a + AMEM0 - AMEM + + case 1: + return a + AMEM8 - AMEM + + case 2: + return a + AMEM16 - AMEM + + case 4: + return a + AMEM32 - AMEM + + case 8: + return a + AMEM64 - AMEM + + case 16: + return a + AMEM128 - AMEM + } + } + + return a +} + +func maptype(key *Type, val *Type) *Type { + if key != nil { + var bad *Type + atype := algtype1(key, &bad) + var mtype int + if bad == nil { + mtype = int(key.Etype) + } else { + mtype = int(bad.Etype) + } + switch mtype { + default: + if atype == ANOEQ { + Yyerror("invalid map key type %v", key) + } + + // will be resolved later. + case TANY: + break + + // map[key] used during definition of key. + // postpone check until key is fully defined. + // if there are multiple uses of map[key] + // before key is fully defined, the error + // will only be printed for the first one. + // good enough. + case TFORW: + if key.Maplineno == 0 { + key.Maplineno = lineno + } + } + } + + t := typ(TMAP) + t.Down = key + t.Type = val + return t +} + +func typ(et int) *Type { + t := new(Type) + t.Etype = uint8(et) + t.Width = BADWIDTH + t.Lineno = int(lineno) + t.Orig = t + return t +} + +type methcmp []*Type + +func (x methcmp) Len() int { + return len(x) +} + +func (x methcmp) Swap(i, j int) { + x[i], x[j] = x[j], x[i] +} + +func (x methcmp) Less(i, j int) bool { + a := x[i] + b := x[j] + if a.Sym == nil && b.Sym == nil { + return false + } + if a.Sym == nil { + return true + } + if b.Sym == nil { + return 1 < 0 + } + k := stringsCompare(a.Sym.Name, b.Sym.Name) + if k != 0 { + return k < 0 + } + if !exportname(a.Sym.Name) { + k := stringsCompare(a.Sym.Pkg.Path, b.Sym.Pkg.Path) + if k != 0 { + return k < 0 + } + } + + return false +} + +func sortinter(t *Type) *Type { + if t.Type == nil || t.Type.Down == nil { + return t + } + + i := 0 + for f := t.Type; f != nil; f = f.Down { + i++ + } + a := make([]*Type, i) + i = 0 + var f *Type + for f = t.Type; f != nil; f = f.Down { + a[i] = f + i++ + } + sort.Sort(methcmp(a[:i])) + for { + tmp11 := i + i-- + if tmp11 <= 0 { + break + } + a[i].Down = f + f = a[i] + } + + t.Type = f + return t +} + +func Nodintconst(v int64) *Node { + c := Nod(OLITERAL, nil, nil) + c.Addable = true + c.Val.U = new(Mpint) + Mpmovecfix(c.Val.U.(*Mpint), v) + c.Val.Ctype = CTINT + c.Type = Types[TIDEAL] + ullmancalc(c) + return c +} + +func nodfltconst(v *Mpflt) *Node { + c := Nod(OLITERAL, nil, nil) + c.Addable = true + c.Val.U = newMpflt() + mpmovefltflt(c.Val.U.(*Mpflt), v) + c.Val.Ctype = CTFLT + c.Type = Types[TIDEAL] + ullmancalc(c) + return c +} + +func Nodconst(n *Node, t *Type, v int64) { + *n = Node{} + n.Op = OLITERAL + n.Addable = true + ullmancalc(n) + n.Val.U = new(Mpint) + Mpmovecfix(n.Val.U.(*Mpint), v) + n.Val.Ctype = CTINT + n.Type = t + + if Isfloat[t.Etype] { + Fatal("nodconst: bad type %v", t) + } +} + +func nodnil() *Node { + c := Nodintconst(0) + c.Val.Ctype = CTNIL + c.Type = Types[TNIL] + return c +} + +func Nodbool(b bool) *Node { + c := Nodintconst(0) + c.Val.Ctype = CTBOOL + c.Val.U = b + c.Type = idealbool + return c +} + +func aindex(b *Node, t *Type) *Type { + bound := int64(-1) // open bound + typecheck(&b, Erv) + if b != nil { + switch consttype(b) { + default: + Yyerror("array bound must be an integer expression") + + case CTINT, CTRUNE: + bound = Mpgetfix(b.Val.U.(*Mpint)) + if bound < 0 { + Yyerror("array bound must be non negative") + } + } + } + + // fixed array + r := typ(TARRAY) + + r.Type = t + r.Bound = bound + return r +} + +func treecopy(n *Node) *Node { + if n == nil { + return nil + } + + var m *Node + switch n.Op { + default: + 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) + if m.Defn != nil { + panic("abort") + } + + case ONONAME: + if n.Sym == Lookup("iota") { + // Not sure yet whether this is the real iota, + // but make a copy of the Node* just in case, + // so that all the copies of this const definition + // don't have the same iota value. + m = Nod(OXXX, nil, nil) + + *m = *n + m.Iota = iota_ + break + } + fallthrough + + case ONAME, OLITERAL, OTYPE: + m = n + } + + return m +} + +func isnil(n *Node) bool { + if n == nil { + return false + } + if n.Op != OLITERAL { + return false + } + if n.Val.Ctype != CTNIL { + return false + } + return true +} + +func isptrto(t *Type, et int) bool { + if t == nil { + return false + } + if !Isptr[t.Etype] { + return false + } + t = t.Type + if t == nil { + return false + } + if int(t.Etype) != et { + return false + } + return true +} + +func Istype(t *Type, et int) bool { + return t != nil && int(t.Etype) == et +} + +func Isfixedarray(t *Type) bool { + return t != nil && t.Etype == TARRAY && t.Bound >= 0 +} + +func Isslice(t *Type) bool { + return t != nil && t.Etype == TARRAY && t.Bound < 0 +} + +func isblank(n *Node) bool { + if n == nil { + return false + } + return isblanksym(n.Sym) +} + +func isblanksym(s *Sym) bool { + return s != nil && s.Name == "_" +} + +func Isinter(t *Type) bool { + return t != nil && t.Etype == TINTER +} + +func isnilinter(t *Type) bool { + if !Isinter(t) { + return false + } + if t.Type != nil { + return false + } + return true +} + +func isideal(t *Type) bool { + if t == nil { + return false + } + if t == idealstring || t == idealbool { + return true + } + switch t.Etype { + case TNIL, TIDEAL: + return true + } + + return false +} + +/* + * given receiver of type t (t == r or t == *r) + * return type to hang methods off (r). + */ +func methtype(t *Type, mustname int) *Type { + if t == nil { + return nil + } + + // strip away pointer if it's there + if Isptr[t.Etype] { + if t.Sym != nil { + return nil + } + t = t.Type + if t == nil { + return nil + } + } + + // need a type name + if t.Sym == nil && (mustname != 0 || t.Etype != TSTRUCT) { + return nil + } + + // check types + if !issimple[t.Etype] { + switch t.Etype { + default: + return nil + + case TSTRUCT, + TARRAY, + TMAP, + TCHAN, + TSTRING, + TFUNC: + break + } + } + + return t +} + +func cplxsubtype(et int) int { + switch et { + case TCOMPLEX64: + return TFLOAT32 + + case TCOMPLEX128: + return TFLOAT64 + } + + Fatal("cplxsubtype: %v\n", Econv(int(et), 0)) + return 0 +} + +func eqnote(a, b *string) bool { + return a == b || a != nil && b != nil && *a == *b +} + +type TypePairList struct { + t1 *Type + t2 *Type + next *TypePairList +} + +func onlist(l *TypePairList, t1 *Type, t2 *Type) bool { + for ; l != nil; l = l.next { + if (l.t1 == t1 && l.t2 == t2) || (l.t1 == t2 && l.t2 == t1) { + return true + } + } + return false +} + +// Return 1 if t1 and t2 are identical, following the spec rules. +// +// Any cyclic type must go through a named type, and if one is +// named, it is only identical to the other if they are the same +// pointer (t1 == t2), so there's no chance of chasing cycles +// ad infinitum, so no need for a depth counter. +func Eqtype(t1 *Type, t2 *Type) bool { + return eqtype1(t1, t2, nil) +} + +func eqtype1(t1 *Type, t2 *Type, assumed_equal *TypePairList) bool { + if t1 == t2 { + return true + } + if t1 == nil || t2 == nil || t1.Etype != t2.Etype { + return false + } + if t1.Sym != nil || t2.Sym != nil { + // Special case: we keep byte and uint8 separate + // for error messages. Treat them as equal. + switch t1.Etype { + case TUINT8: + if (t1 == Types[TUINT8] || t1 == bytetype) && (t2 == Types[TUINT8] || t2 == bytetype) { + return true + } + + case TINT, TINT32: + if (t1 == Types[runetype.Etype] || t1 == runetype) && (t2 == Types[runetype.Etype] || t2 == runetype) { + return true + } + } + + return false + } + + if onlist(assumed_equal, t1, t2) { + return true + } + var l TypePairList + l.next = assumed_equal + l.t1 = t1 + l.t2 = t2 + + switch t1.Etype { + case TINTER, TSTRUCT: + t1 = t1.Type + t2 = t2.Type + for ; t1 != nil && t2 != nil; t1, t2 = t1.Down, t2.Down { + if t1.Etype != TFIELD || t2.Etype != TFIELD { + Fatal("struct/interface missing field: %v %v", t1, t2) + } + if t1.Sym != t2.Sym || t1.Embedded != t2.Embedded || !eqtype1(t1.Type, t2.Type, &l) || !eqnote(t1.Note, t2.Note) { + return false + } + } + + if t1 == nil && t2 == nil { + return true + } + return false + + // Loop over structs: receiver, in, out. + case TFUNC: + t1 = t1.Type + t2 = t2.Type + for ; t1 != nil && t2 != nil; t1, t2 = t1.Down, t2.Down { + if t1.Etype != TSTRUCT || t2.Etype != TSTRUCT { + Fatal("func missing struct: %v %v", t1, t2) + } + + // Loop over fields in structs, ignoring argument names. + ta := t1.Type + tb := t2.Type + for ; ta != nil && tb != nil; ta, tb = ta.Down, tb.Down { + if ta.Etype != TFIELD || tb.Etype != TFIELD { + Fatal("func struct missing field: %v %v", ta, tb) + } + if ta.Isddd != tb.Isddd || !eqtype1(ta.Type, tb.Type, &l) { + return false + } + } + + if ta != nil || tb != nil { + return false + } + } + + if t1 == nil && t2 == nil { + return true + } + return false + + case TARRAY: + if t1.Bound != t2.Bound { + return false + } + + case TCHAN: + if t1.Chan != t2.Chan { + return false + } + } + + if eqtype1(t1.Down, t2.Down, &l) && eqtype1(t1.Type, t2.Type, &l) { + return true + } + return false +} + +// Are t1 and t2 equal struct types when field names are ignored? +// For deciding whether the result struct from g can be copied +// directly when compiling f(g()). +func eqtypenoname(t1 *Type, t2 *Type) bool { + if t1 == nil || t2 == nil || t1.Etype != TSTRUCT || t2.Etype != TSTRUCT { + return false + } + + t1 = t1.Type + t2 = t2.Type + for { + if !Eqtype(t1, t2) { + return false + } + if t1 == nil { + return true + } + t1 = t1.Down + t2 = t2.Down + } +} + +// Is type src assignment compatible to type dst? +// If so, return op code to use in conversion. +// If not, return 0. +func assignop(src *Type, dst *Type, why *string) int { + if why != nil { + *why = "" + } + + // TODO(rsc,lvd): This behaves poorly in the presence of inlining. + // https://golang.org/issue/2795 + if safemode != 0 && importpkg == nil && src != nil && src.Etype == TUNSAFEPTR { + Yyerror("cannot use unsafe.Pointer") + errorexit() + } + + if src == dst { + return OCONVNOP + } + if src == nil || dst == nil || src.Etype == TFORW || dst.Etype == TFORW || src.Orig == nil || dst.Orig == nil { + return 0 + } + + // 1. src type is identical to dst. + if Eqtype(src, dst) { + return OCONVNOP + } + + // 2. src and dst have identical underlying types + // and either src or dst is not a named type or + // both are empty interface types. + // For assignable but different non-empty interface types, + // we want to recompute the itab. + if Eqtype(src.Orig, dst.Orig) && (src.Sym == nil || dst.Sym == nil || isnilinter(src)) { + return OCONVNOP + } + + // 3. dst is an interface type and src implements dst. + if dst.Etype == TINTER && src.Etype != TNIL { + var missing *Type + var ptr int + var have *Type + if implements(src, dst, &missing, &have, &ptr) { + return OCONVIFACE + } + + // we'll have complained about this method anyway, suppress spurious messages. + if have != nil && have.Sym == missing.Sym && (have.Type.Broke != 0 || missing.Type.Broke != 0) { + return OCONVIFACE + } + + if why != nil { + if isptrto(src, TINTER) { + *why = fmt.Sprintf(":\n\t%v is pointer to interface, not interface", src) + } else if have != nil && have.Sym == missing.Sym && have.Nointerface { + *why = fmt.Sprintf(":\n\t%v does not implement %v (%v method is marked 'nointerface')", src, dst, missing.Sym) + } else if have != nil && have.Sym == missing.Sym { + *why = fmt.Sprintf(":\n\t%v does not implement %v (wrong type for %v method)\n"+"\t\thave %v%v\n\t\twant %v%v", src, dst, missing.Sym, have.Sym, Tconv(have.Type, obj.FmtShort|obj.FmtByte), missing.Sym, Tconv(missing.Type, obj.FmtShort|obj.FmtByte)) + } else if ptr != 0 { + *why = fmt.Sprintf(":\n\t%v does not implement %v (%v method has pointer receiver)", src, dst, missing.Sym) + } else if have != nil { + *why = fmt.Sprintf(":\n\t%v does not implement %v (missing %v method)\n"+"\t\thave %v%v\n\t\twant %v%v", src, dst, missing.Sym, have.Sym, Tconv(have.Type, obj.FmtShort|obj.FmtByte), missing.Sym, Tconv(missing.Type, obj.FmtShort|obj.FmtByte)) + } else { + *why = fmt.Sprintf(":\n\t%v does not implement %v (missing %v method)", src, dst, missing.Sym) + } + } + + return 0 + } + + if isptrto(dst, TINTER) { + if why != nil { + *why = fmt.Sprintf(":\n\t%v is pointer to interface, not interface", dst) + } + return 0 + } + + if src.Etype == TINTER && dst.Etype != TBLANK { + var have *Type + var ptr int + var missing *Type + if why != nil && implements(dst, src, &missing, &have, &ptr) { + *why = ": need type assertion" + } + return 0 + } + + // 4. src is a bidirectional channel value, dst is a channel type, + // src and dst have identical element types, and + // either src or dst is not a named type. + if src.Etype == TCHAN && src.Chan == Cboth && dst.Etype == TCHAN { + if Eqtype(src.Type, dst.Type) && (src.Sym == nil || dst.Sym == nil) { + return OCONVNOP + } + } + + // 5. src is the predeclared identifier nil and dst is a nillable type. + if src.Etype == TNIL { + switch dst.Etype { + case TARRAY: + if dst.Bound != -100 { // not slice + break + } + fallthrough + + case TPTR32, + TPTR64, + TFUNC, + TMAP, + TCHAN, + TINTER: + return OCONVNOP + } + } + + // 6. rule about untyped constants - already converted by defaultlit. + + // 7. Any typed value can be assigned to the blank identifier. + if dst.Etype == TBLANK { + return OCONVNOP + } + + return 0 +} + +// Can we convert a value of type src to a value of type dst? +// If so, return op code to use in conversion (maybe OCONVNOP). +// If not, return 0. +func convertop(src *Type, dst *Type, why *string) int { + if why != nil { + *why = "" + } + + if src == dst { + return OCONVNOP + } + if src == nil || dst == nil { + return 0 + } + + // 1. src can be assigned to dst. + op := assignop(src, dst, why) + if op != 0 { + return op + } + + // The rules for interfaces are no different in conversions + // than assignments. If interfaces are involved, stop now + // with the good message from assignop. + // Otherwise clear the error. + if src.Etype == TINTER || dst.Etype == TINTER { + return 0 + } + if why != nil { + *why = "" + } + + // 2. src and dst have identical underlying types. + if Eqtype(src.Orig, dst.Orig) { + return OCONVNOP + } + + // 3. src and dst are unnamed pointer types + // and their base types have identical underlying types. + if Isptr[src.Etype] && Isptr[dst.Etype] && src.Sym == nil && dst.Sym == nil { + if Eqtype(src.Type.Orig, dst.Type.Orig) { + return OCONVNOP + } + } + + // 4. src and dst are both integer or floating point types. + if (Isint[src.Etype] || Isfloat[src.Etype]) && (Isint[dst.Etype] || Isfloat[dst.Etype]) { + if Simtype[src.Etype] == Simtype[dst.Etype] { + return OCONVNOP + } + return OCONV + } + + // 5. src and dst are both complex types. + if Iscomplex[src.Etype] && Iscomplex[dst.Etype] { + if Simtype[src.Etype] == Simtype[dst.Etype] { + return OCONVNOP + } + return OCONV + } + + // 6. src is an integer or has type []byte or []rune + // and dst is a string type. + if Isint[src.Etype] && dst.Etype == TSTRING { + return ORUNESTR + } + + if Isslice(src) && dst.Etype == TSTRING { + if src.Type.Etype == bytetype.Etype { + return OARRAYBYTESTR + } + if src.Type.Etype == runetype.Etype { + return OARRAYRUNESTR + } + } + + // 7. src is a string and dst is []byte or []rune. + // String to slice. + if src.Etype == TSTRING && Isslice(dst) { + if dst.Type.Etype == bytetype.Etype { + return OSTRARRAYBYTE + } + if dst.Type.Etype == runetype.Etype { + return OSTRARRAYRUNE + } + } + + // 8. src is a pointer or uintptr and dst is unsafe.Pointer. + if (Isptr[src.Etype] || src.Etype == TUINTPTR) && dst.Etype == TUNSAFEPTR { + return OCONVNOP + } + + // 9. src is unsafe.Pointer and dst is a pointer or uintptr. + if src.Etype == TUNSAFEPTR && (Isptr[dst.Etype] || dst.Etype == TUINTPTR) { + return OCONVNOP + } + + return 0 +} + +func assignconv(n *Node, t *Type, context string) *Node { + return assignconvfn(n, t, func() string { return context }) +} + +// Convert node n for assignment to type t. +func assignconvfn(n *Node, t *Type, context func() string) *Node { + if n == nil || n.Type == nil || n.Type.Broke != 0 { + return n + } + + if t.Etype == TBLANK && n.Type.Etype == TNIL { + Yyerror("use of untyped nil") + } + + old := n + old.Diag++ // silence errors about n; we'll issue one below + defaultlit(&n, t) + old.Diag-- + if t.Etype == TBLANK { + return n + } + + // Convert ideal bool from comparison to plain bool + // if the next step is non-bool (like interface{}). + if n.Type == idealbool && t.Etype != TBOOL { + if n.Op == ONAME || n.Op == OLITERAL { + r := Nod(OCONVNOP, n, nil) + r.Type = Types[TBOOL] + r.Typecheck = 1 + r.Implicit = true + n = r + } + } + + if Eqtype(n.Type, t) { + return n + } + + var why string + op := assignop(n.Type, t, &why) + if op == 0 { + Yyerror("cannot use %v as type %v in %s%s", Nconv(n, obj.FmtLong), t, context(), why) + op = OCONV + } + + r := Nod(op, n, nil) + r.Type = t + r.Typecheck = 1 + r.Implicit = true + r.Orig = n.Orig + return r +} + +// substArgTypes substitutes the given list of types for +// successive occurrences of the "any" placeholder in the +// type syntax expression n.Type. +func substArgTypes(n *Node, types ...*Type) { + for _, t := range types { + dowidth(t) + } + substAny(&n.Type, &types) + if len(types) > 0 { + Fatal("substArgTypes: too many argument types") + } +} + +// substAny walks *tp, replacing instances of "any" with successive +// elements removed from types. +func substAny(tp **Type, types *[]*Type) { + for { + t := *tp + if t == nil { + return + } + if t.Etype == TANY && t.Copyany != 0 { + if len(*types) == 0 { + Fatal("substArgTypes: not enough argument types") + } + *tp = (*types)[0] + *types = (*types)[1:] + } + + switch t.Etype { + case TPTR32, TPTR64, TCHAN, TARRAY: + tp = &t.Type + continue + + case TMAP: + substAny(&t.Down, types) + tp = &t.Type + continue + + case TFUNC: + substAny(&t.Type, types) + substAny(&t.Type.Down.Down, types) + substAny(&t.Type.Down, types) + + case TSTRUCT: + for t = t.Type; t != nil; t = t.Down { + substAny(&t.Type, types) + } + } + return + } +} + +/* + * Is this a 64-bit type? + */ +func Is64(t *Type) bool { + if t == nil { + return false + } + switch Simtype[t.Etype] { + case TINT64, TUINT64, TPTR64: + return true + } + + return false +} + +/* + * Is a conversion between t1 and t2 a no-op? + */ +func Noconv(t1 *Type, t2 *Type) bool { + e1 := int(Simtype[t1.Etype]) + e2 := int(Simtype[t2.Etype]) + + switch e1 { + case TINT8, TUINT8: + return e2 == TINT8 || e2 == TUINT8 + + case TINT16, TUINT16: + return e2 == TINT16 || e2 == TUINT16 + + case TINT32, TUINT32, TPTR32: + return e2 == TINT32 || e2 == TUINT32 || e2 == TPTR32 + + case TINT64, TUINT64, TPTR64: + return e2 == TINT64 || e2 == TUINT64 || e2 == TPTR64 + + case TFLOAT32: + return e2 == TFLOAT32 + + case TFLOAT64: + return e2 == TFLOAT64 + } + + return false +} + +func shallow(t *Type) *Type { + if t == nil { + return nil + } + nt := typ(0) + *nt = *t + if t.Orig == t { + nt.Orig = nt + } + return nt +} + +func deep(t *Type) *Type { + if t == nil { + return nil + } + + var nt *Type + switch t.Etype { + default: + nt = t // share from here down + + case TANY: + nt = shallow(t) + nt.Copyany = 1 + + case TPTR32, TPTR64, TCHAN, TARRAY: + nt = shallow(t) + nt.Type = deep(t.Type) + + case TMAP: + nt = shallow(t) + nt.Down = deep(t.Down) + nt.Type = deep(t.Type) + + case TFUNC: + nt = shallow(t) + nt.Type = deep(t.Type) + nt.Type.Down = deep(t.Type.Down) + nt.Type.Down.Down = deep(t.Type.Down.Down) + + case TSTRUCT: + nt = shallow(t) + nt.Type = shallow(t.Type) + xt := nt.Type + + for t = t.Type; t != nil; t = t.Down { + xt.Type = deep(t.Type) + xt.Down = shallow(t.Down) + xt = xt.Down + } + } + + return nt +} + +func syslook(name string, copy int) *Node { + s := Pkglookup(name, Runtimepkg) + if s == nil || s.Def == nil { + Fatal("syslook: can't find runtime.%s", name) + } + + if copy == 0 { + return s.Def + } + + n := Nod(0, nil, nil) + *n = *s.Def + n.Type = deep(s.Def.Type) + + return n +} + +/* + * compute a hash value for type t. + * if t is a method type, ignore the receiver + * so that the hash can be used in interface checks. + * %T already contains + * all the necessary logic to generate a representation + * of the type that completely describes it. + * using smprint here avoids duplicating that code. + * using md5 here is overkill, but i got tired of + * accidental collisions making the runtime think + * two types are equal when they really aren't. + */ +func typehash(t *Type) uint32 { + var p string + + if t.Thistuple != 0 { + // hide method receiver from Tpretty + t.Thistuple = 0 + + p = Tconv(t, obj.FmtLeft|obj.FmtUnsigned) + t.Thistuple = 1 + } else { + p = Tconv(t, obj.FmtLeft|obj.FmtUnsigned) + } + + //print("typehash: %s\n", p); + h := md5.Sum([]byte(p)) + return binary.LittleEndian.Uint32(h[:4]) +} + +var initPtrtoDone bool + +var ( + ptrToUint8 *Type + ptrToAny *Type + ptrToString *Type + ptrToBool *Type + ptrToInt32 *Type +) + +func initPtrto() { + ptrToUint8 = ptrto1(Types[TUINT8]) + ptrToAny = ptrto1(Types[TANY]) + ptrToString = ptrto1(Types[TSTRING]) + ptrToBool = ptrto1(Types[TBOOL]) + ptrToInt32 = ptrto1(Types[TINT32]) +} + +func ptrto1(t *Type) *Type { + t1 := typ(Tptr) + t1.Type = t + t1.Width = int64(Widthptr) + t1.Align = uint8(Widthptr) + return t1 +} + +// Ptrto returns the Type *t. +// The returned struct must not be modified. +func Ptrto(t *Type) *Type { + if Tptr == 0 { + Fatal("ptrto: no tptr") + } + // Reduce allocations by pre-creating common cases. + if !initPtrtoDone { + initPtrto() + initPtrtoDone = true + } + switch t { + case Types[TUINT8]: + return ptrToUint8 + case Types[TINT32]: + return ptrToInt32 + case Types[TANY]: + return ptrToAny + case Types[TSTRING]: + return ptrToString + case Types[TBOOL]: + return ptrToBool + } + return ptrto1(t) +} + +func frame(context int) { + var l *NodeList + + if context != 0 { + fmt.Printf("--- external frame ---\n") + l = externdcl + } else if Curfn != nil { + fmt.Printf("--- %v frame ---\n", Curfn.Nname.Sym) + l = Curfn.Func.Dcl + } else { + return + } + + var n *Node + var w int64 + for ; l != nil; l = l.Next { + n = l.N + w = -1 + if n.Type != nil { + w = n.Type.Width + } + switch n.Op { + case ONAME: + fmt.Printf("%v %v G%d %v width=%d\n", Oconv(int(n.Op), 0), n.Sym, n.Vargen, n.Type, w) + + case OTYPE: + fmt.Printf("%v %v width=%d\n", Oconv(int(n.Op), 0), n.Type, w) + } + } +} + +/* + * calculate sethi/ullman number + * roughly how many registers needed to + * compile a node. used to compile the + * hardest side first to minimize registers. + */ +func ullmancalc(n *Node) { + if n == nil { + return + } + + var ul int + var ur int + if n.Ninit != nil { + ul = UINF + goto out + } + + switch n.Op { + case OREGISTER, OLITERAL, ONAME: + ul = 1 + if n.Class == PPARAMREF || (n.Class&PHEAP != 0) { + ul++ + } + goto out + + case OCALL, OCALLFUNC, OCALLMETH, OCALLINTER, OASWB: + ul = UINF + goto out + + // hard with race detector + case OANDAND, OOROR: + if flag_race != 0 { + ul = UINF + goto out + } + } + + ul = 1 + if n.Left != nil { + ul = int(n.Left.Ullman) + } + ur = 1 + if n.Right != nil { + ur = int(n.Right.Ullman) + } + if ul == ur { + ul += 1 + } + if ur > ul { + ul = ur + } + +out: + if ul > 200 { + ul = 200 // clamp to uchar with room to grow + } + n.Ullman = uint8(ul) +} + +func badtype(o int, tl *Type, tr *Type) { + fmt_ := "" + if tl != nil { + fmt_ += fmt.Sprintf("\n\t%v", tl) + } + if tr != nil { + fmt_ += fmt.Sprintf("\n\t%v", tr) + } + + // common mistake: *struct and *interface. + if tl != nil && tr != nil && Isptr[tl.Etype] && Isptr[tr.Etype] { + if tl.Type.Etype == TSTRUCT && tr.Type.Etype == TINTER { + fmt_ += "\n\t(*struct vs *interface)" + } else if tl.Type.Etype == TINTER && tr.Type.Etype == TSTRUCT { + fmt_ += "\n\t(*interface vs *struct)" + } + } + + s := fmt_ + Yyerror("illegal types for operand: %v%s", Oconv(int(o), 0), s) +} + +/* + * iterator to walk a structure declaration + */ +func Structfirst(s *Iter, nn **Type) *Type { + var t *Type + + n := *nn + if n == nil { + goto bad + } + + switch n.Etype { + default: + goto bad + + case TSTRUCT, TINTER, TFUNC: + break + } + + t = n.Type + if t == nil { + return nil + } + + if t.Etype != TFIELD { + Fatal("structfirst: not field %v", t) + } + + s.T = t + return t + +bad: + Fatal("structfirst: not struct %v", n) + + return nil +} + +func structnext(s *Iter) *Type { + n := s.T + t := n.Down + if t == nil { + return nil + } + + if t.Etype != TFIELD { + Fatal("structnext: not struct %v", n) + + return nil + } + + s.T = t + return t +} + +/* + * iterator to this and inargs in a function + */ +func funcfirst(s *Iter, t *Type) *Type { + var fp *Type + + if t == nil { + goto bad + } + + if t.Etype != TFUNC { + goto bad + } + + s.Tfunc = t + s.Done = 0 + fp = Structfirst(s, getthis(t)) + if fp == nil { + s.Done = 1 + fp = Structfirst(s, getinarg(t)) + } + + return fp + +bad: + Fatal("funcfirst: not func %v", t) + return nil +} + +func funcnext(s *Iter) *Type { + fp := structnext(s) + if fp == nil && s.Done == 0 { + s.Done = 1 + fp = Structfirst(s, getinarg(s.Tfunc)) + } + + return fp +} + +func getthis(t *Type) **Type { + if t.Etype != TFUNC { + Fatal("getthis: not a func %v", t) + } + return &t.Type +} + +func Getoutarg(t *Type) **Type { + if t.Etype != TFUNC { + Fatal("getoutarg: not a func %v", t) + } + return &t.Type.Down +} + +func getinarg(t *Type) **Type { + if t.Etype != TFUNC { + Fatal("getinarg: not a func %v", t) + } + return &t.Type.Down.Down +} + +func getthisx(t *Type) *Type { + return *getthis(t) +} + +func getoutargx(t *Type) *Type { + return *Getoutarg(t) +} + +func getinargx(t *Type) *Type { + return *getinarg(t) +} + +// Brcom returns !(op). +// For example, Brcom(==) is !=. +func Brcom(a int) int { + switch a { + case OEQ: + return ONE + case ONE: + return OEQ + case OLT: + return OGE + case OGT: + return OLE + case OLE: + return OGT + case OGE: + return OLT + } + Fatal("brcom: no com for %v\n", Oconv(a, 0)) + return a +} + +// Brrev returns reverse(op). +// For example, Brrev(<) is >. +func Brrev(a int) int { + switch a { + case OEQ: + return OEQ + case ONE: + return ONE + case OLT: + return OGT + case OGT: + return OLT + case OLE: + return OGE + case OGE: + return OLE + } + Fatal("brrev: no rev for %v\n", Oconv(a, 0)) + return a +} + +/* + * return side effect-free n, appending side effects to init. + * result is assignable if n is. + */ +func safeexpr(n *Node, init **NodeList) *Node { + if n == nil { + return nil + } + + if n.Ninit != nil { + walkstmtlist(n.Ninit) + *init = concat(*init, n.Ninit) + n.Ninit = nil + } + + switch n.Op { + case ONAME, OLITERAL: + return n + + case ODOT, OLEN, OCAP: + l := safeexpr(n.Left, init) + if l == n.Left { + return n + } + r := Nod(OXXX, nil, nil) + *r = *n + r.Left = l + typecheck(&r, Erv) + walkexpr(&r, init) + return r + + case ODOTPTR, OIND: + l := safeexpr(n.Left, init) + if l == n.Left { + return n + } + a := Nod(OXXX, nil, nil) + *a = *n + a.Left = l + walkexpr(&a, init) + return a + + case OINDEX, OINDEXMAP: + l := safeexpr(n.Left, init) + r := safeexpr(n.Right, init) + if l == n.Left && r == n.Right { + return n + } + a := Nod(OXXX, nil, nil) + *a = *n + a.Left = l + a.Right = r + walkexpr(&a, init) + return a + } + + // make a copy; must not be used as an lvalue + if islvalue(n) { + Fatal("missing lvalue case in safeexpr: %v", n) + } + return cheapexpr(n, init) +} + +func copyexpr(n *Node, t *Type, init **NodeList) *Node { + l := temp(t) + a := Nod(OAS, l, n) + typecheck(&a, Etop) + walkexpr(&a, init) + *init = list(*init, a) + return l +} + +/* + * return side-effect free and cheap n, appending side effects to init. + * result may not be assignable. + */ +func cheapexpr(n *Node, init **NodeList) *Node { + switch n.Op { + case ONAME, OLITERAL: + return n + } + + return copyexpr(n, n.Type, init) +} + +/* + * return n in a local variable of type t if it is not already. + * the value is guaranteed not to change except by direct + * assignment to it. + */ +func localexpr(n *Node, t *Type, init **NodeList) *Node { + if n.Op == ONAME && (!n.Addrtaken || strings.HasPrefix(n.Sym.Name, "autotmp_")) && (n.Class == PAUTO || n.Class == PPARAM || n.Class == PPARAMOUT) && convertop(n.Type, t, nil) == OCONVNOP { + return n + } + + return copyexpr(n, t, init) +} + +func Setmaxarg(t *Type, extra int32) { + dowidth(t) + w := t.Argwid + if w >= Thearch.MAXWIDTH { + Fatal("bad argwid %v", t) + } + w += int64(extra) + if w >= Thearch.MAXWIDTH { + Fatal("bad argwid %d + %v", extra, t) + } + if w > Maxarg { + Maxarg = w + } +} + +/* + * unicode-aware case-insensitive strcmp + */ + +/* + * code to resolve elided DOTs + * in embedded types + */ + +// search depth 0 -- +// return count of fields+methods +// found with a given name +func lookdot0(s *Sym, t *Type, save **Type, ignorecase int) int { + u := t + if Isptr[u.Etype] { + u = u.Type + } + + c := 0 + if u.Etype == TSTRUCT || u.Etype == TINTER { + for f := u.Type; f != nil; f = f.Down { + if f.Sym == s || (ignorecase != 0 && f.Type.Etype == TFUNC && f.Type.Thistuple > 0 && strings.EqualFold(f.Sym.Name, s.Name)) { + if save != nil { + *save = f + } + c++ + } + } + } + + u = methtype(t, 0) + if u != nil { + for f := u.Method; f != nil; f = f.Down { + if f.Embedded == 0 && (f.Sym == s || (ignorecase != 0 && strings.EqualFold(f.Sym.Name, s.Name))) { + if save != nil { + *save = f + } + c++ + } + } + } + + return c +} + +// search depth d for field/method s -- +// return count of fields+methods +// found at search depth. +// answer is in dotlist array and +// count of number of ways is returned. +func adddot1(s *Sym, t *Type, d int, save **Type, ignorecase int) int { + if t.Trecur != 0 { + return 0 + } + t.Trecur = 1 + + var c int + var u *Type + var a int + if d == 0 { + c = lookdot0(s, t, save, ignorecase) + goto out + } + + c = 0 + u = t + if Isptr[u.Etype] { + u = u.Type + } + if u.Etype != TSTRUCT && u.Etype != TINTER { + goto out + } + + d-- + for f := u.Type; f != nil; f = f.Down { + if f.Embedded == 0 { + continue + } + if f.Sym == nil { + continue + } + a = adddot1(s, f.Type, d, save, ignorecase) + if a != 0 && c == 0 { + dotlist[d].field = f + } + c += a + } + +out: + t.Trecur = 0 + return c +} + +// in T.field +// find missing fields that +// will give shortest unique addressing. +// modify the tree with missing type names. +func adddot(n *Node) *Node { + typecheck(&n.Left, Etype|Erv) + n.Diag |= n.Left.Diag + t := n.Left.Type + if t == nil { + return n + } + + if n.Left.Op == OTYPE { + return n + } + + if n.Right.Op != ONAME { + return n + } + s := n.Right.Sym + if s == nil { + return n + } + + var c int + for d := 0; d < len(dotlist); d++ { + c = adddot1(s, t, d, nil, 0) + if c > 0 { + if c > 1 { + Yyerror("ambiguous selector %v", n) + n.Left = nil + return n + } + + // rebuild elided dots + for c := d - 1; c >= 0; c-- { + if n.Left.Type != nil && Isptr[n.Left.Type.Etype] { + n.Left.Implicit = true + } + n.Left = Nod(ODOT, n.Left, newname(dotlist[c].field.Sym)) + } + + return n + } + } + + return n +} + +/* + * code to help generate trampoline + * functions for methods on embedded + * subtypes. + * these are approx the same as + * the corresponding adddot routines + * except that they expect to be called + * with unique tasks and they return + * the actual methods. + */ +type Symlink struct { + field *Type + good uint8 + followptr uint8 + link *Symlink +} + +var slist *Symlink + +func expand0(t *Type, followptr int) { + u := t + if Isptr[u.Etype] { + followptr = 1 + u = u.Type + } + + if u.Etype == TINTER { + var sl *Symlink + for f := u.Type; f != nil; f = f.Down { + if f.Sym.Flags&SymUniq != 0 { + continue + } + f.Sym.Flags |= SymUniq + sl = new(Symlink) + sl.field = f + sl.link = slist + sl.followptr = uint8(followptr) + slist = sl + } + + return + } + + u = methtype(t, 0) + if u != nil { + var sl *Symlink + for f := u.Method; f != nil; f = f.Down { + if f.Sym.Flags&SymUniq != 0 { + continue + } + f.Sym.Flags |= SymUniq + sl = new(Symlink) + sl.field = f + sl.link = slist + sl.followptr = uint8(followptr) + slist = sl + } + } +} + +func expand1(t *Type, d int, followptr int) { + if t.Trecur != 0 { + return + } + if d == 0 { + return + } + t.Trecur = 1 + + if d != len(dotlist)-1 { + expand0(t, followptr) + } + + u := t + if Isptr[u.Etype] { + followptr = 1 + u = u.Type + } + + if u.Etype != TSTRUCT && u.Etype != TINTER { + goto out + } + + for f := u.Type; f != nil; f = f.Down { + if f.Embedded == 0 { + continue + } + if f.Sym == nil { + continue + } + expand1(f.Type, d-1, followptr) + } + +out: + t.Trecur = 0 +} + +func expandmeth(t *Type) { + if t == nil || t.Xmethod != nil { + return + } + + // mark top-level method symbols + // so that expand1 doesn't consider them. + var f *Type + for f = t.Method; f != nil; f = f.Down { + f.Sym.Flags |= SymUniq + } + + // generate all reachable methods + slist = nil + + expand1(t, len(dotlist)-1, 0) + + // check each method to be uniquely reachable + var c int + var d int + for sl := slist; sl != nil; sl = sl.link { + sl.field.Sym.Flags &^= SymUniq + for d = 0; d < len(dotlist); d++ { + c = adddot1(sl.field.Sym, t, d, &f, 0) + if c == 0 { + continue + } + if c == 1 { + // addot1 may have dug out arbitrary fields, we only want methods. + if f.Type.Etype == TFUNC && f.Type.Thistuple > 0 { + sl.good = 1 + sl.field = f + } + } + + break + } + } + + for f = t.Method; f != nil; f = f.Down { + f.Sym.Flags &^= SymUniq + } + + t.Xmethod = t.Method + for sl := slist; sl != nil; sl = sl.link { + if sl.good != 0 { + // add it to the base type method list + f = typ(TFIELD) + + *f = *sl.field + f.Embedded = 1 // needs a trampoline + if sl.followptr != 0 { + f.Embedded = 2 + } + f.Down = t.Xmethod + t.Xmethod = f + } + } +} + +/* + * Given funarg struct list, return list of ODCLFIELD Node fn args. + */ +func structargs(tl **Type, mustname int) *NodeList { + var savet Iter + var a *Node + var n *Node + var buf string + + var args *NodeList + gen := 0 + for t := Structfirst(&savet, tl); t != nil; t = structnext(&savet) { + n = nil + if mustname != 0 && (t.Sym == nil || t.Sym.Name == "_") { + // invent a name so that we can refer to it in the trampoline + buf = fmt.Sprintf(".anon%d", gen) + gen++ + + n = newname(Lookup(buf)) + } else if t.Sym != nil { + n = newname(t.Sym) + } + a = Nod(ODCLFIELD, n, typenod(t.Type)) + a.Isddd = t.Isddd + if n != nil { + n.Isddd = t.Isddd + } + args = list(args, a) + } + + return args +} + +/* + * Generate a wrapper function to convert from + * a receiver of type T to a receiver of type U. + * That is, + * + * func (t T) M() { + * ... + * } + * + * already exists; this function generates + * + * func (u U) M() { + * u.M() + * } + * + * where the types T and U are such that u.M() is valid + * and calls the T.M method. + * The resulting function is for use in method tables. + * + * rcvr - U + * method - M func (t T)(), a TFIELD type struct + * newnam - the eventual mangled name of this function + */ + +var genwrapper_linehistdone int = 0 + +func genwrapper(rcvr *Type, method *Type, newnam *Sym, iface int) { + if false && Debug['r'] != 0 { + fmt.Printf("genwrapper rcvrtype=%v method=%v newnam=%v\n", rcvr, method, newnam) + } + + lexlineno++ + lineno = lexlineno + if genwrapper_linehistdone == 0 { + // All the wrappers can share the same linehist entry. + linehistpush("") + + genwrapper_linehistdone = 1 + } + + dclcontext = PEXTERN + markdcl() + + this := Nod(ODCLFIELD, newname(Lookup(".this")), typenod(rcvr)) + this.Left.Ntype = this.Right + in := structargs(getinarg(method.Type), 1) + out := structargs(Getoutarg(method.Type), 0) + + t := Nod(OTFUNC, nil, nil) + l := list1(this) + if iface != 0 && rcvr.Width < Types[Tptr].Width { + // Building method for interface table and receiver + // is smaller than the single pointer-sized word + // that the interface call will pass in. + // Add a dummy padding argument after the + // receiver to make up the difference. + tpad := typ(TARRAY) + + tpad.Type = Types[TUINT8] + tpad.Bound = Types[Tptr].Width - rcvr.Width + pad := Nod(ODCLFIELD, newname(Lookup(".pad")), typenod(tpad)) + l = list(l, pad) + } + + t.List = concat(l, in) + t.Rlist = out + + fn := Nod(ODCLFUNC, nil, nil) + fn.Nname = newname(newnam) + fn.Nname.Defn = fn + fn.Nname.Ntype = t + declare(fn.Nname, PFUNC) + funchdr(fn) + + // arg list + var args *NodeList + + isddd := false + for l := in; l != nil; l = l.Next { + args = list(args, l.N.Left) + isddd = l.N.Left.Isddd + } + + methodrcvr := getthisx(method.Type).Type.Type + + // generate nil pointer check for better error + if Isptr[rcvr.Etype] && rcvr.Type == methodrcvr { + // generating wrapper from *T to T. + n := Nod(OIF, nil, nil) + + n.Ntest = Nod(OEQ, this.Left, nodnil()) + + // these strings are already in the reflect tables, + // so no space cost to use them here. + var l *NodeList + + var v Val + v.Ctype = CTSTR + v.U = rcvr.Type.Sym.Pkg.Name // package name + l = list(l, nodlit(v)) + v.U = rcvr.Type.Sym.Name // type name + l = list(l, nodlit(v)) + v.U = method.Sym.Name + l = list(l, nodlit(v)) // method name + call := Nod(OCALL, syslook("panicwrap", 0), nil) + call.List = l + n.Nbody = list1(call) + fn.Nbody = list(fn.Nbody, n) + } + + dot := adddot(Nod(OXDOT, this.Left, newname(method.Sym))) + + // generate call + if flag_race == 0 && Isptr[rcvr.Etype] && Isptr[methodrcvr.Etype] && method.Embedded != 0 && !isifacemethod(method.Type) { + // generate tail call: adjust pointer receiver and jump to embedded method. + dot = dot.Left // skip final .M + if !Isptr[dotlist[0].field.Type.Etype] { + dot = Nod(OADDR, dot, nil) + } + as := Nod(OAS, this.Left, Nod(OCONVNOP, dot, nil)) + as.Right.Type = rcvr + fn.Nbody = list(fn.Nbody, as) + n := Nod(ORETJMP, nil, nil) + n.Left = newname(methodsym(method.Sym, methodrcvr, 0)) + fn.Nbody = list(fn.Nbody, n) + } else { + fn.Func.Wrapper = true // ignore frame for panic+recover matching + call := Nod(OCALL, dot, nil) + call.List = args + call.Isddd = isddd + if method.Type.Outtuple > 0 { + n := Nod(ORETURN, nil, nil) + n.List = list1(call) + call = n + } + + fn.Nbody = list(fn.Nbody, call) + } + + if false && Debug['r'] != 0 { + dumplist("genwrapper body", fn.Nbody) + } + + funcbody(fn) + Curfn = fn + + // wrappers where T is anonymous (struct or interface) can be duplicated. + if rcvr.Etype == TSTRUCT || rcvr.Etype == TINTER || Isptr[rcvr.Etype] && rcvr.Type.Etype == TSTRUCT { + fn.Func.Dupok = true + } + typecheck(&fn, Etop) + typechecklist(fn.Nbody, Etop) + + // Set inl_nonlocal to whether we are calling a method on a + // type defined in a different package. Checked in inlvar. + if !methodrcvr.Local { + inl_nonlocal = 1 + } + + inlcalls(fn) + + inl_nonlocal = 0 + + Curfn = nil + funccompile(fn) +} + +func hashmem(t *Type) *Node { + sym := Pkglookup("memhash", Runtimepkg) + + n := newname(sym) + n.Class = PFUNC + tfn := Nod(OTFUNC, nil, nil) + tfn.List = list(tfn.List, Nod(ODCLFIELD, nil, typenod(Ptrto(t)))) + tfn.List = list(tfn.List, Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR]))) + tfn.List = list(tfn.List, Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR]))) + tfn.Rlist = list(tfn.Rlist, Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR]))) + typecheck(&tfn, Etype) + n.Type = tfn.Type + return n +} + +func hashfor(t *Type) *Node { + var sym *Sym + + a := algtype1(t, nil) + switch a { + case AMEM: + Fatal("hashfor with AMEM type") + + case AINTER: + sym = Pkglookup("interhash", Runtimepkg) + + case ANILINTER: + sym = Pkglookup("nilinterhash", Runtimepkg) + + case ASTRING: + sym = Pkglookup("strhash", Runtimepkg) + + case AFLOAT32: + sym = Pkglookup("f32hash", Runtimepkg) + + case AFLOAT64: + sym = Pkglookup("f64hash", Runtimepkg) + + case ACPLX64: + sym = Pkglookup("c64hash", Runtimepkg) + + case ACPLX128: + sym = Pkglookup("c128hash", Runtimepkg) + + default: + sym = typesymprefix(".hash", t) + } + + n := newname(sym) + n.Class = PFUNC + tfn := Nod(OTFUNC, nil, nil) + tfn.List = list(tfn.List, Nod(ODCLFIELD, nil, typenod(Ptrto(t)))) + tfn.List = list(tfn.List, Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR]))) + tfn.Rlist = list(tfn.Rlist, Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR]))) + typecheck(&tfn, Etype) + n.Type = tfn.Type + return n +} + +/* + * Generate a helper function to compute the hash of a value of type t. + */ +func genhash(sym *Sym, t *Type) { + if Debug['r'] != 0 { + fmt.Printf("genhash %v %v\n", sym, t) + } + + lineno = 1 // less confusing than end of input + dclcontext = PEXTERN + markdcl() + + // func sym(p *T, h uintptr) uintptr + fn := Nod(ODCLFUNC, nil, nil) + + fn.Nname = newname(sym) + fn.Nname.Class = PFUNC + tfn := Nod(OTFUNC, nil, nil) + fn.Nname.Ntype = tfn + + n := Nod(ODCLFIELD, newname(Lookup("p")), typenod(Ptrto(t))) + tfn.List = list(tfn.List, n) + np := n.Left + n = Nod(ODCLFIELD, newname(Lookup("h")), typenod(Types[TUINTPTR])) + tfn.List = list(tfn.List, n) + nh := n.Left + n = Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])) // return value + tfn.Rlist = list(tfn.Rlist, n) + + funchdr(fn) + typecheck(&fn.Nname.Ntype, Etype) + + // genhash is only called for types that have equality but + // cannot be handled by the standard algorithms, + // so t must be either an array or a struct. + switch t.Etype { + default: + Fatal("genhash %v", t) + + case TARRAY: + if Isslice(t) { + Fatal("genhash %v", t) + } + + // An array of pure memory would be handled by the + // standard algorithm, so the element type must not be + // pure memory. + hashel := hashfor(t.Type) + + n := Nod(ORANGE, nil, Nod(OIND, np, nil)) + ni := newname(Lookup("i")) + ni.Type = Types[TINT] + n.List = list1(ni) + n.Colas = true + colasdefn(n.List, n) + ni = n.List.N + + // TODO: with aeshash we don't need these shift/mul parts + + // h = h<<3 | h>>61 + n.Nbody = list(n.Nbody, Nod(OAS, nh, Nod(OOR, Nod(OLSH, nh, Nodintconst(3)), Nod(ORSH, nh, Nodintconst(int64(Widthptr)*8-3))))) + + // h *= mul + // Same multipliers as in runtime.memhash. + var mul int64 + if Widthptr == 4 { + mul = 3267000013 + } else { + mul = 23344194077549503 + } + n.Nbody = list(n.Nbody, Nod(OAS, nh, Nod(OMUL, nh, Nodintconst(mul)))) + + // h = hashel(&p[i], h) + call := Nod(OCALL, hashel, nil) + + nx := Nod(OINDEX, np, ni) + nx.Bounded = true + na := Nod(OADDR, nx, nil) + na.Etype = 1 // no escape to heap + call.List = list(call.List, na) + call.List = list(call.List, nh) + n.Nbody = list(n.Nbody, Nod(OAS, nh, call)) + + fn.Nbody = list(fn.Nbody, n) + + // Walk the struct using memhash for runs of AMEM + // and calling specific hash functions for the others. + case TSTRUCT: + var first *Type + + offend := int64(0) + var size int64 + var call *Node + var nx *Node + var na *Node + var hashel *Node + for t1 := t.Type; ; t1 = t1.Down { + if t1 != nil && algtype1(t1.Type, nil) == AMEM && !isblanksym(t1.Sym) { + offend = t1.Width + t1.Type.Width + if first == nil { + first = t1 + } + + // If it's a memory field but it's padded, stop here. + if ispaddedfield(t1, t.Width) { + t1 = t1.Down + } else { + continue + } + } + + // Run memhash for fields up to this one. + if first != nil { + size = offend - first.Width // first->width is offset + hashel = hashmem(first.Type) + + // h = hashel(&p.first, size, h) + call = Nod(OCALL, hashel, nil) + + nx = Nod(OXDOT, np, newname(first.Sym)) // TODO: fields from other packages? + na = Nod(OADDR, nx, nil) + na.Etype = 1 // no escape to heap + call.List = list(call.List, na) + call.List = list(call.List, nh) + call.List = list(call.List, Nodintconst(size)) + fn.Nbody = list(fn.Nbody, Nod(OAS, nh, call)) + + first = nil + } + + if t1 == nil { + break + } + if isblanksym(t1.Sym) { + continue + } + + // Run hash for this field. + if algtype1(t1.Type, nil) == AMEM { + hashel = hashmem(t1.Type) + + // h = memhash(&p.t1, h, size) + call = Nod(OCALL, hashel, nil) + + nx = Nod(OXDOT, np, newname(t1.Sym)) // TODO: fields from other packages? + na = Nod(OADDR, nx, nil) + na.Etype = 1 // no escape to heap + call.List = list(call.List, na) + call.List = list(call.List, nh) + call.List = list(call.List, Nodintconst(t1.Type.Width)) + fn.Nbody = list(fn.Nbody, Nod(OAS, nh, call)) + } else { + hashel = hashfor(t1.Type) + + // h = hashel(&p.t1, h) + call = Nod(OCALL, hashel, nil) + + nx = Nod(OXDOT, np, newname(t1.Sym)) // TODO: fields from other packages? + na = Nod(OADDR, nx, nil) + na.Etype = 1 // no escape to heap + call.List = list(call.List, na) + call.List = list(call.List, nh) + fn.Nbody = list(fn.Nbody, Nod(OAS, nh, call)) + } + } + } + + r := Nod(ORETURN, nil, nil) + r.List = list(r.List, nh) + fn.Nbody = list(fn.Nbody, r) + + if Debug['r'] != 0 { + dumplist("genhash body", fn.Nbody) + } + + funcbody(fn) + Curfn = fn + fn.Func.Dupok = true + typecheck(&fn, Etop) + typechecklist(fn.Nbody, Etop) + Curfn = nil + + // Disable safemode while compiling this code: the code we + // generate internally can refer to unsafe.Pointer. + // In this case it can happen if we need to generate an == + // for a struct containing a reflect.Value, which itself has + // an unexported field of type unsafe.Pointer. + old_safemode := safemode + + safemode = 0 + funccompile(fn) + safemode = old_safemode +} + +// Return node for +// if p.field != q.field { return false } +func eqfield(p *Node, q *Node, field *Node) *Node { + nx := Nod(OXDOT, p, field) + ny := Nod(OXDOT, q, field) + nif := Nod(OIF, nil, nil) + nif.Ntest = Nod(ONE, nx, ny) + r := Nod(ORETURN, nil, nil) + r.List = list(r.List, Nodbool(false)) + nif.Nbody = list(nif.Nbody, r) + return nif +} + +func eqmemfunc(size int64, type_ *Type, needsize *int) *Node { + var fn *Node + + switch size { + default: + fn = syslook("memequal", 1) + *needsize = 1 + + case 1, 2, 4, 8, 16: + buf := fmt.Sprintf("memequal%d", int(size)*8) + fn = syslook(buf, 1) + *needsize = 0 + } + + substArgTypes(fn, type_, type_) + return fn +} + +// Return node for +// if !memequal(&p.field, &q.field [, size]) { return false } +func eqmem(p *Node, q *Node, field *Node, size int64) *Node { + var needsize int + + nx := Nod(OADDR, Nod(OXDOT, p, field), nil) + nx.Etype = 1 // does not escape + ny := Nod(OADDR, Nod(OXDOT, q, field), nil) + ny.Etype = 1 // does not escape + typecheck(&nx, Erv) + typecheck(&ny, Erv) + + call := Nod(OCALL, eqmemfunc(size, nx.Type.Type, &needsize), nil) + call.List = list(call.List, nx) + call.List = list(call.List, ny) + if needsize != 0 { + call.List = list(call.List, Nodintconst(size)) + } + + nif := Nod(OIF, nil, nil) + nif.Ntest = Nod(ONOT, call, nil) + r := Nod(ORETURN, nil, nil) + r.List = list(r.List, Nodbool(false)) + nif.Nbody = list(nif.Nbody, r) + return nif +} + +/* + * Generate a helper function to check equality of two values of type t. + */ +func geneq(sym *Sym, t *Type) { + if Debug['r'] != 0 { + fmt.Printf("geneq %v %v\n", sym, t) + } + + lineno = 1 // less confusing than end of input + dclcontext = PEXTERN + markdcl() + + // func sym(p, q *T) bool + fn := Nod(ODCLFUNC, nil, nil) + + fn.Nname = newname(sym) + fn.Nname.Class = PFUNC + tfn := Nod(OTFUNC, nil, nil) + fn.Nname.Ntype = tfn + + n := Nod(ODCLFIELD, newname(Lookup("p")), typenod(Ptrto(t))) + tfn.List = list(tfn.List, n) + np := n.Left + n = Nod(ODCLFIELD, newname(Lookup("q")), typenod(Ptrto(t))) + tfn.List = list(tfn.List, n) + nq := n.Left + n = Nod(ODCLFIELD, nil, typenod(Types[TBOOL])) + tfn.Rlist = list(tfn.Rlist, n) + + funchdr(fn) + + // geneq is only called for types that have equality but + // cannot be handled by the standard algorithms, + // so t must be either an array or a struct. + switch t.Etype { + default: + Fatal("geneq %v", t) + + case TARRAY: + if Isslice(t) { + Fatal("geneq %v", t) + } + + // An array of pure memory would be handled by the + // standard memequal, so the element type must not be + // pure memory. Even if we unrolled the range loop, + // each iteration would be a function call, so don't bother + // unrolling. + nrange := Nod(ORANGE, nil, Nod(OIND, np, nil)) + + ni := newname(Lookup("i")) + ni.Type = Types[TINT] + nrange.List = list1(ni) + nrange.Colas = true + colasdefn(nrange.List, nrange) + ni = nrange.List.N + + // if p[i] != q[i] { return false } + nx := Nod(OINDEX, np, ni) + + nx.Bounded = true + ny := Nod(OINDEX, nq, ni) + ny.Bounded = true + + nif := Nod(OIF, nil, nil) + nif.Ntest = Nod(ONE, nx, ny) + r := Nod(ORETURN, nil, nil) + r.List = list(r.List, Nodbool(false)) + nif.Nbody = list(nif.Nbody, r) + nrange.Nbody = list(nrange.Nbody, nif) + fn.Nbody = list(fn.Nbody, nrange) + + // Walk the struct using memequal for runs of AMEM + // and calling specific equality tests for the others. + // Skip blank-named fields. + case TSTRUCT: + var first *Type + + offend := int64(0) + var size int64 + for t1 := t.Type; ; t1 = t1.Down { + if t1 != nil && algtype1(t1.Type, nil) == AMEM && !isblanksym(t1.Sym) { + offend = t1.Width + t1.Type.Width + if first == nil { + first = t1 + } + + // If it's a memory field but it's padded, stop here. + if ispaddedfield(t1, t.Width) { + t1 = t1.Down + } else { + continue + } + } + + // Run memequal for fields up to this one. + // TODO(rsc): All the calls to newname are wrong for + // cross-package unexported fields. + if first != nil { + if first.Down == t1 { + fn.Nbody = list(fn.Nbody, eqfield(np, nq, newname(first.Sym))) + } else if first.Down.Down == t1 { + fn.Nbody = list(fn.Nbody, eqfield(np, nq, newname(first.Sym))) + first = first.Down + if !isblanksym(first.Sym) { + fn.Nbody = list(fn.Nbody, eqfield(np, nq, newname(first.Sym))) + } + } else { + // More than two fields: use memequal. + size = offend - first.Width // first->width is offset + fn.Nbody = list(fn.Nbody, eqmem(np, nq, newname(first.Sym), size)) + } + + first = nil + } + + if t1 == nil { + break + } + if isblanksym(t1.Sym) { + continue + } + + // Check this field, which is not just memory. + fn.Nbody = list(fn.Nbody, eqfield(np, nq, newname(t1.Sym))) + } + } + + // return true + r := Nod(ORETURN, nil, nil) + + r.List = list(r.List, Nodbool(true)) + fn.Nbody = list(fn.Nbody, r) + + if Debug['r'] != 0 { + dumplist("geneq body", fn.Nbody) + } + + funcbody(fn) + Curfn = fn + fn.Func.Dupok = true + typecheck(&fn, Etop) + typechecklist(fn.Nbody, Etop) + Curfn = nil + + // Disable safemode while compiling this code: the code we + // generate internally can refer to unsafe.Pointer. + // In this case it can happen if we need to generate an == + // for a struct containing a reflect.Value, which itself has + // an unexported field of type unsafe.Pointer. + old_safemode := safemode + + safemode = 0 + funccompile(fn) + safemode = old_safemode +} + +func ifacelookdot(s *Sym, t *Type, followptr *int, ignorecase int) *Type { + *followptr = 0 + + if t == nil { + return nil + } + + var m *Type + var i int + var c int + for d := 0; d < len(dotlist); d++ { + c = adddot1(s, t, d, &m, ignorecase) + if c > 1 { + Yyerror("%v.%v is ambiguous", t, s) + return nil + } + + if c == 1 { + for i = 0; i < d; i++ { + if Isptr[dotlist[i].field.Type.Etype] { + *followptr = 1 + break + } + } + + if m.Type.Etype != TFUNC || m.Type.Thistuple == 0 { + Yyerror("%v.%v is a field, not a method", t, s) + return nil + } + + return m + } + } + + return nil +} + +func implements(t *Type, iface *Type, m **Type, samename **Type, ptr *int) bool { + t0 := t + if t == nil { + return false + } + + // if this is too slow, + // could sort these first + // and then do one loop. + + if t.Etype == TINTER { + var tm *Type + for im := iface.Type; im != nil; im = im.Down { + for tm = t.Type; tm != nil; tm = tm.Down { + if tm.Sym == im.Sym { + if Eqtype(tm.Type, im.Type) { + goto found + } + *m = im + *samename = tm + *ptr = 0 + return false + } + } + + *m = im + *samename = nil + *ptr = 0 + return false + found: + } + + return true + } + + t = methtype(t, 0) + if t != nil { + expandmeth(t) + } + var tm *Type + var imtype *Type + var followptr int + var rcvr *Type + for im := iface.Type; im != nil; im = im.Down { + imtype = methodfunc(im.Type, nil) + tm = ifacelookdot(im.Sym, t, &followptr, 0) + if tm == nil || tm.Nointerface || !Eqtype(methodfunc(tm.Type, nil), imtype) { + if tm == nil { + tm = ifacelookdot(im.Sym, t, &followptr, 1) + } + *m = im + *samename = tm + *ptr = 0 + return false + } + + // if pointer receiver in method, + // the method does not exist for value types. + rcvr = getthisx(tm.Type).Type.Type + + if Isptr[rcvr.Etype] && !Isptr[t0.Etype] && followptr == 0 && !isifacemethod(tm.Type) { + if false && Debug['r'] != 0 { + Yyerror("interface pointer mismatch") + } + + *m = im + *samename = nil + *ptr = 1 + return false + } + } + + return true +} + +/* + * even simpler simtype; get rid of ptr, bool. + * assuming that the front end has rejected + * all the invalid conversions (like ptr -> bool) + */ +func Simsimtype(t *Type) int { + if t == nil { + return 0 + } + + et := int(Simtype[t.Etype]) + switch et { + case TPTR32: + et = TUINT32 + + case TPTR64: + et = TUINT64 + + case TBOOL: + et = TUINT8 + } + + return et +} + +func listtreecopy(l *NodeList) *NodeList { + var out *NodeList + for ; l != nil; l = l.Next { + out = list(out, treecopy(l.N)) + } + return out +} + +func liststmt(l *NodeList) *Node { + n := Nod(OBLOCK, nil, nil) + n.List = l + if l != nil { + n.Lineno = l.N.Lineno + } + return n +} + +/* + * return nelem of list + */ +func structcount(t *Type) int { + var s Iter + + v := 0 + for t = Structfirst(&s, &t); t != nil; t = structnext(&s) { + v++ + } + return v +} + +/* + * return power of 2 of the constant + * operand. -1 if it is not a power of 2. + * 1000+ if it is a -(power of 2) + */ +func powtwo(n *Node) int { + if n == nil || n.Op != OLITERAL || n.Type == nil { + return -1 + } + if !Isint[n.Type.Etype] { + return -1 + } + + v := uint64(Mpgetfix(n.Val.U.(*Mpint))) + b := uint64(1) + for i := 0; i < 64; i++ { + if b == v { + return i + } + b = b << 1 + } + + if !Issigned[n.Type.Etype] { + return -1 + } + + v = -v + b = 1 + for i := 0; i < 64; i++ { + if b == v { + return i + 1000 + } + b = b << 1 + } + + return -1 +} + +/* + * return the unsigned type for + * a signed integer type. + * returns T if input is not a + * signed integer type. + */ +func tounsigned(t *Type) *Type { + // this is types[et+1], but not sure + // that this relation is immutable + switch t.Etype { + default: + fmt.Printf("tounsigned: unknown type %v\n", t) + t = nil + + case TINT: + t = Types[TUINT] + + case TINT8: + t = Types[TUINT8] + + case TINT16: + t = Types[TUINT16] + + case TINT32: + t = Types[TUINT32] + + case TINT64: + t = Types[TUINT64] + } + + return t +} + +/* + * magic number for signed division + * see hacker's delight chapter 10 + */ +func Smagic(m *Magic) { + var mask uint64 + + m.Bad = 0 + switch m.W { + default: + m.Bad = 1 + return + + case 8: + mask = 0xff + + case 16: + mask = 0xffff + + case 32: + mask = 0xffffffff + + case 64: + mask = 0xffffffffffffffff + } + + two31 := mask ^ (mask >> 1) + + p := m.W - 1 + ad := uint64(m.Sd) + if m.Sd < 0 { + ad = -uint64(m.Sd) + } + + // bad denominators + if ad == 0 || ad == 1 || ad == two31 { + m.Bad = 1 + return + } + + t := two31 + ad &= mask + + anc := t - 1 - t%ad + anc &= mask + + q1 := two31 / anc + r1 := two31 - q1*anc + q1 &= mask + r1 &= mask + + q2 := two31 / ad + r2 := two31 - q2*ad + q2 &= mask + r2 &= mask + + var delta uint64 + for { + p++ + q1 <<= 1 + r1 <<= 1 + q1 &= mask + r1 &= mask + if r1 >= anc { + q1++ + r1 -= anc + q1 &= mask + r1 &= mask + } + + q2 <<= 1 + r2 <<= 1 + q2 &= mask + r2 &= mask + if r2 >= ad { + q2++ + r2 -= ad + q2 &= mask + r2 &= mask + } + + delta = ad - r2 + delta &= mask + if q1 < delta || (q1 == delta && r1 == 0) { + continue + } + + break + } + + m.Sm = int64(q2 + 1) + if uint64(m.Sm)&two31 != 0 { + m.Sm |= ^int64(mask) + } + m.S = p - m.W +} + +/* + * magic number for unsigned division + * see hacker's delight chapter 10 + */ +func Umagic(m *Magic) { + var mask uint64 + + m.Bad = 0 + m.Ua = 0 + + switch m.W { + default: + m.Bad = 1 + return + + case 8: + mask = 0xff + + case 16: + mask = 0xffff + + case 32: + mask = 0xffffffff + + case 64: + mask = 0xffffffffffffffff + } + + two31 := mask ^ (mask >> 1) + + m.Ud &= mask + if m.Ud == 0 || m.Ud == two31 { + m.Bad = 1 + return + } + + nc := mask - (-m.Ud&mask)%m.Ud + p := m.W - 1 + + q1 := two31 / nc + r1 := two31 - q1*nc + q1 &= mask + r1 &= mask + + q2 := (two31 - 1) / m.Ud + r2 := (two31 - 1) - q2*m.Ud + q2 &= mask + r2 &= mask + + var delta uint64 + for { + p++ + if r1 >= nc-r1 { + q1 <<= 1 + q1++ + r1 <<= 1 + r1 -= nc + } else { + q1 <<= 1 + r1 <<= 1 + } + + q1 &= mask + r1 &= mask + if r2+1 >= m.Ud-r2 { + if q2 >= two31-1 { + m.Ua = 1 + } + + q2 <<= 1 + q2++ + r2 <<= 1 + r2++ + r2 -= m.Ud + } else { + if q2 >= two31 { + m.Ua = 1 + } + + q2 <<= 1 + r2 <<= 1 + r2++ + } + + q2 &= mask + r2 &= mask + + delta = m.Ud - 1 - r2 + delta &= mask + + if p < m.W+m.W { + if q1 < delta || (q1 == delta && r1 == 0) { + continue + } + } + + break + } + + m.Um = q2 + 1 + m.S = p - m.W +} + +func ngotype(n *Node) *Sym { + if n.Type != nil { + return typenamesym(n.Type) + } + return nil +} + +/* + * Convert raw string to the prefix that will be used in the symbol + * table. All control characters, space, '%' and '"', as well as + * non-7-bit clean bytes turn into %xx. The period needs escaping + * only in the last segment of the path, and it makes for happier + * users if we escape that as little as possible. + * + * If you edit this, edit ../ld/lib.c:/^pathtoprefix too. + * If you edit this, edit ../../debug/goobj/read.go:/importPathToPrefix too. + */ +func pathtoprefix(s string) string { + slash := strings.LastIndex(s, "/") + for i := 0; i < len(s); i++ { + c := s[i] + if c <= ' ' || i >= slash && c == '.' || c == '%' || c == '"' || c >= 0x7F { + var buf bytes.Buffer + for i := 0; i < len(s); i++ { + c := s[i] + if c <= ' ' || i >= slash && c == '.' || c == '%' || c == '"' || c >= 0x7F { + fmt.Fprintf(&buf, "%%%02x", c) + continue + } + buf.WriteByte(c) + } + return buf.String() + } + } + return s +} + +var pkgMap = make(map[string]*Pkg) +var pkgs []*Pkg + +func mkpkg(path string) *Pkg { + if p := pkgMap[path]; p != nil { + return p + } + + p := new(Pkg) + p.Path = path + p.Prefix = pathtoprefix(path) + p.Syms = make(map[string]*Sym) + pkgMap[path] = p + pkgs = append(pkgs, p) + return p +} + +func addinit(np **Node, init *NodeList) { + if init == nil { + return + } + + n := *np + switch n.Op { + // There may be multiple refs to this node; + // introduce OCONVNOP to hold init list. + case ONAME, OLITERAL: + n = Nod(OCONVNOP, n, nil) + + n.Type = n.Left.Type + n.Typecheck = 1 + *np = n + } + + n.Ninit = concat(init, n.Ninit) + n.Ullman = UINF +} + +var reservedimports = []string{ + "go", + "type", +} + +func isbadimport(path string) bool { + if strings.Contains(path, "\x00") { + Yyerror("import path contains NUL") + return true + } + + for i := 0; i < len(reservedimports); i++ { + if path == reservedimports[i] { + Yyerror("import path %q is reserved and cannot be used", path) + return true + } + } + + var s string + _ = s + var r uint + _ = r + for _, r := range path { + if r == utf8.RuneError { + Yyerror("import path contains invalid UTF-8 sequence: %q", path) + return true + } + + if r < 0x20 || r == 0x7f { + Yyerror("import path contains control character: %q", path) + return true + } + + if r == '\\' { + Yyerror("import path contains backslash; use slash: %q", path) + return true + } + + if unicode.IsSpace(rune(r)) { + Yyerror("import path contains space character: %q", path) + return true + } + + if strings.ContainsRune("!\"#$%&'()*,:;<=>?[]^`{|}", r) { + Yyerror("import path contains invalid character '%c': %q", r, path) + return true + } + } + + return false +} + +func checknil(x *Node, init **NodeList) { + if Isinter(x.Type) { + x = Nod(OITAB, x, nil) + typecheck(&x, Erv) + } + + n := Nod(OCHECKNIL, x, nil) + n.Typecheck = 1 + *init = list(*init, n) +} + +/* + * Can this type be stored directly in an interface word? + * Yes, if the representation is a single pointer. + */ +func isdirectiface(t *Type) bool { + switch t.Etype { + case TPTR32, + TPTR64, + TCHAN, + TMAP, + TFUNC, + TUNSAFEPTR: + return true + + // Array of 1 direct iface type can be direct. + case TARRAY: + return t.Bound == 1 && isdirectiface(t.Type) + + // Struct with 1 field of direct iface type can be direct. + case TSTRUCT: + return t.Type != nil && t.Type.Down == nil && isdirectiface(t.Type.Type) + } + + return false +} + +// type2IET returns "T" if t is a concrete type, +// "I" if t is an interface type, and "E" if t is an empty interface type. +// It is used to build calls to the conv* and assert* runtime routines. +func type2IET(t *Type) string { + if isnilinter(t) { + return "E" + } + if Isinter(t) { + return "I" + } + return "T" +} diff --git a/src/cmd/compile/internal/gc/swt.go b/src/cmd/compile/internal/gc/swt.go new file mode 100644 index 0000000000..e8f15a5ce5 --- /dev/null +++ b/src/cmd/compile/internal/gc/swt.go @@ -0,0 +1,840 @@ +// Copyright 2009 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 gc + +import ( + "cmd/internal/obj" + "fmt" + "sort" + "strconv" +) + +const ( + // expression switch + switchKindExpr = iota // switch a {...} or switch 5 {...} + switchKindTrue // switch true {...} or switch {...} + switchKindFalse // switch false {...} + + // type switch + switchKindType // switch a.(type) {...} +) + +const ( + caseKindDefault = iota // default: + + // expression switch + caseKindExprConst // case 5: + caseKindExprVar // case x: + + // type switch + caseKindTypeNil // case nil: + caseKindTypeConst // case time.Time: (concrete type, has type hash) + caseKindTypeVar // case io.Reader: (interface type) +) + +const binarySearchMin = 4 // minimum number of cases for binary search + +// An exprSwitch walks an expression switch. +type exprSwitch struct { + exprname *Node // node for the expression being switched on + kind int // kind of switch statement (switchKind*) +} + +// A typeSwitch walks a type switch. +type typeSwitch struct { + hashname *Node // node for the hash of the type of the variable being switched on + facename *Node // node for the concrete type of the variable being switched on + okname *Node // boolean node used for comma-ok type assertions +} + +// A caseClause is a single case clause in a switch statement. +type caseClause struct { + node *Node // points at case statement + ordinal int // position in switch + hash uint32 // hash of a type switch + typ uint8 // type of case +} + +// typecheckswitch typechecks a switch statement. +func typecheckswitch(n *Node) { + lno := int(lineno) + typechecklist(n.Ninit, Etop) + + var nilonly string + var top int + var t *Type + + if n.Ntest != nil && n.Ntest.Op == OTYPESW { + // type switch + top = Etype + typecheck(&n.Ntest.Right, Erv) + t = n.Ntest.Right.Type + if t != nil && t.Etype != TINTER { + Yyerror("cannot type switch on non-interface value %v", Nconv(n.Ntest.Right, obj.FmtLong)) + } + } else { + // expression switch + top = Erv + if n.Ntest != nil { + typecheck(&n.Ntest, Erv) + defaultlit(&n.Ntest, nil) + t = n.Ntest.Type + } else { + t = Types[TBOOL] + } + if t != nil { + var badtype *Type + switch { + case !okforeq[t.Etype]: + Yyerror("cannot switch on %v", Nconv(n.Ntest, obj.FmtLong)) + case t.Etype == TARRAY && !Isfixedarray(t): + nilonly = "slice" + case t.Etype == TARRAY && Isfixedarray(t) && algtype1(t, nil) == ANOEQ: + Yyerror("cannot switch on %v", Nconv(n.Ntest, obj.FmtLong)) + case t.Etype == TSTRUCT && algtype1(t, &badtype) == ANOEQ: + Yyerror("cannot switch on %v (struct containing %v cannot be compared)", Nconv(n.Ntest, obj.FmtLong), badtype) + case t.Etype == TFUNC: + nilonly = "func" + case t.Etype == TMAP: + nilonly = "map" + } + } + } + + n.Type = t + + var def *Node + var ll *NodeList + for l := n.List; l != nil; l = l.Next { + ncase := l.N + setlineno(n) + if ncase.List == nil { + // default + if def != nil { + Yyerror("multiple defaults in switch (first at %v)", def.Line()) + } else { + def = ncase + } + } else { + for ll = ncase.List; ll != nil; ll = ll.Next { + setlineno(ll.N) + typecheck(&ll.N, Erv|Etype) + if ll.N.Type == nil || t == nil { + continue + } + setlineno(ncase) + switch top { + // expression switch + case Erv: + defaultlit(&ll.N, t) + switch { + case ll.N.Op == OTYPE: + Yyerror("type %v is not an expression", ll.N.Type) + case ll.N.Type != nil && assignop(ll.N.Type, t, nil) == 0 && assignop(t, ll.N.Type, nil) == 0: + if n.Ntest != nil { + Yyerror("invalid case %v in switch on %v (mismatched types %v and %v)", ll.N, n.Ntest, ll.N.Type, t) + } else { + Yyerror("invalid case %v in switch (mismatched types %v and bool)", ll.N, ll.N.Type) + } + case nilonly != "" && !Isconst(ll.N, CTNIL): + Yyerror("invalid case %v in switch (can only compare %s %v to nil)", ll.N, nilonly, n.Ntest) + } + + // type switch + case Etype: + var missing, have *Type + var ptr int + switch { + case ll.N.Op == OLITERAL && Istype(ll.N.Type, TNIL): + case ll.N.Op != OTYPE && ll.N.Type != nil: // should this be ||? + Yyerror("%v is not a type", Nconv(ll.N, obj.FmtLong)) + // reset to original type + ll.N = n.Ntest.Right + case ll.N.Type.Etype != TINTER && t.Etype == TINTER && !implements(ll.N.Type, t, &missing, &have, &ptr): + if have != nil && missing.Broke == 0 && have.Broke == 0 { + Yyerror("impossible type switch case: %v cannot have dynamic type %v"+" (wrong type for %v method)\n\thave %v%v\n\twant %v%v", Nconv(n.Ntest.Right, obj.FmtLong), ll.N.Type, missing.Sym, have.Sym, Tconv(have.Type, obj.FmtShort), missing.Sym, Tconv(missing.Type, obj.FmtShort)) + } else if missing.Broke == 0 { + Yyerror("impossible type switch case: %v cannot have dynamic type %v"+" (missing %v method)", Nconv(n.Ntest.Right, obj.FmtLong), ll.N.Type, missing.Sym) + } + } + } + } + } + + if top == Etype && n.Type != nil { + ll = ncase.List + nvar := ncase.Nname + 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) + } else { + // multiple entry type switch or default + nvar.Ntype = typenod(n.Type) + } + + typecheck(&nvar, Erv|Easgn) + ncase.Nname = nvar + } + } + + typechecklist(ncase.Nbody, Etop) + } + + lineno = int32(lno) +} + +// walkswitch walks a switch statement. +func walkswitch(sw *Node) { + // convert switch {...} to switch true {...} + if sw.Ntest == nil { + sw.Ntest = Nodbool(true) + typecheck(&sw.Ntest, Erv) + } + + if sw.Ntest.Op == OTYPESW { + var s typeSwitch + s.walk(sw) + } else { + var s exprSwitch + s.walk(sw) + } + + // Discard old AST elements. They can confuse racewalk. + sw.Ntest = nil + sw.List = nil +} + +// walk generates an AST implementing sw. +// sw is an expression switch. +// The AST is generally of the form of a linear +// search using if..goto, although binary search +// is used with long runs of constants. +func (s *exprSwitch) walk(sw *Node) { + casebody(sw, nil) + + s.kind = switchKindExpr + if Isconst(sw.Ntest, CTBOOL) { + s.kind = switchKindTrue + if !sw.Ntest.Val.U.(bool) { + s.kind = switchKindFalse + } + } + + walkexpr(&sw.Ntest, &sw.Ninit) + t := sw.Type + if t == nil { + return + } + + // convert the switch into OIF statements + var cas *NodeList + if s.kind == switchKindTrue || s.kind == switchKindFalse { + s.exprname = Nodbool(s.kind == switchKindTrue) + } else if consttype(sw.Ntest) >= 0 { + // leave constants to enable dead code elimination (issue 9608) + s.exprname = sw.Ntest + } else { + s.exprname = temp(sw.Ntest.Type) + cas = list1(Nod(OAS, s.exprname, sw.Ntest)) + typechecklist(cas, Etop) + } + + // enumerate the cases, and lop off the default case + cc := caseClauses(sw, s.kind) + var def *Node + if len(cc) > 0 && cc[0].typ == caseKindDefault { + def = cc[0].node.Right + cc = cc[1:] + } else { + def = Nod(OBREAK, nil, nil) + } + + // handle the cases in order + for len(cc) > 0 { + // deal with expressions one at a time + if !okforcmp[t.Etype] || cc[0].typ != caseKindExprConst { + a := s.walkCases(cc[:1]) + cas = list(cas, a) + cc = cc[1:] + continue + } + + // do binary search on runs of constants + var run int + for run = 1; run < len(cc) && cc[run].typ == caseKindExprConst; run++ { + } + + // sort and compile constants + sort.Sort(caseClauseByExpr(cc[:run])) + a := s.walkCases(cc[:run]) + cas = list(cas, a) + cc = cc[run:] + } + + // handle default case + if nerrors == 0 { + cas = list(cas, def) + sw.Nbody = concat(cas, sw.Nbody) + sw.List = nil + walkstmtlist(sw.Nbody) + } +} + +// walkCases generates an AST implementing the cases in cc. +func (s *exprSwitch) walkCases(cc []*caseClause) *Node { + if len(cc) < binarySearchMin { + // linear search + var cas *NodeList + for _, c := range cc { + n := c.node + lno := int(setlineno(n)) + + a := Nod(OIF, nil, nil) + if (s.kind != switchKindTrue && s.kind != switchKindFalse) || assignop(n.Left.Type, s.exprname.Type, nil) == OCONVIFACE || assignop(s.exprname.Type, n.Left.Type, nil) == OCONVIFACE { + a.Ntest = Nod(OEQ, s.exprname, n.Left) // if name == val + typecheck(&a.Ntest, Erv) + } else if s.kind == switchKindTrue { + a.Ntest = n.Left // if val + } else { + // s.kind == switchKindFalse + a.Ntest = Nod(ONOT, n.Left, nil) // if !val + typecheck(&a.Ntest, Erv) + } + a.Nbody = list1(n.Right) // goto l + + cas = list(cas, a) + lineno = int32(lno) + } + return liststmt(cas) + } + + // find the middle and recur + half := len(cc) / 2 + a := Nod(OIF, nil, nil) + mid := cc[half-1].node.Left + le := Nod(OLE, s.exprname, mid) + if Isconst(mid, CTSTR) { + // Search by length and then by value; see exprcmp. + lenlt := Nod(OLT, Nod(OLEN, s.exprname, nil), Nod(OLEN, mid, nil)) + leneq := Nod(OEQ, Nod(OLEN, s.exprname, nil), Nod(OLEN, mid, nil)) + a.Ntest = Nod(OOROR, lenlt, Nod(OANDAND, leneq, le)) + } else { + a.Ntest = le + } + typecheck(&a.Ntest, Erv) + a.Nbody = list1(s.walkCases(cc[:half])) + a.Nelse = list1(s.walkCases(cc[half:])) + return a +} + +// casebody builds separate lists of statements and cases. +// It makes labels between cases and statements +// and deals with fallthrough, break, and unreachable statements. +func casebody(sw *Node, typeswvar *Node) { + if sw.List == nil { + return + } + + lno := setlineno(sw) + + var cas *NodeList // cases + var stat *NodeList // statements + var def *Node // defaults + br := Nod(OBREAK, nil, nil) + + for l := sw.List; l != nil; l = l.Next { + n := l.N + setlineno(n) + if n.Op != OXCASE { + Fatal("casebody %v", Oconv(int(n.Op), 0)) + } + n.Op = OCASE + needvar := count(n.List) != 1 || n.List.N.Op == OLITERAL + + jmp := Nod(OGOTO, newCaseLabel(), nil) + if n.List == nil { + if def != nil { + Yyerror("more than one default case") + } + // reuse original default case + n.Right = jmp + def = n + } + + if n.List != nil && n.List.Next == nil { + // one case -- reuse OCASE node + n.Left = n.List.N + n.Right = jmp + n.List = nil + cas = list(cas, n) + } else { + // expand multi-valued cases + for lc := n.List; lc != nil; lc = lc.Next { + cas = list(cas, Nod(OCASE, lc.N, jmp)) + } + } + + stat = list(stat, Nod(OLABEL, jmp.Left, nil)) + if typeswvar != nil && needvar && n.Nname != nil { + l := list1(Nod(ODCL, n.Nname, nil)) + l = list(l, Nod(OAS, n.Nname, typeswvar)) + typechecklist(l, Etop) + stat = concat(stat, l) + } + stat = concat(stat, n.Nbody) + + // botch - shouldn't fall thru declaration + last := stat.End.N + if last.Xoffset == n.Xoffset && last.Op == OXFALL { + if typeswvar != nil { + setlineno(last) + Yyerror("cannot fallthrough in type switch") + } + + if l.Next == nil { + setlineno(last) + Yyerror("cannot fallthrough final case in switch") + } + + last.Op = OFALL + } else { + stat = list(stat, br) + } + } + + stat = list(stat, br) + if def != nil { + cas = list(cas, def) + } + + sw.List = cas + sw.Nbody = stat + lineno = lno +} + +// nSwitchLabel is the number of switch labels generated. +// This should be per-function, but it is a global counter for now. +var nSwitchLabel int + +func newCaseLabel() *Node { + label := strconv.Itoa(nSwitchLabel) + nSwitchLabel++ + return newname(Lookup(label)) +} + +// caseClauses generates a slice of caseClauses +// corresponding to the clauses in the switch statement sw. +// Kind is the kind of switch statement. +func caseClauses(sw *Node, kind int) []*caseClause { + var cc []*caseClause + for l := sw.List; l != nil; l = l.Next { + n := l.N + c := new(caseClause) + cc = append(cc, c) + c.ordinal = len(cc) + c.node = n + + if n.Left == nil { + c.typ = caseKindDefault + continue + } + + if kind == switchKindType { + // type switch + switch { + case n.Left.Op == OLITERAL: + c.typ = caseKindTypeNil + case Istype(n.Left.Type, TINTER): + c.typ = caseKindTypeVar + default: + c.typ = caseKindTypeConst + c.hash = typehash(n.Left.Type) + } + } else { + // expression switch + switch consttype(n.Left) { + case CTFLT, CTINT, CTRUNE, CTSTR: + c.typ = caseKindExprConst + default: + c.typ = caseKindExprVar + } + } + } + + if cc == nil { + return nil + } + + // sort by value and diagnose duplicate cases + if kind == switchKindType { + // type switch + sort.Sort(caseClauseByType(cc)) + for i, c1 := range cc { + if c1.typ == caseKindTypeNil || c1.typ == caseKindDefault { + break + } + for _, c2 := range cc[i+1:] { + if c2.typ == caseKindTypeNil || c2.typ == caseKindDefault || c1.hash != c2.hash { + break + } + if Eqtype(c1.node.Left.Type, c2.node.Left.Type) { + yyerrorl(int(c2.node.Lineno), "duplicate case %v in type switch\n\tprevious case at %v", c2.node.Left.Type, c1.node.Line()) + } + } + } + } else { + // expression switch + sort.Sort(caseClauseByExpr(cc)) + for i, c1 := range cc { + if i+1 == len(cc) { + break + } + c2 := cc[i+1] + if exprcmp(c1, c2) != 0 { + continue + } + setlineno(c2.node) + Yyerror("duplicate case %v in switch\n\tprevious case at %v", c1.node.Left, c1.node.Line()) + } + } + + // put list back in processing order + sort.Sort(caseClauseByOrd(cc)) + return cc +} + +// walk generates an AST that implements sw, +// where sw is a type switch. +// The AST is generally of the form of a linear +// search using if..goto, although binary search +// is used with long runs of concrete types. +func (s *typeSwitch) walk(sw *Node) { + if sw.Ntest == nil { + return + } + if sw.Ntest.Right == nil { + setlineno(sw) + Yyerror("type switch must have an assignment") + return + } + + walkexpr(&sw.Ntest.Right, &sw.Ninit) + if !Istype(sw.Ntest.Right.Type, TINTER) { + Yyerror("type switch must be on an interface") + return + } + + var cas *NodeList + + // predeclare temporary variables and the boolean var + s.facename = temp(sw.Ntest.Right.Type) + + a := Nod(OAS, s.facename, sw.Ntest.Right) + typecheck(&a, Etop) + cas = list(cas, a) + + s.okname = temp(Types[TBOOL]) + typecheck(&s.okname, Erv) + + s.hashname = temp(Types[TUINT32]) + typecheck(&s.hashname, Erv) + + // set up labels and jumps + casebody(sw, s.facename) + + // calculate type hash + t := sw.Ntest.Right.Type + if isnilinter(t) { + a = syslook("efacethash", 1) + } else { + a = syslook("ifacethash", 1) + } + substArgTypes(a, t) + a = Nod(OCALL, a, nil) + a.List = list1(s.facename) + a = Nod(OAS, s.hashname, a) + typecheck(&a, Etop) + cas = list(cas, a) + + cc := caseClauses(sw, switchKindType) + var def *Node + if len(cc) > 0 && cc[0].typ == caseKindDefault { + def = cc[0].node.Right + cc = cc[1:] + } else { + def = Nod(OBREAK, nil, nil) + } + + // insert type equality check into each case block + for _, c := range cc { + n := c.node + switch c.typ { + case caseKindTypeNil: + var v Val + v.Ctype = CTNIL + a = Nod(OIF, nil, nil) + a.Ntest = Nod(OEQ, s.facename, nodlit(v)) + typecheck(&a.Ntest, Erv) + a.Nbody = list1(n.Right) // if i==nil { goto l } + n.Right = a + + case caseKindTypeVar, caseKindTypeConst: + n.Right = s.typeone(n) + } + } + + // generate list of if statements, binary search for constant sequences + for len(cc) > 0 { + if cc[0].typ != caseKindTypeConst { + n := cc[0].node + cas = list(cas, n.Right) + cc = cc[1:] + continue + } + + // identify run of constants + var run int + for run = 1; run < len(cc) && cc[run].typ == caseKindTypeConst; run++ { + } + + // sort by hash + sort.Sort(caseClauseByType(cc[:run])) + + // for debugging: linear search + if false { + for i := 0; i < run; i++ { + n := cc[i].node + cas = list(cas, n.Right) + } + continue + } + + // combine adjacent cases with the same hash + ncase := 0 + for i := 0; i < run; i++ { + ncase++ + hash := list1(cc[i].node.Right) + for j := i + 1; j < run && cc[i].hash == cc[j].hash; j++ { + hash = list(hash, cc[j].node.Right) + } + cc[i].node.Right = liststmt(hash) + } + + // binary search among cases to narrow by hash + cas = list(cas, s.walkCases(cc[:ncase])) + cc = cc[ncase:] + } + + // handle default case + if nerrors == 0 { + cas = list(cas, def) + sw.Nbody = concat(cas, sw.Nbody) + sw.List = nil + walkstmtlist(sw.Nbody) + } +} + +// typeone generates an AST that jumps to the +// case body if the variable is of type t. +func (s *typeSwitch) typeone(t *Node) *Node { + name := t.Nname + var init *NodeList + if name == nil { + typecheck(&nblank, Erv|Easgn) + name = nblank + } else { + init = list1(Nod(ODCL, name, nil)) + } + + a := Nod(OAS2, nil, nil) + a.List = list(list1(name), s.okname) // name, ok = + b := Nod(ODOTTYPE, s.facename, nil) + b.Type = t.Left.Type // interface.(type) + a.Rlist = list1(b) + typecheck(&a, Etop) + init = list(init, a) + + c := Nod(OIF, nil, nil) + c.Ntest = s.okname + c.Nbody = list1(t.Right) // if ok { goto l } + + return liststmt(list(init, c)) +} + +// walkCases generates an AST implementing the cases in cc. +func (s *typeSwitch) walkCases(cc []*caseClause) *Node { + if len(cc) < binarySearchMin { + var cas *NodeList + for _, c := range cc { + n := c.node + if c.typ != caseKindTypeConst { + Fatal("typeSwitch walkCases") + } + a := Nod(OIF, nil, nil) + a.Ntest = Nod(OEQ, s.hashname, Nodintconst(int64(c.hash))) + typecheck(&a.Ntest, Erv) + a.Nbody = list1(n.Right) + cas = list(cas, a) + } + return liststmt(cas) + } + + // find the middle and recur + half := len(cc) / 2 + a := Nod(OIF, nil, nil) + a.Ntest = Nod(OLE, s.hashname, Nodintconst(int64(cc[half-1].hash))) + typecheck(&a.Ntest, Erv) + a.Nbody = list1(s.walkCases(cc[:half])) + a.Nelse = list1(s.walkCases(cc[half:])) + return a +} + +type caseClauseByOrd []*caseClause + +func (x caseClauseByOrd) Len() int { return len(x) } +func (x caseClauseByOrd) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x caseClauseByOrd) Less(i, j int) bool { + c1, c2 := x[i], x[j] + switch { + // sort default first + case c1.typ == caseKindDefault: + return true + case c2.typ == caseKindDefault: + return false + + // sort nil second + case c1.typ == caseKindTypeNil: + return true + case c2.typ == caseKindTypeNil: + return false + } + + // sort by ordinal + return c1.ordinal < c2.ordinal +} + +type caseClauseByExpr []*caseClause + +func (x caseClauseByExpr) Len() int { return len(x) } +func (x caseClauseByExpr) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x caseClauseByExpr) Less(i, j int) bool { + return exprcmp(x[i], x[j]) < 0 +} + +func exprcmp(c1, c2 *caseClause) int { + // sort non-constants last + if c1.typ != caseKindExprConst { + return +1 + } + if c2.typ != caseKindExprConst { + return -1 + } + + n1 := c1.node.Left + n2 := c2.node.Left + + // sort by type (for switches on interface) + ct := int(n1.Val.Ctype) + if ct > int(n2.Val.Ctype) { + return +1 + } + if ct < int(n2.Val.Ctype) { + return -1 + } + if !Eqtype(n1.Type, n2.Type) { + if n1.Type.Vargen > n2.Type.Vargen { + return +1 + } else { + return -1 + } + } + + // sort by constant value to enable binary search + switch ct { + case CTFLT: + return mpcmpfltflt(n1.Val.U.(*Mpflt), n2.Val.U.(*Mpflt)) + case CTINT, CTRUNE: + 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.(string) + b := n2.Val.U.(string) + if len(a) < len(b) { + return -1 + } + if len(a) > len(b) { + return +1 + } + return stringsCompare(a, b) + } + + return 0 +} + +type caseClauseByType []*caseClause + +func (x caseClauseByType) Len() int { return len(x) } +func (x caseClauseByType) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x caseClauseByType) Less(i, j int) bool { + c1, c2 := x[i], x[j] + switch { + // sort non-constants last + case c1.typ != caseKindTypeConst: + return false + case c2.typ != caseKindTypeConst: + return true + + // sort by hash code + case c1.hash != c2.hash: + return c1.hash < c2.hash + } + + // sort by ordinal + return c1.ordinal < c2.ordinal +} + +func dumpcase(cc []*caseClause) { + for _, c := range cc { + switch c.typ { + case caseKindDefault: + fmt.Printf("case-default\n") + fmt.Printf("\tord=%d\n", c.ordinal) + + case caseKindExprConst: + fmt.Printf("case-exprconst\n") + fmt.Printf("\tord=%d\n", c.ordinal) + + case caseKindExprVar: + fmt.Printf("case-exprvar\n") + fmt.Printf("\tord=%d\n", c.ordinal) + fmt.Printf("\top=%v\n", Oconv(int(c.node.Left.Op), 0)) + + case caseKindTypeNil: + fmt.Printf("case-typenil\n") + fmt.Printf("\tord=%d\n", c.ordinal) + + case caseKindTypeConst: + fmt.Printf("case-typeconst\n") + fmt.Printf("\tord=%d\n", c.ordinal) + fmt.Printf("\thash=%x\n", c.hash) + + case caseKindTypeVar: + fmt.Printf("case-typevar\n") + fmt.Printf("\tord=%d\n", c.ordinal) + + default: + fmt.Printf("case-???\n") + fmt.Printf("\tord=%d\n", c.ordinal) + fmt.Printf("\top=%v\n", Oconv(int(c.node.Left.Op), 0)) + fmt.Printf("\thash=%x\n", c.hash) + } + } + + fmt.Printf("\n") +} diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go new file mode 100644 index 0000000000..69348d1c2f --- /dev/null +++ b/src/cmd/compile/internal/gc/syntax.go @@ -0,0 +1,469 @@ +// Copyright 2009 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. + +// “Abstract” syntax representation. + +package gc + +// A Node is a single node in the syntax tree. +// Actually the syntax tree is a syntax DAG, because there is only one +// node with Op=ONAME for a given instance of a variable x. +// The same is true for Op=OTYPE and Op=OLITERAL. +type Node struct { + // Tree structure. + // Generic recursive walks should follow these fields. + Left *Node + Right *Node + Ntest *Node + Nincr *Node + Ninit *NodeList + Nbody *NodeList + Nelse *NodeList + 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 + Addable bool // addressable + 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 + Colas bool // OAS resulting from := + Diag uint8 // already printed error about this + Noescape bool // func arguments do not escape; TODO(rsc): move Noescape to Func struct (see CL 7360) + Walkdef uint8 + Typecheck uint8 + Local bool + Dodata uint8 + Initorder uint8 + 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 +} + +// 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 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 + Byval bool // is the variable captured by value or by reference + 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 + Enter *NodeList + Exit *NodeList + Cvars *NodeList // closure params + Dcl *NodeList // autodcl for this func/closure + Inldcl *NodeList // copy of dcl for use in inlining + Closgen int + Outerfunc *Node + + Inl *NodeList // copy of the body for use in inlining + InlCost int32 + + Endlineno int32 + + Nosplit bool // func should not execute on separate stack + Nowritebarrier bool // emit compiler error instead of write barrier + Dupok bool // duplicate definitions ok + Wrapper bool // is method wrapper + Needctxt bool // function uses context register (has closure variables) +} + +// Node ops. +const ( + OXXX = iota + + // names + ONAME // var, const or func name + ONONAME // unnamed arg or return value: f(int, string) (int, error) { etc } + OTYPE // type name + OPACK // import + OLITERAL // literal + + // expressions + OADD // x + y + OSUB // x - y + OOR // x | y + OXOR // x ^ y + OADDSTR // s + "foo" + OADDR // &x + OANDAND // b0 && b1 + OAPPEND // append + OARRAYBYTESTR // string(bytes) + OARRAYBYTESTRTMP // string(bytes) ephemeral + OARRAYRUNESTR // string(runes) + OSTRARRAYBYTE // []byte(s) + OSTRARRAYBYTETMP // []byte(s) ephemeral + OSTRARRAYRUNE // []rune(s) + OAS // x = y or x := y + OAS2 // x, y, z = xx, yy, zz + OAS2FUNC // x, y = f() + OAS2RECV // x, ok = <-c + OAS2MAPR // x, ok = m["foo"] + OAS2DOTTYPE // x, ok = I.(int) + OASOP // x += y + OASWB // OAS but with write barrier + OCALL // function call, method call or type conversion, possibly preceded by defer or go. + OCALLFUNC // f() + OCALLMETH // t.Method() + OCALLINTER // err.Error() + OCALLPART // t.Method (without ()) + OCAP // cap + OCLOSE // close + OCLOSURE // f = func() { etc } + OCMPIFACE // err1 == err2 + OCMPSTR // s1 == s2 + OCOMPLIT // composite literal, typechecking may convert to a more specific OXXXLIT. + OMAPLIT // M{"foo":3, "bar":4} + OSTRUCTLIT // T{x:3, y:4} + OARRAYLIT // [2]int{3, 4} + OPTRLIT // &T{x:3, y:4} + OCONV // var i int; var u uint; i = int(u) + OCONVIFACE // I(t) + OCONVNOP // type Int int; var i int; var j Int; i = int(j) + OCOPY // copy + ODCL // var x int + ODCLFUNC // func f() or func (r) f() + ODCLFIELD // struct field, interface field, or func/method argument/return value. + ODCLCONST // const pi = 3.14 + ODCLTYPE // type Int int + ODELETE // delete + ODOT // t.x + ODOTPTR // p.x that is implicitly (*p).x + ODOTMETH // t.Method + ODOTINTER // err.Error + OXDOT // t.x, typechecking may convert to a more specific ODOTXXX. + ODOTTYPE // e = err.(MyErr) + ODOTTYPE2 // e, ok = err.(MyErr) + OEQ // x == y + ONE // x != y + OLT // x < y + OLE // x <= y + OGE // x >= y + OGT // x > y + OIND // *p + OINDEX // a[i] + OINDEXMAP // m[s] + OKEY // The x:3 in t{x:3, y:4}, the 1:2 in a[1:2], the 2:20 in [3]int{2:20}, etc. + OPARAM // The on-stack copy of a parameter or return value that escapes. + OLEN // len + OMAKE // make, typechecking may convert to a more specific OMAKEXXX. + OMAKECHAN // make(chan int) + OMAKEMAP // make(map[string]int) + OMAKESLICE // make([]int, 0) + OMUL // * + ODIV // x / y + OMOD // x % y + OLSH // x << u + ORSH // x >> u + OAND // x & y + OANDNOT // x &^ y + ONEW // new + ONOT // !b + OCOM // ^x + OPLUS // +x + OMINUS // -y + OOROR // b1 || b2 + OPANIC // panic + OPRINT // print + OPRINTN // println + OPAREN // (x) + OSEND // c <- x + OSLICE // v[1:2], typechecking may convert to a more specific OSLICEXXX. + OSLICEARR // a[1:2] + OSLICESTR // s[1:2] + OSLICE3 // v[1:2:3], typechecking may convert to OSLICE3ARR. + OSLICE3ARR // a[1:2:3] + ORECOVER // recover + ORECV // <-c + ORUNESTR // string(i) + OSELRECV // case x = <-c: + OSELRECV2 // case x, ok = <-c: + OIOTA // iota + OREAL // real + OIMAG // imag + OCOMPLEX // complex + + // statements + OBLOCK // block of code + OBREAK // break + OCASE // case, after being verified by swt.c's casebody. + OXCASE // case, before verification. + OCONTINUE // continue + ODEFER // defer + OEMPTY // no-op + OFALL // fallthrough, after being verified by swt.c's casebody. + OXFALL // fallthrough, before verification. + OFOR // for + OGOTO // goto + OIF // if + OLABEL // label: + OPROC // go + ORANGE // range + ORETURN // return + OSELECT // select + OSWITCH // switch x + OTYPESW // switch err.(type) + + // types + OTCHAN // chan int + OTMAP // map[string]int + OTSTRUCT // struct{} + OTINTER // interface{} + OTFUNC // func() + OTARRAY // []int, [8]int, [N]int or [...]int + + // misc + ODDD // func f(args ...int) or f(l...) or var a = [...]int{0, 1, 2}. + ODDDARG // func f(args ...int), introduced by escape analysis. + OINLCALL // intermediary representation of an inlined call. + OEFACE // itable and data words of an empty-interface value. + OITAB // itable word of an interface value. + OSPTR // base pointer of a slice or string. + OCLOSUREVAR // variable reference at beginning of closure function + OCFUNC // reference to c function pointer (not go func value) + OCHECKNIL // emit code to ensure pointer/interface not nil + OVARKILL // variable is dead + + // thearch-specific registers + OREGISTER // a register, such as AX. + OINDREG // offset plus indirect of a register, such as 8(SP). + + // arch-specific opcodes + OCMP // compare: ACMP. + ODEC // decrement: ADEC. + OINC // increment: AINC. + OEXTEND // extend: ACWD/ACDQ/ACQO. + OHMUL // high mul: AMUL/AIMUL for unsigned/signed (OMUL uses AIMUL for both). + OLROT // left rotate: AROL. + ORROTC // right rotate-carry: ARCR. + ORETJMP // return to other function + OPS // compare parity set (for x86 NaN check) + OPC // compare parity clear (for x86 NaN check) + OSQRT // sqrt(float64), on systems that have hw support + OGETG // runtime.getg() (read g pointer) + + OEND +) + +/* + * Every node has a walkgen field. + * If you want to do a traversal of a node graph that + * might contain duplicates and want to avoid + * visiting the same nodes twice, increment walkgen + * before starting. Then before processing a node, do + * + * if(n->walkgen == walkgen) + * return; + * n->walkgen = walkgen; + * + * Such a walk cannot call another such walk recursively, + * because of the use of the global walkgen. + */ +var walkgen uint32 + +// A NodeList is a linked list of nodes. +// TODO(rsc): Some uses of NodeList should be made into slices. +// The remaining ones probably just need a simple linked list, +// not one with concatenation support. +type NodeList struct { + N *Node + Next *NodeList + End *NodeList +} + +// concat returns the concatenation of the lists a and b. +// The storage taken by both is reused for the result. +func concat(a *NodeList, b *NodeList) *NodeList { + if a == nil { + return b + } + if b == nil { + return a + } + + a.End.Next = b + a.End = b.End + b.End = nil + return a +} + +// list1 returns a one-element list containing n. +func list1(n *Node) *NodeList { + if n == nil { + return nil + } + if n.Op == OBLOCK && n.Ninit == nil { + // Flatten list and steal storage. + // Poison pointer to catch errant uses. + l := n.List + + n.List = nil + return l + } + + l := new(NodeList) + l.N = n + l.End = l + return l +} + +// list returns the result of appending n to l. +func list(l *NodeList, n *Node) *NodeList { + return concat(l, list1(n)) +} + +// listsort sorts *l in place according to the 3-way comparison function f. +// The algorithm is mergesort, so it is guaranteed to be O(n log n). +func listsort(l **NodeList, f func(*Node, *Node) int) { + if *l == nil || (*l).Next == nil { + return + } + + l1 := *l + l2 := *l + for { + l2 = l2.Next + if l2 == nil { + break + } + l2 = l2.Next + if l2 == nil { + break + } + l1 = l1.Next + } + + l2 = l1.Next + l1.Next = nil + l2.End = (*l).End + (*l).End = l1 + + l1 = *l + listsort(&l1, f) + listsort(&l2, f) + + if f(l1.N, l2.N) < 0 { + *l = l1 + } else { + *l = l2 + l2 = l1 + l1 = *l + } + + // now l1 == *l; and l1 < l2 + + var le *NodeList + for (l1 != nil) && (l2 != nil) { + for (l1.Next != nil) && f(l1.Next.N, l2.N) < 0 { + l1 = l1.Next + } + + // l1 is last one from l1 that is < l2 + le = l1.Next // le is the rest of l1, first one that is >= l2 + if le != nil { + le.End = (*l).End + } + + (*l).End = l1 // cut *l at l1 + *l = concat(*l, l2) // glue l2 to *l's tail + + l1 = l2 // l1 is the first element of *l that is < the new l2 + l2 = le // ... because l2 now is the old tail of l1 + } + + *l = concat(*l, l2) // any remainder +} + +// count returns the length of the list l. +func count(l *NodeList) int { + n := int64(0) + for ; l != nil; l = l.Next { + n++ + } + if int64(int(n)) != n { // Overflow. + Yyerror("too many elements in list") + } + return int(n) +} diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go new file mode 100644 index 0000000000..0395ec5f5b --- /dev/null +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -0,0 +1,4069 @@ +// Copyright 2009 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 gc + +import ( + "cmd/internal/obj" + "fmt" + "math" + "strings" +) + +/* + * type check the whole tree of an expression. + * calculates expression types. + * evaluates compile time constants. + * marks variables that escape the local frame. + * rewrites n->op to be more specific in some cases. + */ +var typecheckdefstack *NodeList + +/* + * resolve ONONAME to definition, if any. + */ +func resolve(n *Node) *Node { + if n != nil && n.Op == ONONAME && n.Sym != nil { + r := n.Sym.Def + if r != nil { + if r.Op != OIOTA { + n = r + } else if n.Iota >= 0 { + n = Nodintconst(int64(n.Iota)) + } + } + } + + return n +} + +func typechecklist(l *NodeList, top int) { + for ; l != nil; l = l.Next { + typecheck(&l.N, top) + } +} + +var _typekind = []string{ + TINT: "int", + TUINT: "uint", + TINT8: "int8", + TUINT8: "uint8", + TINT16: "int16", + TUINT16: "uint16", + TINT32: "int32", + TUINT32: "uint32", + TINT64: "int64", + TUINT64: "uint64", + TUINTPTR: "uintptr", + TCOMPLEX64: "complex64", + TCOMPLEX128: "complex128", + TFLOAT32: "float32", + TFLOAT64: "float64", + TBOOL: "bool", + TSTRING: "string", + TPTR32: "pointer", + TPTR64: "pointer", + TUNSAFEPTR: "unsafe.Pointer", + TSTRUCT: "struct", + TINTER: "interface", + TCHAN: "chan", + TMAP: "map", + TARRAY: "array", + TFUNC: "func", + TNIL: "nil", + TIDEAL: "untyped number", +} + +func typekind(t *Type) string { + if Isslice(t) { + return "slice" + } + et := int(t.Etype) + if 0 <= et && et < len(_typekind) { + s := _typekind[et] + if s != "" { + return s + } + } + return fmt.Sprintf("etype=%d", et) +} + +/* + * sprint_depchain prints a dependency chain + * of nodes into fmt. + * It is used by typecheck in the case of OLITERAL nodes + * to print constant definition loops. + */ +func sprint_depchain(fmt_ *string, stack *NodeList, cur *Node, first *Node) { + for l := stack; l != nil; l = l.Next { + if l.N.Op == cur.Op { + if l.N != first { + sprint_depchain(fmt_, l.Next, l.N, first) + } + *fmt_ += fmt.Sprintf("\n\t%v: %v uses %v", l.N.Line(), l.N, cur) + return + } + } +} + +/* + * type check node *np. + * replaces *np with a new pointer in some cases. + * returns the final value of *np as a convenience. + */ + +var typecheck_tcstack *NodeList +var typecheck_tcfree *NodeList + +func typecheck(np **Node, top int) *Node { + // cannot type check until all the source has been parsed + if typecheckok == 0 { + Fatal("early typecheck") + } + + n := *np + if n == nil { + return nil + } + + lno := int(setlineno(n)) + + // Skip over parens. + for n.Op == OPAREN { + n = n.Left + } + + // Resolve definition of name and value of iota lazily. + n = resolve(n) + + *np = n + + // Skip typecheck if already done. + // But re-typecheck ONAME/OTYPE/OLITERAL/OPACK node in case context has changed. + if n.Typecheck == 1 { + switch n.Op { + case ONAME, OTYPE, OLITERAL, OPACK: + break + + default: + lineno = int32(lno) + return n + } + } + + if n.Typecheck == 2 { + // Typechecking loop. Trying printing a meaningful message, + // otherwise a stack trace of typechecking. + var fmt_ string + switch n.Op { + // We can already diagnose variables used as types. + case ONAME: + if top&(Erv|Etype) == Etype { + Yyerror("%v is not a type", n) + } + + case OLITERAL: + if top&(Erv|Etype) == Etype { + Yyerror("%v is not a type", n) + break + } + + fmt_ = "" + sprint_depchain(&fmt_, typecheck_tcstack, n, n) + yyerrorl(int(n.Lineno), "constant definition loop%s", fmt_) + } + + if nsavederrors+nerrors == 0 { + fmt_ = "" + for l := typecheck_tcstack; l != nil; l = l.Next { + fmt_ += fmt.Sprintf("\n\t%v %v", l.N.Line(), l.N) + } + Yyerror("typechecking loop involving %v%s", n, fmt_) + } + + lineno = int32(lno) + return n + } + + n.Typecheck = 2 + + var l *NodeList + if typecheck_tcfree != nil { + l = typecheck_tcfree + typecheck_tcfree = l.Next + } else { + l = new(NodeList) + } + l.Next = typecheck_tcstack + l.N = n + typecheck_tcstack = l + + typecheck1(&n, top) + *np = n + n.Typecheck = 1 + + if typecheck_tcstack != l { + Fatal("typecheck stack out of sync") + } + typecheck_tcstack = l.Next + l.Next = typecheck_tcfree + typecheck_tcfree = l + + lineno = int32(lno) + return n +} + +/* + * does n contain a call or receive operation? + */ +func callrecv(n *Node) bool { + if n == nil { + return false + } + + switch n.Op { + case OCALL, + OCALLMETH, + OCALLINTER, + OCALLFUNC, + ORECV, + OCAP, + OLEN, + OCOPY, + ONEW, + OAPPEND, + ODELETE: + return true + } + + return callrecv(n.Left) || callrecv(n.Right) || callrecv(n.Ntest) || callrecv(n.Nincr) || callrecvlist(n.Ninit) || callrecvlist(n.Nbody) || callrecvlist(n.Nelse) || callrecvlist(n.List) || callrecvlist(n.Rlist) +} + +func callrecvlist(l *NodeList) bool { + for ; l != nil; l = l.Next { + if callrecv(l.N) { + return true + } + } + return false +} + +// indexlit implements typechecking of untyped values as +// array/slice indexes. It is equivalent to defaultlit +// except for constants of numerical kind, which are acceptable +// whenever they can be represented by a value of type int. +func indexlit(np **Node) { + n := *np + if n == nil || !isideal(n.Type) { + return + } + switch consttype(n) { + case CTINT, CTRUNE, CTFLT, CTCPLX: + defaultlit(np, Types[TINT]) + } + + defaultlit(np, nil) +} + +func typecheck1(np **Node, top int) { + n := *np + defer func() { + *np = n + }() + + if n.Sym != nil { + if n.Op == ONAME && n.Etype != 0 && top&Ecall == 0 { + Yyerror("use of builtin %v not in function call", n.Sym) + n.Type = nil + return + } + + typecheckdef(n) + if n.Op == ONONAME { + n.Type = nil + return + } + } + + ok := 0 +OpSwitch: + switch n.Op { + // until typecheck is complete, do nothing. + default: + Dump("typecheck", n) + + Fatal("typecheck %v", Oconv(int(n.Op), 0)) + + /* + * names + */ + case OLITERAL: + ok |= Erv + + if n.Type == nil && n.Val.Ctype == CTSTR { + n.Type = idealstring + } + break OpSwitch + + case ONONAME: + ok |= Erv + break OpSwitch + + case ONAME: + if n.Name.Decldepth == 0 { + n.Name.Decldepth = decldepth + } + if n.Etype != 0 { + ok |= Ecall + break OpSwitch + } + + if top&Easgn == 0 { + // not a write to the variable + if isblank(n) { + Yyerror("cannot use _ as value") + n.Type = nil + return + } + + n.Used = true + } + + if top&Ecall == 0 && isunsafebuiltin(n) { + Yyerror("%v is not an expression, must be called", n) + n.Type = nil + return + } + + ok |= Erv + break OpSwitch + + case OPACK: + Yyerror("use of package %v without selector", n.Sym) + n.Type = nil + return + + case ODDD: + break + + /* + * types (OIND is with exprs) + */ + case OTYPE: + ok |= Etype + + if n.Type == nil { + n.Type = nil + return + } + + case OTARRAY: + ok |= Etype + t := typ(TARRAY) + l := n.Left + r := n.Right + if l == nil { + t.Bound = -1 // slice + } else if l.Op == ODDD { + t.Bound = -100 // to be filled in + if top&Ecomplit == 0 && n.Diag == 0 { + t.Broke = 1 + n.Diag = 1 + Yyerror("use of [...] array outside of array literal") + } + } else { + l := typecheck(&n.Left, Erv) + var v Val + switch consttype(l) { + case CTINT, CTRUNE: + v = l.Val + + case CTFLT: + v = toint(l.Val) + + default: + if l.Type != nil && Isint[l.Type.Etype] && l.Op != OLITERAL { + Yyerror("non-constant array bound %v", l) + } else { + Yyerror("invalid array bound %v", l) + } + n.Type = nil + return + } + + t.Bound = Mpgetfix(v.U.(*Mpint)) + if doesoverflow(v, Types[TINT]) { + Yyerror("array bound is too large") + n.Type = nil + return + } else if t.Bound < 0 { + Yyerror("array bound must be non-negative") + n.Type = nil + return + } + } + + typecheck(&r, Etype) + if r.Type == nil { + n.Type = nil + return + } + t.Type = r.Type + n.Op = OTYPE + n.Type = t + n.Left = nil + n.Right = nil + if t.Bound != -100 { + checkwidth(t) + } + + case OTMAP: + ok |= Etype + l := typecheck(&n.Left, Etype) + r := typecheck(&n.Right, Etype) + if l.Type == nil || r.Type == nil { + n.Type = nil + return + } + n.Op = OTYPE + n.Type = maptype(l.Type, r.Type) + n.Left = nil + n.Right = nil + + case OTCHAN: + ok |= Etype + l := typecheck(&n.Left, Etype) + if l.Type == nil { + n.Type = nil + return + } + t := typ(TCHAN) + t.Type = l.Type + t.Chan = n.Etype + n.Op = OTYPE + n.Type = t + n.Left = nil + n.Etype = 0 + + case OTSTRUCT: + ok |= Etype + n.Op = OTYPE + n.Type = tostruct(n.List) + if n.Type == nil || n.Type.Broke != 0 { + n.Type = nil + return + } + n.List = nil + + case OTINTER: + ok |= Etype + n.Op = OTYPE + n.Type = tointerface(n.List) + if n.Type == nil { + n.Type = nil + return + } + + case OTFUNC: + ok |= Etype + n.Op = OTYPE + n.Type = functype(n.Left, n.List, n.Rlist) + if n.Type == nil { + n.Type = nil + return + } + + /* + * type or expr + */ + case OIND: + ntop := Erv | Etype + + if top&Eaddr == 0 { // The *x in &*x is not an indirect. + ntop |= Eindir + } + ntop |= top & Ecomplit + l := typecheck(&n.Left, ntop) + t := l.Type + if t == nil { + n.Type = nil + return + } + if l.Op == OTYPE { + ok |= Etype + n.Op = OTYPE + n.Type = Ptrto(l.Type) + n.Left = nil + break OpSwitch + } + + if !Isptr[t.Etype] { + if top&(Erv|Etop) != 0 { + Yyerror("invalid indirect of %v", Nconv(n.Left, obj.FmtLong)) + n.Type = nil + return + } + + break OpSwitch + } + + ok |= Erv + n.Type = t.Type + break OpSwitch + + /* + * arithmetic exprs + */ + case OASOP, + OADD, + OAND, + OANDAND, + OANDNOT, + ODIV, + OEQ, + OGE, + OGT, + OLE, + OLT, + OLSH, + ORSH, + OMOD, + OMUL, + ONE, + OOR, + OOROR, + OSUB, + OXOR: + var l *Node + var op int + var r *Node + if n.Op == OASOP { + ok |= Etop + l = typecheck(&n.Left, Erv) + r = typecheck(&n.Right, Erv) + checkassign(n, n.Left) + if l.Type == nil || r.Type == nil { + n.Type = nil + return + } + op = int(n.Etype) + } else { + ok |= Erv + l = typecheck(&n.Left, Erv|top&Eiota) + r = typecheck(&n.Right, Erv|top&Eiota) + if l.Type == nil || r.Type == nil { + n.Type = nil + return + } + op = int(n.Op) + } + if op == OLSH || op == ORSH { + defaultlit(&r, Types[TUINT]) + n.Right = r + t := r.Type + if !Isint[t.Etype] || Issigned[t.Etype] { + Yyerror("invalid operation: %v (shift count type %v, must be unsigned integer)", n, r.Type) + n.Type = nil + return + } + + t = l.Type + if t != nil && t.Etype != TIDEAL && !Isint[t.Etype] { + Yyerror("invalid operation: %v (shift of type %v)", n, t) + n.Type = nil + return + } + + // no defaultlit for left + // the outer context gives the type + n.Type = l.Type + + break OpSwitch + } + + // ideal mixed with non-ideal + defaultlit2(&l, &r, 0) + + n.Left = l + n.Right = r + if l.Type == nil || r.Type == nil { + n.Type = nil + return + } + t := l.Type + if t.Etype == TIDEAL { + t = r.Type + } + et := int(t.Etype) + if et == TIDEAL { + et = TINT + } + aop := 0 + if iscmp[n.Op] && t.Etype != TIDEAL && !Eqtype(l.Type, r.Type) { + // comparison is okay as long as one side is + // assignable to the other. convert so they have + // the same type. + // + // the only conversion that isn't a no-op is concrete == interface. + // in that case, check comparability of the concrete type. + // The conversion allocates, so only do it if the concrete type is huge. + if r.Type.Etype != TBLANK { + aop = assignop(l.Type, r.Type, nil) + if aop != 0 { + if Isinter(r.Type) && !Isinter(l.Type) && algtype1(l.Type, nil) == ANOEQ { + Yyerror("invalid operation: %v (operator %v not defined on %s)", n, Oconv(int(op), 0), typekind(l.Type)) + n.Type = nil + return + } + + dowidth(l.Type) + if Isinter(r.Type) == Isinter(l.Type) || l.Type.Width >= 1<<16 { + l = Nod(aop, l, nil) + l.Type = r.Type + l.Typecheck = 1 + n.Left = l + } + + t = r.Type + goto converted + } + } + + if l.Type.Etype != TBLANK { + aop = assignop(r.Type, l.Type, nil) + if aop != 0 { + if Isinter(l.Type) && !Isinter(r.Type) && algtype1(r.Type, nil) == ANOEQ { + Yyerror("invalid operation: %v (operator %v not defined on %s)", n, Oconv(int(op), 0), typekind(r.Type)) + n.Type = nil + return + } + + dowidth(r.Type) + if Isinter(r.Type) == Isinter(l.Type) || r.Type.Width >= 1<<16 { + r = Nod(aop, r, nil) + r.Type = l.Type + r.Typecheck = 1 + n.Right = r + } + + t = l.Type + } + } + + converted: + et = int(t.Etype) + } + + if t.Etype != TIDEAL && !Eqtype(l.Type, r.Type) { + defaultlit2(&l, &r, 1) + if n.Op == OASOP && n.Implicit { + Yyerror("invalid operation: %v (non-numeric type %v)", n, l.Type) + n.Type = nil + return + } + + if Isinter(r.Type) == Isinter(l.Type) || aop == 0 { + Yyerror("invalid operation: %v (mismatched types %v and %v)", n, l.Type, r.Type) + n.Type = nil + return + } + } + + if !okfor[op][et] { + Yyerror("invalid operation: %v (operator %v not defined on %s)", n, Oconv(int(op), 0), typekind(t)) + n.Type = nil + return + } + + // okfor allows any array == array, map == map, func == func. + // restrict to slice/map/func == nil and nil == slice/map/func. + if Isfixedarray(l.Type) && algtype1(l.Type, nil) == ANOEQ { + Yyerror("invalid operation: %v (%v cannot be compared)", n, l.Type) + n.Type = nil + return + } + + if Isslice(l.Type) && !isnil(l) && !isnil(r) { + Yyerror("invalid operation: %v (slice can only be compared to nil)", n) + n.Type = nil + return + } + + if l.Type.Etype == TMAP && !isnil(l) && !isnil(r) { + Yyerror("invalid operation: %v (map can only be compared to nil)", n) + n.Type = nil + return + } + + if l.Type.Etype == TFUNC && !isnil(l) && !isnil(r) { + Yyerror("invalid operation: %v (func can only be compared to nil)", n) + n.Type = nil + return + } + + var badtype *Type + if l.Type.Etype == TSTRUCT && algtype1(l.Type, &badtype) == ANOEQ { + Yyerror("invalid operation: %v (struct containing %v cannot be compared)", n, badtype) + n.Type = nil + return + } + + t = l.Type + if iscmp[n.Op] { + evconst(n) + t = idealbool + if n.Op != OLITERAL { + defaultlit2(&l, &r, 1) + n.Left = l + n.Right = r + } + } else if n.Op == OANDAND || n.Op == OOROR { + if l.Type == r.Type { + t = l.Type + } else if l.Type == idealbool { + t = r.Type + } else if r.Type == idealbool { + t = l.Type + } + } else + // non-comparison operators on ideal bools should make them lose their ideal-ness + if t == idealbool { + t = Types[TBOOL] + } + + if et == TSTRING { + if iscmp[n.Op] { + n.Etype = n.Op + n.Op = OCMPSTR + } else if n.Op == OADD { + // create OADDSTR node with list of strings in x + y + z + (w + v) + ... + n.Op = OADDSTR + + if l.Op == OADDSTR { + n.List = l.List + } else { + n.List = list1(l) + } + if r.Op == OADDSTR { + n.List = concat(n.List, r.List) + } else { + n.List = list(n.List, r) + } + n.Left = nil + n.Right = nil + } + } + + if et == TINTER { + if l.Op == OLITERAL && l.Val.Ctype == CTNIL { + // swap for back end + n.Left = r + + n.Right = l + } else if r.Op == OLITERAL && r.Val.Ctype == CTNIL { + } else // leave alone for back end + if Isinter(r.Type) == Isinter(l.Type) { + n.Etype = n.Op + n.Op = OCMPIFACE + } + } + + if (op == ODIV || op == OMOD) && Isconst(r, CTINT) { + if mpcmpfixc(r.Val.U.(*Mpint), 0) == 0 { + Yyerror("division by zero") + n.Type = nil + return + } + } + + n.Type = t + break OpSwitch + + case OCOM, OMINUS, ONOT, OPLUS: + ok |= Erv + l := typecheck(&n.Left, Erv|top&Eiota) + t := l.Type + if t == nil { + n.Type = nil + return + } + if !okfor[n.Op][t.Etype] { + Yyerror("invalid operation: %v %v", Oconv(int(n.Op), 0), t) + n.Type = nil + return + } + + n.Type = t + break OpSwitch + + /* + * exprs + */ + case OADDR: + ok |= Erv + + typecheck(&n.Left, Erv|Eaddr) + if n.Left.Type == nil { + n.Type = nil + return + } + checklvalue(n.Left, "take the address of") + r := outervalue(n.Left) + 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.Orig != l && l.Op == ONAME { + Fatal("found non-orig name node %v", l) + } + l.Addrtaken = true + if l.Param != nil && l.Closure != nil { + l.Closure.Addrtaken = true + } + defaultlit(&n.Left, nil) + l = n.Left + t := l.Type + if t == nil { + n.Type = nil + return + } + n.Type = Ptrto(t) + break OpSwitch + + case OCOMPLIT: + ok |= Erv + typecheckcomplit(&n) + if n.Type == nil { + n.Type = nil + return + } + break OpSwitch + + case OXDOT, ODOT: + if n.Op == OXDOT { + n = adddot(n) + n.Op = ODOT + if n.Left == nil { + n.Type = nil + return + } + } + + typecheck(&n.Left, Erv|Etype) + + defaultlit(&n.Left, nil) + if n.Right.Op != ONAME { + Yyerror("rhs of . must be a name") // impossible + n.Type = nil + return + } + + t := n.Left.Type + if t == nil { + adderrorname(n) + n.Type = nil + return + } + + r := n.Right + + if n.Left.Op == OTYPE { + if !looktypedot(n, t, 0) { + if looktypedot(n, t, 1) { + Yyerror("%v undefined (cannot refer to unexported method %v)", n, n.Right.Sym) + } else { + Yyerror("%v undefined (type %v has no method %v)", n, t, n.Right.Sym) + } + n.Type = nil + return + } + + if n.Type.Etype != TFUNC || n.Type.Thistuple != 1 { + Yyerror("type %v has no method %v", n.Left.Type, Sconv(n.Right.Sym, obj.FmtShort)) + n.Type = nil + n.Type = nil + return + } + + 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 + n.Class = PFUNC + ok = Erv + break OpSwitch + } + + if Isptr[t.Etype] && t.Type.Etype != TINTER { + t = t.Type + if t == nil { + n.Type = nil + return + } + n.Op = ODOTPTR + checkwidth(t) + } + + if isblank(n.Right) { + Yyerror("cannot refer to blank field or method") + n.Type = nil + return + } + + if lookdot(n, t, 0) == nil { + // Legitimate field or method lookup failed, try to explain the error + switch { + case isnilinter(t): + Yyerror("%v undefined (type %v is interface with no methods)", n, n.Left.Type) + + case Isptr[t.Etype] && Isinter(t.Type): + // Pointer to interface is almost always a mistake. + Yyerror("%v undefined (type %v is pointer to interface, not interface)", n, n.Left.Type) + + case lookdot(n, t, 1) != nil: + // Field or method matches by name, but it is not exported. + Yyerror("%v undefined (cannot refer to unexported field or method %v)", n, n.Right.Sym) + + default: + if mt := lookdot(n, t, 2); mt != nil { // Case-insensitive lookup. + Yyerror("%v undefined (type %v has no field or method %v, but does have %v)", n, n.Left.Type, n.Right.Sym, mt.Sym) + } else { + Yyerror("%v undefined (type %v has no field or method %v)", n, n.Left.Type, n.Right.Sym) + } + } + n.Type = nil + return + } + + switch n.Op { + case ODOTINTER, ODOTMETH: + if top&Ecall != 0 { + ok |= Ecall + } else { + typecheckpartialcall(n, r) + ok |= Erv + } + + default: + ok |= Erv + } + + break OpSwitch + + case ODOTTYPE: + ok |= Erv + typecheck(&n.Left, Erv) + defaultlit(&n.Left, nil) + l := n.Left + t := l.Type + if t == nil { + n.Type = nil + return + } + if !Isinter(t) { + Yyerror("invalid type assertion: %v (non-interface type %v on left)", n, t) + n.Type = nil + return + } + + if n.Right != nil { + typecheck(&n.Right, Etype) + n.Type = n.Right.Type + n.Right = nil + if n.Type == nil { + n.Type = nil + return + } + } + + if n.Type != nil && n.Type.Etype != TINTER { + var have *Type + var missing *Type + var ptr int + if !implements(n.Type, t, &missing, &have, &ptr) { + if have != nil && have.Sym == missing.Sym { + Yyerror("impossible type assertion:\n\t%v does not implement %v (wrong type for %v method)\n"+"\t\thave %v%v\n\t\twant %v%v", n.Type, t, missing.Sym, have.Sym, Tconv(have.Type, obj.FmtShort|obj.FmtByte), missing.Sym, Tconv(missing.Type, obj.FmtShort|obj.FmtByte)) + } else if ptr != 0 { + Yyerror("impossible type assertion:\n\t%v does not implement %v (%v method has pointer receiver)", n.Type, t, missing.Sym) + } else if have != nil { + Yyerror("impossible type assertion:\n\t%v does not implement %v (missing %v method)\n"+"\t\thave %v%v\n\t\twant %v%v", n.Type, t, missing.Sym, have.Sym, Tconv(have.Type, obj.FmtShort|obj.FmtByte), missing.Sym, Tconv(missing.Type, obj.FmtShort|obj.FmtByte)) + } else { + Yyerror("impossible type assertion:\n\t%v does not implement %v (missing %v method)", n.Type, t, missing.Sym) + } + n.Type = nil + return + } + } + + break OpSwitch + + case OINDEX: + ok |= Erv + typecheck(&n.Left, Erv) + defaultlit(&n.Left, nil) + implicitstar(&n.Left) + l := n.Left + typecheck(&n.Right, Erv) + r := n.Right + t := l.Type + if t == nil || r.Type == nil { + n.Type = nil + return + } + switch t.Etype { + default: + Yyerror("invalid operation: %v (type %v does not support indexing)", n, t) + n.Type = nil + return + + case TSTRING, TARRAY: + indexlit(&n.Right) + if t.Etype == TSTRING { + n.Type = bytetype + } else { + n.Type = t.Type + } + why := "string" + if t.Etype == TARRAY { + if Isfixedarray(t) { + why = "array" + } else { + why = "slice" + } + } + + if n.Right.Type != nil && !Isint[n.Right.Type.Etype] { + Yyerror("non-integer %s index %v", why, n.Right) + break + } + + if Isconst(n.Right, CTINT) { + 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.(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) + } + } + + case TMAP: + n.Etype = 0 + defaultlit(&n.Right, t.Down) + if n.Right.Type != nil { + n.Right = assignconv(n.Right, t.Down, "map index") + } + n.Type = t.Type + n.Op = OINDEXMAP + } + + break OpSwitch + + case ORECV: + ok |= Etop | Erv + typecheck(&n.Left, Erv) + defaultlit(&n.Left, nil) + l := n.Left + t := l.Type + if t == nil { + n.Type = nil + return + } + if t.Etype != TCHAN { + Yyerror("invalid operation: %v (receive from non-chan type %v)", n, t) + n.Type = nil + return + } + + if t.Chan&Crecv == 0 { + Yyerror("invalid operation: %v (receive from send-only type %v)", n, t) + n.Type = nil + return + } + + n.Type = t.Type + break OpSwitch + + case OSEND: + ok |= Etop + l := typecheck(&n.Left, Erv) + typecheck(&n.Right, Erv) + defaultlit(&n.Left, nil) + l = n.Left + t := l.Type + if t == nil { + n.Type = nil + return + } + if t.Etype != TCHAN { + Yyerror("invalid operation: %v (send to non-chan type %v)", n, t) + n.Type = nil + return + } + + if t.Chan&Csend == 0 { + Yyerror("invalid operation: %v (send to receive-only type %v)", n, t) + n.Type = nil + return + } + + defaultlit(&n.Right, t.Type) + r := n.Right + if r.Type == nil { + n.Type = nil + return + } + n.Right = assignconv(r, l.Type.Type, "send") + + // TODO: more aggressive + n.Etype = 0 + + n.Type = nil + break OpSwitch + + case OSLICE: + ok |= Erv + typecheck(&n.Left, top) + typecheck(&n.Right.Left, Erv) + typecheck(&n.Right.Right, Erv) + defaultlit(&n.Left, nil) + indexlit(&n.Right.Left) + indexlit(&n.Right.Right) + l := n.Left + if Isfixedarray(l.Type) { + if !islvalue(n.Left) { + Yyerror("invalid operation %v (slice of unaddressable value)", n) + n.Type = nil + return + } + + n.Left = Nod(OADDR, n.Left, nil) + n.Left.Implicit = true + typecheck(&n.Left, Erv) + l = n.Left + } + + t := l.Type + if t == nil { + n.Type = nil + return + } + var tp *Type + if Istype(t, TSTRING) { + n.Type = t + n.Op = OSLICESTR + } else if Isptr[t.Etype] && Isfixedarray(t.Type) { + tp = t.Type + n.Type = typ(TARRAY) + n.Type.Type = tp.Type + n.Type.Bound = -1 + dowidth(n.Type) + n.Op = OSLICEARR + } else if Isslice(t) { + n.Type = t + } else { + Yyerror("cannot slice %v (type %v)", l, t) + n.Type = nil + return + } + + lo := n.Right.Left + if lo != nil && checksliceindex(l, lo, tp) < 0 { + n.Type = nil + return + } + hi := n.Right.Right + if hi != nil && checksliceindex(l, hi, tp) < 0 { + n.Type = nil + return + } + if checksliceconst(lo, hi) < 0 { + n.Type = nil + return + } + break OpSwitch + + case OSLICE3: + ok |= Erv + typecheck(&n.Left, top) + typecheck(&n.Right.Left, Erv) + typecheck(&n.Right.Right.Left, Erv) + typecheck(&n.Right.Right.Right, Erv) + defaultlit(&n.Left, nil) + indexlit(&n.Right.Left) + indexlit(&n.Right.Right.Left) + indexlit(&n.Right.Right.Right) + l := n.Left + if Isfixedarray(l.Type) { + if !islvalue(n.Left) { + Yyerror("invalid operation %v (slice of unaddressable value)", n) + n.Type = nil + return + } + + n.Left = Nod(OADDR, n.Left, nil) + n.Left.Implicit = true + typecheck(&n.Left, Erv) + l = n.Left + } + + t := l.Type + if t == nil { + n.Type = nil + return + } + if Istype(t, TSTRING) { + Yyerror("invalid operation %v (3-index slice of string)", n) + n.Type = nil + return + } + + var tp *Type + if Isptr[t.Etype] && Isfixedarray(t.Type) { + tp = t.Type + n.Type = typ(TARRAY) + n.Type.Type = tp.Type + n.Type.Bound = -1 + dowidth(n.Type) + n.Op = OSLICE3ARR + } else if Isslice(t) { + n.Type = t + } else { + Yyerror("cannot slice %v (type %v)", l, t) + n.Type = nil + return + } + + lo := n.Right.Left + if lo != nil && checksliceindex(l, lo, tp) < 0 { + n.Type = nil + return + } + mid := n.Right.Right.Left + if mid != nil && checksliceindex(l, mid, tp) < 0 { + n.Type = nil + return + } + hi := n.Right.Right.Right + if hi != nil && checksliceindex(l, hi, tp) < 0 { + n.Type = nil + return + } + if checksliceconst(lo, hi) < 0 || checksliceconst(lo, mid) < 0 || checksliceconst(mid, hi) < 0 { + n.Type = nil + return + } + break OpSwitch + + /* + * call and call like + */ + case OCALL: + l := n.Left + + if l.Op == ONAME { + r := unsafenmagic(n) + if r != nil { + if n.Isddd { + Yyerror("invalid use of ... with builtin %v", l) + } + n = r + typecheck1(&n, top) + return + } + } + + typecheck(&n.Left, Erv|Etype|Ecall|top&Eproc) + n.Diag |= n.Left.Diag + l = n.Left + if l.Op == ONAME && l.Etype != 0 { + if n.Isddd && l.Etype != OAPPEND { + Yyerror("invalid use of ... with builtin %v", l) + } + + // builtin: OLEN, OCAP, etc. + n.Op = l.Etype + + n.Left = n.Right + n.Right = nil + typecheck1(&n, top) + return + } + + defaultlit(&n.Left, nil) + l = n.Left + if l.Op == OTYPE { + if n.Isddd || l.Type.Bound == -100 { + if l.Type.Broke == 0 { + Yyerror("invalid use of ... in type conversion to %v", l.Type) + } + n.Diag = 1 + } + + // pick off before type-checking arguments + ok |= Erv + + // turn CALL(type, arg) into CONV(arg) w/ type + n.Left = nil + + n.Op = OCONV + n.Type = l.Type + if onearg(n, "conversion to %v", l.Type) < 0 { + n.Type = nil + return + } + typecheck1(&n, top) + return + } + + if count(n.List) == 1 && !n.Isddd { + typecheck(&n.List.N, Erv|Efnstruct) + } else { + typechecklist(n.List, Erv) + } + t := l.Type + if t == nil { + n.Type = nil + return + } + checkwidth(t) + + switch l.Op { + case ODOTINTER: + n.Op = OCALLINTER + + case ODOTMETH: + n.Op = OCALLMETH + + // typecheckaste was used here but there wasn't enough + // information further down the call chain to know if we + // were testing a method receiver for unexported fields. + // It isn't necessary, so just do a sanity check. + tp := getthisx(t).Type.Type + + if l.Left == nil || !Eqtype(l.Left.Type, tp) { + Fatal("method receiver") + } + + default: + n.Op = OCALLFUNC + if t.Etype != TFUNC { + Yyerror("cannot call non-function %v (type %v)", l, t) + n.Type = nil + return + } + } + + typecheckaste(OCALL, n.Left, n.Isddd, getinargx(t), n.List, func() string { return fmt.Sprintf("argument to %v", n.Left) }) + ok |= Etop + if t.Outtuple == 0 { + break OpSwitch + } + ok |= Erv + if t.Outtuple == 1 { + t := getoutargx(l.Type).Type + if t == nil { + n.Type = nil + return + } + if t.Etype == TFIELD { + t = t.Type + } + n.Type = t + + if n.Op == OCALLFUNC && n.Left.Op == ONAME && (compiling_runtime != 0 || n.Left.Sym.Pkg == Runtimepkg) && n.Left.Sym.Name == "getg" { + // Emit code for runtime.getg() directly instead of calling function. + // Most such rewrites (for example the similar one for math.Sqrt) should be done in walk, + // so that the ordering pass can make sure to preserve the semantics of the original code + // (in particular, the exact time of the function call) by introducing temporaries. + // In this case, we know getg() always returns the same result within a given function + // and we want to avoid the temporaries, so we do the rewrite earlier than is typical. + n.Op = OGETG + } + + break OpSwitch + } + + // multiple return + if top&(Efnstruct|Etop) == 0 { + Yyerror("multiple-value %v() in single-value context", l) + break OpSwitch + } + + n.Type = getoutargx(l.Type) + + break OpSwitch + + case OCAP, OLEN, OREAL, OIMAG: + ok |= Erv + if onearg(n, "%v", Oconv(int(n.Op), 0)) < 0 { + n.Type = nil + return + } + typecheck(&n.Left, Erv) + defaultlit(&n.Left, nil) + implicitstar(&n.Left) + l := n.Left + t := l.Type + if t == nil { + n.Type = nil + return + } + switch n.Op { + case OCAP: + if !okforcap[t.Etype] { + goto badcall1 + } + + case OLEN: + if !okforlen[t.Etype] { + goto badcall1 + } + + case OREAL, OIMAG: + if !Iscomplex[t.Etype] { + goto badcall1 + } + if Isconst(l, CTCPLX) { + r := n + if n.Op == OREAL { + n = nodfltconst(&l.Val.U.(*Mpcplx).Real) + } else { + n = nodfltconst(&l.Val.U.(*Mpcplx).Imag) + } + n.Orig = r + } + + n.Type = Types[cplxsubtype(int(t.Etype))] + break OpSwitch + } + + // might be constant + switch t.Etype { + case TSTRING: + if Isconst(l, CTSTR) { + r := Nod(OXXX, nil, nil) + Nodconst(r, Types[TINT], int64(len(l.Val.U.(string)))) + r.Orig = n + n = r + } + + case TARRAY: + if t.Bound < 0 { // slice + break + } + if callrecv(l) { // has call or receive + break + } + r := Nod(OXXX, nil, nil) + Nodconst(r, Types[TINT], t.Bound) + r.Orig = n + n = r + } + + n.Type = Types[TINT] + break OpSwitch + + badcall1: + Yyerror("invalid argument %v for %v", Nconv(n.Left, obj.FmtLong), Oconv(int(n.Op), 0)) + n.Type = nil + return + + case OCOMPLEX: + ok |= Erv + var r *Node + var l *Node + if count(n.List) == 1 { + typechecklist(n.List, Efnstruct) + if n.List.N.Op != OCALLFUNC && n.List.N.Op != OCALLMETH { + Yyerror("invalid operation: complex expects two arguments") + n.Type = nil + return + } + + t := n.List.N.Left.Type + if t.Outtuple != 2 { + Yyerror("invalid operation: complex expects two arguments, %v returns %d results", n.List.N, t.Outtuple) + n.Type = nil + return + } + + t = n.List.N.Type.Type + l = t.Nname + r = t.Down.Nname + } else { + if twoarg(n) < 0 { + n.Type = nil + return + } + l = typecheck(&n.Left, Erv|top&Eiota) + r = typecheck(&n.Right, Erv|top&Eiota) + if l.Type == nil || r.Type == nil { + n.Type = nil + return + } + defaultlit2(&l, &r, 0) + if l.Type == nil || r.Type == nil { + n.Type = nil + return + } + n.Left = l + n.Right = r + } + + if !Eqtype(l.Type, r.Type) { + Yyerror("invalid operation: %v (mismatched types %v and %v)", n, l.Type, r.Type) + n.Type = nil + return + } + + var t *Type + switch l.Type.Etype { + default: + Yyerror("invalid operation: %v (arguments have type %v, expected floating-point)", n, l.Type) + n.Type = nil + return + + case TIDEAL: + t = Types[TIDEAL] + + case TFLOAT32: + t = Types[TCOMPLEX64] + + case TFLOAT64: + t = Types[TCOMPLEX128] + } + + if l.Op == OLITERAL && r.Op == OLITERAL { + // make it a complex literal + r = nodcplxlit(l.Val, r.Val) + + r.Orig = n + n = r + } + + n.Type = t + break OpSwitch + + case OCLOSE: + if onearg(n, "%v", Oconv(int(n.Op), 0)) < 0 { + n.Type = nil + return + } + typecheck(&n.Left, Erv) + defaultlit(&n.Left, nil) + l := n.Left + t := l.Type + if t == nil { + n.Type = nil + return + } + if t.Etype != TCHAN { + Yyerror("invalid operation: %v (non-chan type %v)", n, t) + n.Type = nil + return + } + + if t.Chan&Csend == 0 { + Yyerror("invalid operation: %v (cannot close receive-only channel)", n) + n.Type = nil + return + } + + ok |= Etop + break OpSwitch + + case ODELETE: + args := n.List + if args == nil { + Yyerror("missing arguments to delete") + n.Type = nil + return + } + + if args.Next == nil { + Yyerror("missing second (key) argument to delete") + n.Type = nil + return + } + + if args.Next.Next != nil { + Yyerror("too many arguments to delete") + n.Type = nil + return + } + + ok |= Etop + typechecklist(args, Erv) + l := args.N + r := args.Next.N + if l.Type != nil && l.Type.Etype != TMAP { + Yyerror("first argument to delete must be map; have %v", Tconv(l.Type, obj.FmtLong)) + n.Type = nil + return + } + + args.Next.N = assignconv(r, l.Type.Down, "delete") + break OpSwitch + + case OAPPEND: + ok |= Erv + args := n.List + if args == nil { + Yyerror("missing arguments to append") + n.Type = nil + return + } + + if count(args) == 1 && !n.Isddd { + typecheck(&args.N, Erv|Efnstruct) + } else { + typechecklist(args, Erv) + } + + t := args.N.Type + if t == nil { + n.Type = nil + return + } + + // Unpack multiple-return result before type-checking. + var funarg *Type + if Istype(t, TSTRUCT) && t.Funarg != 0 { + funarg = t + t = t.Type.Type + } + + n.Type = t + if !Isslice(t) { + if Isconst(args.N, CTNIL) { + Yyerror("first argument to append must be typed slice; have untyped nil") + n.Type = nil + return + } + + Yyerror("first argument to append must be slice; have %v", Tconv(t, obj.FmtLong)) + n.Type = nil + return + } + + if n.Isddd { + if args.Next == nil { + Yyerror("cannot use ... on first argument to append") + n.Type = nil + return + } + + if args.Next.Next != nil { + Yyerror("too many arguments to append") + n.Type = nil + return + } + + if Istype(t.Type, TUINT8) && Istype(args.Next.N.Type, TSTRING) { + defaultlit(&args.Next.N, Types[TSTRING]) + break OpSwitch + } + + args.Next.N = assignconv(args.Next.N, t.Orig, "append") + break OpSwitch + } + + 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") + } + } + + break OpSwitch + + case OCOPY: + ok |= Etop | Erv + args := n.List + if args == nil || args.Next == nil { + Yyerror("missing arguments to copy") + n.Type = nil + return + } + + if args.Next.Next != nil { + Yyerror("too many arguments to copy") + n.Type = nil + return + } + + n.Left = args.N + n.Right = args.Next.N + n.List = nil + n.Type = Types[TINT] + typecheck(&n.Left, Erv) + typecheck(&n.Right, Erv) + if n.Left.Type == nil || n.Right.Type == nil { + n.Type = nil + return + } + defaultlit(&n.Left, nil) + defaultlit(&n.Right, nil) + if n.Left.Type == nil || n.Right.Type == nil { + n.Type = nil + return + } + + // copy([]byte, string) + if Isslice(n.Left.Type) && n.Right.Type.Etype == TSTRING { + if Eqtype(n.Left.Type.Type, bytetype) { + break OpSwitch + } + Yyerror("arguments to copy have different element types: %v and string", Tconv(n.Left.Type, obj.FmtLong)) + n.Type = nil + return + } + + if !Isslice(n.Left.Type) || !Isslice(n.Right.Type) { + if !Isslice(n.Left.Type) && !Isslice(n.Right.Type) { + Yyerror("arguments to copy must be slices; have %v, %v", Tconv(n.Left.Type, obj.FmtLong), Tconv(n.Right.Type, obj.FmtLong)) + } else if !Isslice(n.Left.Type) { + Yyerror("first argument to copy should be slice; have %v", Tconv(n.Left.Type, obj.FmtLong)) + } else { + Yyerror("second argument to copy should be slice or string; have %v", Tconv(n.Right.Type, obj.FmtLong)) + } + n.Type = nil + return + } + + if !Eqtype(n.Left.Type.Type, n.Right.Type.Type) { + Yyerror("arguments to copy have different element types: %v and %v", Tconv(n.Left.Type, obj.FmtLong), Tconv(n.Right.Type, obj.FmtLong)) + n.Type = nil + return + } + + break OpSwitch + + case OCONV: + ok |= Erv + saveorignode(n) + typecheck(&n.Left, Erv|top&(Eindir|Eiota)) + convlit1(&n.Left, n.Type, true) + t := n.Left.Type + if t == nil || n.Type == nil { + n.Type = nil + return + } + var why string + n.Op = uint8(convertop(t, n.Type, &why)) + if (n.Op) == 0 { + if n.Diag == 0 && n.Type.Broke == 0 { + Yyerror("cannot convert %v to type %v%s", Nconv(n.Left, obj.FmtLong), n.Type, why) + n.Diag = 1 + } + + n.Op = OCONV + } + + switch n.Op { + case OCONVNOP: + if n.Left.Op == OLITERAL && n.Type != Types[TBOOL] { + r := Nod(OXXX, nil, nil) + n.Op = OCONV + n.Orig = r + *r = *n + n.Op = OLITERAL + n.Val = n.Left.Val + } + + // do not use stringtoarraylit. + // generated code and compiler memory footprint is better without it. + case OSTRARRAYBYTE: + break + + case OSTRARRAYRUNE: + if n.Left.Op == OLITERAL { + stringtoarraylit(&n) + } + } + + break OpSwitch + + case OMAKE: + ok |= Erv + args := n.List + if args == nil { + Yyerror("missing argument to make") + n.Type = nil + return + } + + n.List = nil + l := args.N + args = args.Next + typecheck(&l, Etype) + t := l.Type + if t == nil { + n.Type = nil + return + } + + switch t.Etype { + default: + Yyerror("cannot make type %v", t) + n.Type = nil + return + + case TARRAY: + if !Isslice(t) { + Yyerror("cannot make type %v", t) + n.Type = nil + return + } + + if args == nil { + Yyerror("missing len argument to make(%v)", t) + n.Type = nil + return + } + + l = args.N + args = args.Next + typecheck(&l, Erv) + var r *Node + if args != nil { + r = args.N + args = args.Next + typecheck(&r, Erv) + } + + if l.Type == nil || (r != nil && r.Type == nil) { + n.Type = nil + return + } + et := obj.Bool2int(checkmake(t, "len", l) < 0) + et |= obj.Bool2int(r != nil && checkmake(t, "cap", r) < 0) + if et != 0 { + n.Type = nil + return + } + 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 + } + + n.Left = l + n.Right = r + n.Op = OMAKESLICE + + case TMAP: + if args != nil { + l = args.N + args = args.Next + typecheck(&l, Erv) + defaultlit(&l, Types[TINT]) + if l.Type == nil { + n.Type = nil + return + } + if checkmake(t, "size", l) < 0 { + n.Type = nil + return + } + n.Left = l + } else { + n.Left = Nodintconst(0) + } + n.Op = OMAKEMAP + + case TCHAN: + l = nil + if args != nil { + l = args.N + args = args.Next + typecheck(&l, Erv) + defaultlit(&l, Types[TINT]) + if l.Type == nil { + n.Type = nil + return + } + if checkmake(t, "buffer", l) < 0 { + n.Type = nil + return + } + n.Left = l + } else { + n.Left = Nodintconst(0) + } + n.Op = OMAKECHAN + } + + if args != nil { + Yyerror("too many arguments to make(%v)", t) + n.Op = OMAKE + n.Type = nil + return + } + + n.Type = t + break OpSwitch + + case ONEW: + ok |= Erv + args := n.List + if args == nil { + Yyerror("missing argument to new") + n.Type = nil + return + } + + l := args.N + typecheck(&l, Etype) + t := l.Type + if t == nil { + n.Type = nil + return + } + if args.Next != nil { + Yyerror("too many arguments to new(%v)", t) + n.Type = nil + return + } + + n.Left = l + n.Type = Ptrto(t) + break OpSwitch + + case OPRINT, OPRINTN: + ok |= Etop + typechecklist(n.List, Erv|Eindir) // Eindir: address does not escape + for args := n.List; args != nil; args = args.Next { + // Special case for print: int constant is int64, not int. + if Isconst(args.N, CTINT) { + defaultlit(&args.N, Types[TINT64]) + } else { + defaultlit(&args.N, nil) + } + } + + break OpSwitch + + case OPANIC: + ok |= Etop + if onearg(n, "panic") < 0 { + n.Type = nil + return + } + typecheck(&n.Left, Erv) + defaultlit(&n.Left, Types[TINTER]) + if n.Left.Type == nil { + n.Type = nil + return + } + break OpSwitch + + case ORECOVER: + ok |= Erv | Etop + if n.List != nil { + Yyerror("too many arguments to recover") + n.Type = nil + return + } + + n.Type = Types[TINTER] + break OpSwitch + + case OCLOSURE: + ok |= Erv + typecheckclosure(n, top) + if n.Type == nil { + n.Type = nil + return + } + break OpSwitch + + case OITAB: + ok |= Erv + typecheck(&n.Left, Erv) + t := n.Left.Type + if t == nil { + n.Type = nil + return + } + if t.Etype != TINTER { + Fatal("OITAB of %v", t) + } + n.Type = Ptrto(Types[TUINTPTR]) + break OpSwitch + + case OSPTR: + ok |= Erv + typecheck(&n.Left, Erv) + t := n.Left.Type + if t == nil { + n.Type = nil + return + } + if !Isslice(t) && t.Etype != TSTRING { + Fatal("OSPTR of %v", t) + } + if t.Etype == TSTRING { + n.Type = Ptrto(Types[TUINT8]) + } else { + n.Type = Ptrto(t.Type) + } + break OpSwitch + + case OCLOSUREVAR: + ok |= Erv + break OpSwitch + + case OCFUNC: + ok |= Erv + typecheck(&n.Left, Erv) + n.Type = Types[TUINTPTR] + break OpSwitch + + case OCONVNOP: + ok |= Erv + typecheck(&n.Left, Erv) + break OpSwitch + + /* + * statements + */ + case OAS: + ok |= Etop + + typecheckas(n) + + // Code that creates temps does not bother to set defn, so do it here. + if n.Left.Op == ONAME && strings.HasPrefix(n.Left.Sym.Name, "autotmp_") { + n.Left.Defn = n + } + break OpSwitch + + case OAS2: + ok |= Etop + typecheckas2(n) + break OpSwitch + + case OBREAK, + OCONTINUE, + ODCL, + OEMPTY, + OGOTO, + OXFALL, + OVARKILL: + ok |= Etop + break OpSwitch + + case OLABEL: + ok |= Etop + decldepth++ + break OpSwitch + + case ODEFER: + ok |= Etop + typecheck(&n.Left, Etop|Erv) + if n.Left.Diag == 0 { + checkdefergo(n) + } + break OpSwitch + + case OPROC: + ok |= Etop + typecheck(&n.Left, Etop|Eproc|Erv) + checkdefergo(n) + break OpSwitch + + case OFOR: + ok |= Etop + typechecklist(n.Ninit, Etop) + decldepth++ + typecheck(&n.Ntest, Erv) + if n.Ntest != nil { + t := n.Ntest.Type + if t != nil && t.Etype != TBOOL { + Yyerror("non-bool %v used as for condition", Nconv(n.Ntest, obj.FmtLong)) + } + } + typecheck(&n.Nincr, Etop) + typechecklist(n.Nbody, Etop) + decldepth-- + break OpSwitch + + case OIF: + ok |= Etop + typechecklist(n.Ninit, Etop) + typecheck(&n.Ntest, Erv) + if n.Ntest != nil { + t := n.Ntest.Type + if t != nil && t.Etype != TBOOL { + Yyerror("non-bool %v used as if condition", Nconv(n.Ntest, obj.FmtLong)) + } + } + typechecklist(n.Nbody, Etop) + typechecklist(n.Nelse, Etop) + break OpSwitch + + case ORETURN: + ok |= Etop + if count(n.List) == 1 { + typechecklist(n.List, Erv|Efnstruct) + } else { + typechecklist(n.List, Erv) + } + if Curfn == nil { + Yyerror("return outside function") + n.Type = nil + return + } + + if Curfn.Type.Outnamed != 0 && n.List == nil { + break OpSwitch + } + typecheckaste(ORETURN, nil, false, getoutargx(Curfn.Type), n.List, func() string { return "return argument" }) + break OpSwitch + + case ORETJMP: + ok |= Etop + break OpSwitch + + case OSELECT: + ok |= Etop + typecheckselect(n) + break OpSwitch + + case OSWITCH: + ok |= Etop + typecheckswitch(n) + break OpSwitch + + case ORANGE: + ok |= Etop + typecheckrange(n) + break OpSwitch + + case OTYPESW: + Yyerror("use of .(type) outside type switch") + n.Type = nil + return + + case OXCASE: + ok |= Etop + typechecklist(n.List, Erv) + typechecklist(n.Nbody, Etop) + break OpSwitch + + case ODCLFUNC: + ok |= Etop + typecheckfunc(n) + break OpSwitch + + case ODCLCONST: + ok |= Etop + typecheck(&n.Left, Erv) + break OpSwitch + + case ODCLTYPE: + ok |= Etop + typecheck(&n.Left, Etype) + if incannedimport == 0 { + checkwidth(n.Left.Type) + } + break OpSwitch + } + + t := n.Type + if t != nil && t.Funarg == 0 && n.Op != OTYPE { + switch t.Etype { + case TFUNC, // might have TANY; wait until its called + TANY, + TFORW, + TIDEAL, + TNIL, + TBLANK: + break + + default: + checkwidth(t) + } + } + + if safemode != 0 && incannedimport == 0 && importpkg == nil && compiling_wrappers == 0 && t != nil && t.Etype == TUNSAFEPTR { + Yyerror("cannot use unsafe.Pointer") + } + + evconst(n) + if n.Op == OTYPE && top&Etype == 0 { + Yyerror("type %v is not an expression", n.Type) + n.Type = nil + return + } + + if top&(Erv|Etype) == Etype && n.Op != OTYPE { + Yyerror("%v is not a type", n) + n.Type = nil + return + } + + // TODO(rsc): simplify + if (top&(Ecall|Erv|Etype) != 0) && top&Etop == 0 && ok&(Erv|Etype|Ecall) == 0 { + Yyerror("%v used as value", n) + n.Type = nil + return + } + + if (top&Etop != 0) && top&(Ecall|Erv|Etype) == 0 && ok&Etop == 0 { + if n.Diag == 0 { + Yyerror("%v evaluated but not used", n) + n.Diag = 1 + } + + n.Type = nil + return + } + + /* TODO + if(n->type == T) + fatal("typecheck nil type"); + */ +} + +func checksliceindex(l *Node, r *Node, tp *Type) int { + t := r.Type + if t == nil { + return -1 + } + if !Isint[t.Etype] { + Yyerror("invalid slice index %v (type %v)", r, t) + return -1 + } + + if r.Op == OLITERAL { + 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.(*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.(*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.(*Mpint), Maxintval[TINT]) > 0 { + Yyerror("invalid slice index %v (index too large)", r) + return -1 + } + } + + return 0 +} + +func checksliceconst(lo *Node, hi *Node) int { + 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 + } + + return 0 +} + +func checkdefergo(n *Node) { + what := "defer" + if n.Op == OPROC { + what = "go" + } + + switch n.Left.Op { + // ok + case OCALLINTER, + OCALLMETH, + OCALLFUNC, + OCLOSE, + OCOPY, + ODELETE, + OPANIC, + OPRINT, + OPRINTN, + ORECOVER: + return + + case OAPPEND, + OCAP, + OCOMPLEX, + OIMAG, + OLEN, + OMAKE, + OMAKESLICE, + OMAKECHAN, + OMAKEMAP, + ONEW, + OREAL, + OLITERAL: // conversion or unsafe.Alignof, Offsetof, Sizeof + if n.Left.Orig != nil && n.Left.Orig.Op == OCONV { + break + } + Yyerror("%s discards result of %v", what, n.Left) + return + } + + // type is broken or missing, most likely a method call on a broken type + // we will warn about the broken type elsewhere. no need to emit a potentially confusing error + if n.Left.Type == nil || n.Left.Type.Broke != 0 { + return + } + + if n.Diag == 0 { + // The syntax made sure it was a call, so this must be + // a conversion. + n.Diag = 1 + + Yyerror("%s requires function call, not conversion", what) + } +} + +func implicitstar(nn **Node) { + // insert implicit * if needed for fixed array + n := *nn + + t := n.Type + if t == nil || !Isptr[t.Etype] { + return + } + t = t.Type + if t == nil { + return + } + if !Isfixedarray(t) { + return + } + n = Nod(OIND, n, nil) + n.Implicit = true + typecheck(&n, Erv) + *nn = n +} + +func onearg(n *Node, f string, args ...interface{}) int { + if n.Left != nil { + return 0 + } + if n.List == nil { + p := fmt.Sprintf(f, args...) + Yyerror("missing argument to %s: %v", p, n) + return -1 + } + + if n.List.Next != nil { + p := fmt.Sprintf(f, args...) + Yyerror("too many arguments to %s: %v", p, n) + n.Left = n.List.N + n.List = nil + return -1 + } + + n.Left = n.List.N + n.List = nil + return 0 +} + +func twoarg(n *Node) int { + if n.Left != nil { + return 0 + } + if n.List == nil { + Yyerror("missing argument to %v - %v", Oconv(int(n.Op), 0), n) + return -1 + } + + n.Left = n.List.N + if n.List.Next == nil { + Yyerror("missing argument to %v - %v", Oconv(int(n.Op), 0), n) + n.List = nil + return -1 + } + + if n.List.Next.Next != nil { + Yyerror("too many arguments to %v - %v", Oconv(int(n.Op), 0), n) + n.List = nil + return -1 + } + + n.Right = n.List.Next.N + n.List = nil + return 0 +} + +func lookdot1(errnode *Node, s *Sym, t *Type, f *Type, dostrcmp int) *Type { + var r *Type + for ; f != nil; f = f.Down { + if dostrcmp != 0 && f.Sym.Name == s.Name { + return f + } + if dostrcmp == 2 && strings.EqualFold(f.Sym.Name, s.Name) { + return f + } + if f.Sym != s { + continue + } + if r != nil { + if errnode != nil { + Yyerror("ambiguous selector %v", errnode) + } else if Isptr[t.Etype] { + Yyerror("ambiguous selector (%v).%v", t, s) + } else { + Yyerror("ambiguous selector %v.%v", t, s) + } + break + } + + r = f + } + + return r +} + +func looktypedot(n *Node, t *Type, dostrcmp int) bool { + s := n.Right.Sym + + if t.Etype == TINTER { + f1 := lookdot1(n, s, t, t.Type, dostrcmp) + if f1 == nil { + return false + } + + n.Right = methodname(n.Right, t) + n.Xoffset = f1.Width + n.Type = f1.Type + n.Op = ODOTINTER + return true + } + + // Find the base type: methtype will fail if t + // is not of the form T or *T. + f2 := methtype(t, 0) + + if f2 == nil { + return false + } + + expandmeth(f2) + f2 = lookdot1(n, s, f2, f2.Xmethod, dostrcmp) + if f2 == nil { + return false + } + + // disallow T.m if m requires *T receiver + if Isptr[getthisx(f2.Type).Type.Type.Etype] && !Isptr[t.Etype] && f2.Embedded != 2 && !isifacemethod(f2.Type) { + Yyerror("invalid method expression %v (needs pointer receiver: (*%v).%v)", n, t, Sconv(f2.Sym, obj.FmtShort)) + return false + } + + n.Right = methodname(n.Right, t) + n.Xoffset = f2.Width + n.Type = f2.Type + n.Op = ODOTMETH + return true +} + +func derefall(t *Type) *Type { + for t != nil && int(t.Etype) == Tptr { + t = t.Type + } + return t +} + +func lookdot(n *Node, t *Type, dostrcmp int) *Type { + s := n.Right.Sym + + dowidth(t) + var f1 *Type + if t.Etype == TSTRUCT || t.Etype == TINTER { + f1 = lookdot1(n, s, t, t.Type, dostrcmp) + } + + var f2 *Type + if n.Left.Type == t || n.Left.Type.Sym == nil { + f2 = methtype(t, 0) + if f2 != nil { + // Use f2->method, not f2->xmethod: adddot has + // already inserted all the necessary embedded dots. + f2 = lookdot1(n, s, f2, f2.Method, dostrcmp) + } + } + + if f1 != nil { + if dostrcmp > 1 { + // Already in the process of diagnosing an error. + return f1 + } + if f2 != nil { + Yyerror("%v is both field and method", n.Right.Sym) + } + if f1.Width == BADWIDTH { + Fatal("lookdot badwidth %v %p", f1, f1) + } + n.Xoffset = f1.Width + n.Type = f1.Type + n.Paramfld = f1 + if t.Etype == TINTER { + if Isptr[n.Left.Type.Etype] { + n.Left = Nod(OIND, n.Left, nil) // implicitstar + n.Left.Implicit = true + typecheck(&n.Left, Erv) + } + + n.Op = ODOTINTER + } + + return f1 + } + + if f2 != nil { + if dostrcmp > 1 { + // Already in the process of diagnosing an error. + return f2 + } + tt := n.Left.Type + dowidth(tt) + rcvr := getthisx(f2.Type).Type.Type + if !Eqtype(rcvr, tt) { + if int(rcvr.Etype) == Tptr && Eqtype(rcvr.Type, tt) { + checklvalue(n.Left, "call pointer method on") + n.Left = Nod(OADDR, n.Left, nil) + n.Left.Implicit = true + typecheck(&n.Left, Etype|Erv) + } else if int(tt.Etype) == Tptr && int(rcvr.Etype) != Tptr && Eqtype(tt.Type, rcvr) { + n.Left = Nod(OIND, n.Left, nil) + n.Left.Implicit = true + typecheck(&n.Left, Etype|Erv) + } else if int(tt.Etype) == Tptr && int(tt.Type.Etype) == Tptr && Eqtype(derefall(tt), derefall(rcvr)) { + Yyerror("calling method %v with receiver %v requires explicit dereference", n.Right, Nconv(n.Left, obj.FmtLong)) + for int(tt.Etype) == Tptr { + // Stop one level early for method with pointer receiver. + if int(rcvr.Etype) == Tptr && int(tt.Type.Etype) != Tptr { + break + } + n.Left = Nod(OIND, n.Left, nil) + n.Left.Implicit = true + typecheck(&n.Left, Etype|Erv) + tt = tt.Type + } + } else { + Fatal("method mismatch: %v for %v", rcvr, tt) + } + } + + ll := n.Left + for ll.Left != nil { + ll = ll.Left + } + if ll.Implicit { + if Isptr[ll.Type.Etype] && ll.Type.Sym != nil && ll.Type.Sym.Def != nil && ll.Type.Sym.Def.Op == OTYPE { + // It is invalid to automatically dereference a named pointer type when selecting a method. + // Make n->left == ll to clarify error message. + n.Left = ll + return nil + } + } + + n.Right = methodname(n.Right, n.Left.Type) + n.Xoffset = f2.Width + n.Type = f2.Type + + // print("lookdot found [%p] %T\n", f2->type, f2->type); + n.Op = ODOTMETH + + return f2 + } + + return nil +} + +func nokeys(l *NodeList) bool { + for ; l != nil; l = l.Next { + if l.N.Op == OKEY { + return false + } + } + return true +} + +func hasddd(t *Type) bool { + for tl := t.Type; tl != nil; tl = tl.Down { + if tl.Isddd { + return true + } + } + + return false +} + +func downcount(t *Type) int { + n := 0 + for tl := t.Type; tl != nil; tl = tl.Down { + n++ + } + + return n +} + +/* + * typecheck assignment: type list = expression list + */ +func typecheckaste(op int, call *Node, isddd bool, tstruct *Type, nl *NodeList, desc func() string) { + var t *Type + var n *Node + var n1 int + var n2 int + + lno := int(lineno) + + if tstruct.Broke != 0 { + goto out + } + + n = nil + if nl != nil && nl.Next == nil { + n = nl.N + if n.Type != nil { + if n.Type.Etype == TSTRUCT && n.Type.Funarg != 0 { + if !hasddd(tstruct) { + n1 := downcount(tstruct) + n2 := downcount(n.Type) + if n2 > n1 { + goto toomany + } + if n2 < n1 { + goto notenough + } + } + + tn := n.Type.Type + var why string + for tl := tstruct.Type; tl != nil; tl = tl.Down { + if tl.Isddd { + for ; tn != nil; tn = tn.Down { + if assignop(tn.Type, tl.Type.Type, &why) == 0 { + if call != nil { + Yyerror("cannot use %v as type %v in argument to %v%s", tn.Type, tl.Type.Type, call, why) + } else { + Yyerror("cannot use %v as type %v in %s%s", tn.Type, tl.Type.Type, desc(), why) + } + } + } + + goto out + } + + if tn == nil { + goto notenough + } + if assignop(tn.Type, tl.Type, &why) == 0 { + if call != nil { + Yyerror("cannot use %v as type %v in argument to %v%s", tn.Type, tl.Type, call, why) + } else { + Yyerror("cannot use %v as type %v in %s%s", tn.Type, tl.Type, desc(), why) + } + } + + tn = tn.Down + } + + if tn != nil { + goto toomany + } + goto out + } + } + } + + n1 = downcount(tstruct) + n2 = count(nl) + if !hasddd(tstruct) { + if n2 > n1 { + goto toomany + } + if n2 < n1 { + goto notenough + } + } else { + if !isddd { + if n2 < n1-1 { + goto notenough + } + } else { + if n2 > n1 { + goto toomany + } + if n2 < n1 { + goto notenough + } + } + } + + for tl := tstruct.Type; tl != nil; tl = tl.Down { + t = tl.Type + if tl.Isddd { + if isddd { + if nl == nil { + goto notenough + } + if nl.Next != nil { + goto toomany + } + n = nl.N + setlineno(n) + if n.Type != nil { + nl.N = assignconvfn(n, t, desc) + } + goto out + } + + for ; nl != nil; nl = nl.Next { + n = nl.N + setlineno(nl.N) + if n.Type != nil { + nl.N = assignconvfn(n, t.Type, desc) + } + } + + goto out + } + + if nl == nil { + goto notenough + } + n = nl.N + setlineno(n) + if n.Type != nil { + nl.N = assignconvfn(n, t, desc) + } + nl = nl.Next + } + + if nl != nil { + goto toomany + } + if isddd { + if call != nil { + Yyerror("invalid use of ... in call to %v", call) + } else { + Yyerror("invalid use of ... in %v", Oconv(int(op), 0)) + } + } + +out: + lineno = int32(lno) + return + +notenough: + if n == nil || n.Diag == 0 { + if call != nil { + Yyerror("not enough arguments in call to %v", call) + } else { + Yyerror("not enough arguments to %v", Oconv(int(op), 0)) + } + if n != nil { + n.Diag = 1 + } + } + + goto out + +toomany: + if call != nil { + Yyerror("too many arguments in call to %v", call) + } else { + Yyerror("too many arguments to %v", Oconv(int(op), 0)) + } + goto out +} + +/* + * type check composite + */ +func fielddup(n *Node, hash map[string]bool) { + if n.Op != ONAME { + Fatal("fielddup: not ONAME") + } + name := n.Sym.Name + if hash[name] { + Yyerror("duplicate field name in struct literal: %s", name) + return + } + hash[name] = true +} + +func keydup(n *Node, hash []*Node) { + orign := n + if n.Op == OCONVIFACE { + n = n.Left + } + evconst(n) + if n.Op != OLITERAL { + return // we dont check variables + } + + var b uint32 + switch n.Val.Ctype { + default: // unknown, bool, nil + b = 23 + + case CTINT, CTRUNE: + b = uint32(Mpgetfix(n.Val.U.(*Mpint))) + + case CTFLT: + d := mpgetflt(n.Val.U.(*Mpflt)) + x := math.Float64bits(d) + for i := 0; i < 8; i++ { + b = b*PRIME1 + uint32(x&0xFF) + x >>= 8 + } + + case CTSTR: + b = 0 + s := n.Val.U.(string) + for i := len(n.Val.U.(string)); i > 0; i-- { + b = b*PRIME1 + uint32(s[0]) + s = s[1:] + } + } + + h := uint(b % uint32(len(hash))) + var cmp Node + for a := hash[h]; a != nil; a = a.Ntest { + cmp.Op = OEQ + cmp.Left = n + b = 0 + if a.Op == OCONVIFACE && orign.Op == OCONVIFACE { + if Eqtype(a.Left.Type, n.Type) { + cmp.Right = a.Left + evconst(&cmp) + 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.(bool))) + } + + if b != 0 { + Yyerror("duplicate key %v in map literal", n) + return + } + } + + orign.Ntest = hash[h] + hash[h] = orign +} + +func indexdup(n *Node, hash []*Node) { + if n.Op != OLITERAL { + Fatal("indexdup: not OLITERAL") + } + + 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.(*Mpint))) + if b == c { + Yyerror("duplicate index in array literal: %d", b) + return + } + } + + n.Ntest = hash[h] + hash[h] = n +} + +func prime(h uint32, sr uint32) bool { + for n := uint32(3); n <= sr; n += 2 { + if h%n == 0 { + return false + } + } + return true +} + +func inithash(n *Node, autohash []*Node) []*Node { + // count the number of entries + h := uint32(0) + + for ll := n.List; ll != nil; ll = ll.Next { + h++ + } + + // if the auto hash table is + // large enough use it. + if h <= uint32(len(autohash)) { + for i := range autohash { + autohash[i] = nil + } + return autohash + } + + // make hash size odd and 12% larger than entries + h += h / 8 + + h |= 1 + + // calculate sqrt of h + sr := h / 2 + + for i := 0; i < 5; i++ { + sr = (sr + h/sr) / 2 + } + + // check for primeality + for !prime(h, sr) { + h += 2 + } + + // build and return a throw-away hash table + return make([]*Node, h) +} + +func iscomptype(t *Type) bool { + switch t.Etype { + case TARRAY, TSTRUCT, TMAP: + return true + + case TPTR32, TPTR64: + switch t.Type.Etype { + case TARRAY, TSTRUCT, TMAP: + return true + } + } + + return false +} + +func pushtype(n *Node, t *Type) { + if n == nil || n.Op != OCOMPLIT || !iscomptype(t) { + return + } + + if n.Right == nil { + n.Right = typenod(t) + n.Implicit = true // don't print + n.Right.Implicit = true // * is okay + } else if Debug['s'] != 0 { + typecheck(&n.Right, Etype) + if n.Right.Type != nil && Eqtype(n.Right.Type, t) { + fmt.Printf("%v: redundant type: %v\n", n.Line(), t) + } + } +} + +func typecheckcomplit(np **Node) { + n := *np + lno := lineno + defer func() { + lineno = lno + *np = n + }() + + if n.Right == nil { + if n.List != nil { + setlineno(n.List.N) + } + Yyerror("missing type in composite literal") + n.Type = nil + return + } + + // Save original node (including n->right) + norig := Nod(int(n.Op), nil, nil) + + *norig = *n + + setlineno(n.Right) + l := typecheck(&n.Right, Etype|Ecomplit) /* sic */ + t := l.Type + if t == nil { + n.Type = nil + return + } + nerr := nerrors + n.Type = t + + if Isptr[t.Etype] { + // For better or worse, we don't allow pointers as the composite literal type, + // except when using the &T syntax, which sets implicit on the OIND. + if !n.Right.Implicit { + Yyerror("invalid pointer type %v for composite literal (use &%v instead)", t, t.Type) + n.Type = nil + return + } + + // Also, the underlying type must be a struct, map, slice, or array. + if !iscomptype(t) { + Yyerror("invalid pointer type %v for composite literal", t) + n.Type = nil + return + } + + t = t.Type + } + + var r *Node + switch t.Etype { + default: + Yyerror("invalid type for composite literal: %v", t) + n.Type = nil + + case TARRAY: + var autohash [101]*Node + hash := inithash(n, autohash[:]) + + length := int64(0) + i := 0 + var l *Node + for ll := n.List; ll != nil; ll = ll.Next { + l = ll.N + setlineno(l) + if l.Op != OKEY { + l = Nod(OKEY, Nodintconst(int64(i)), l) + l.Left.Type = Types[TINT] + l.Left.Typecheck = 1 + ll.N = l + } + + typecheck(&l.Left, Erv) + evconst(l.Left) + i = nonnegconst(l.Left) + if i < 0 && l.Left.Diag == 0 { + Yyerror("array index must be non-negative integer constant") + l.Left.Diag = 1 + i = -(1 << 30) // stay negative for a while + } + + if i >= 0 { + indexdup(l.Left, hash) + } + i++ + if int64(i) > length { + length = int64(i) + if t.Bound >= 0 && length > t.Bound { + setlineno(l) + Yyerror("array index %d out of bounds [0:%d]", length-1, t.Bound) + t.Bound = -1 // no more errors + } + } + + r = l.Right + pushtype(r, t.Type) + typecheck(&r, Erv) + defaultlit(&r, t.Type) + l.Right = assignconv(r, t.Type, "array element") + } + + if t.Bound == -100 { + t.Bound = length + } + if t.Bound < 0 { + n.Right = Nodintconst(length) + } + n.Op = OARRAYLIT + + case TMAP: + var autohash [101]*Node + hash := inithash(n, autohash[:]) + + var l *Node + for ll := n.List; ll != nil; ll = ll.Next { + l = ll.N + setlineno(l) + if l.Op != OKEY { + typecheck(&ll.N, Erv) + Yyerror("missing key in map literal") + continue + } + + typecheck(&l.Left, Erv) + defaultlit(&l.Left, t.Down) + l.Left = assignconv(l.Left, t.Down, "map key") + if l.Left.Op != OCONV { + keydup(l.Left, hash) + } + + r = l.Right + pushtype(r, t.Type) + typecheck(&r, Erv) + defaultlit(&r, t.Type) + l.Right = assignconv(r, t.Type, "map value") + } + + n.Op = OMAPLIT + + case TSTRUCT: + bad := 0 + if n.List != nil && nokeys(n.List) { + // simple list of variables + f := t.Type + + var s *Sym + for ll := n.List; ll != nil; ll = ll.Next { + setlineno(ll.N) + typecheck(&ll.N, Erv) + if f == nil { + tmp12 := bad + bad++ + if tmp12 == 0 { + Yyerror("too many values in struct initializer") + } + continue + } + + s = f.Sym + if s != nil && !exportname(s.Name) && s.Pkg != localpkg { + Yyerror("implicit assignment of unexported field '%s' in %v literal", s.Name, t) + } + + // No pushtype allowed here. Must name fields for that. + ll.N = assignconv(ll.N, f.Type, "field value") + + ll.N = Nod(OKEY, newname(f.Sym), ll.N) + ll.N.Left.Type = f + ll.N.Left.Typecheck = 1 + f = f.Down + } + + if f != nil { + Yyerror("too few values in struct initializer") + } + } else { + hash := make(map[string]bool) + + // keyed list + var s *Sym + var f *Type + var l *Node + var s1 *Sym + for ll := n.List; ll != nil; ll = ll.Next { + l = ll.N + setlineno(l) + if l.Op != OKEY { + tmp13 := bad + bad++ + if tmp13 == 0 { + Yyerror("mixture of field:value and value initializers") + } + typecheck(&ll.N, Erv) + continue + } + + s = l.Left.Sym + if s == nil { + Yyerror("invalid field name %v in struct initializer", l.Left) + typecheck(&l.Right, Erv) + continue + } + + // Sym might have resolved to name in other top-level + // package, because of import dot. Redirect to correct sym + // before we do the lookup. + if s.Pkg != localpkg && exportname(s.Name) { + s1 = Lookup(s.Name) + if s1.Origpkg == s.Pkg { + s = s1 + } + } + + f = lookdot1(nil, s, t, t.Type, 0) + if f == nil { + Yyerror("unknown %v field '%v' in struct literal", t, s) + continue + } + + l.Left = newname(s) + l.Left.Typecheck = 1 + l.Left.Type = f + s = f.Sym + fielddup(newname(s), hash) + r = l.Right + + // No pushtype allowed here. Tried and rejected. + typecheck(&r, Erv) + + l.Right = assignconv(r, f.Type, "field value") + } + } + + n.Op = OSTRUCTLIT + } + + if nerr != nerrors { + n.Type = nil + return + } + + n.Orig = norig + if Isptr[n.Type.Etype] { + n = Nod(OPTRLIT, n, nil) + n.Typecheck = 1 + n.Type = n.Left.Type + n.Left.Type = t + n.Left.Typecheck = 1 + } + + n.Orig = norig + return +} + +/* + * lvalue etc + */ +func islvalue(n *Node) bool { + switch n.Op { + case OINDEX: + if Isfixedarray(n.Left.Type) { + return islvalue(n.Left) + } + if n.Left.Type != nil && n.Left.Type.Etype == TSTRING { + return false + } + fallthrough + + // fall through + case OIND, ODOTPTR, OCLOSUREVAR, OPARAM: + return true + + case ODOT: + return islvalue(n.Left) + + case ONAME: + if n.Class == PFUNC { + return false + } + return true + } + + return false +} + +func checklvalue(n *Node, verb string) { + if !islvalue(n) { + Yyerror("cannot %s %v", verb, n) + } +} + +func checkassign(stmt *Node, n *Node) { + // Variables declared in ORANGE are assigned on every iteration. + if n.Defn != stmt || stmt.Op == ORANGE { + r := outervalue(n) + 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 + } + } + + l.Assigned = true + if l.Param != nil && l.Closure != nil { + l.Closure.Assigned = true + } + } + + if islvalue(n) { + return + } + if n.Op == OINDEXMAP { + n.Etype = 1 + return + } + + // have already complained about n being undefined + if n.Op == ONONAME { + return + } + + Yyerror("cannot assign to %v", n) +} + +func checkassignlist(stmt *Node, l *NodeList) { + for ; l != nil; l = l.Next { + checkassign(stmt, l.N) + } +} + +// Check whether l and r are the same side effect-free expression, +// so that it is safe to reuse one instead of computing both. +func samesafeexpr(l *Node, r *Node) bool { + if l.Op != r.Op || !Eqtype(l.Type, r.Type) { + return false + } + + switch l.Op { + case ONAME, OCLOSUREVAR: + return l == r + + case ODOT, ODOTPTR: + return l.Right != nil && r.Right != nil && l.Right.Sym == r.Right.Sym && samesafeexpr(l.Left, r.Left) + + case OIND: + return samesafeexpr(l.Left, r.Left) + + case OINDEX: + return samesafeexpr(l.Left, r.Left) && samesafeexpr(l.Right, r.Right) + } + + return false +} + +/* + * type check assignment. + * if this assignment is the definition of a var on the left side, + * fill in the var's type. + */ +func typecheckas(n *Node) { + // delicate little dance. + // the definition of n may refer to this assignment + // as its definition, in which case it will call typecheckas. + // in that case, do not call typecheck back, or it will cycle. + // if the variable has a type (ntype) then typechecking + // will not look at defn, so it is okay (and desirable, + // so that the conversion below happens). + n.Left = resolve(n.Left) + + if n.Left.Defn != n || n.Left.Ntype != nil { + typecheck(&n.Left, Erv|Easgn) + } + + typecheck(&n.Right, Erv) + checkassign(n, n.Left) + if n.Right != nil && n.Right.Type != nil { + if n.Left.Type != nil { + n.Right = assignconv(n.Right, n.Left.Type, "assignment") + } + } + + if n.Left.Defn == n && n.Left.Ntype == nil { + defaultlit(&n.Right, nil) + n.Left.Type = n.Right.Type + } + + // second half of dance. + // now that right is done, typecheck the left + // just to get it over with. see dance above. + n.Typecheck = 1 + + if n.Left.Typecheck == 0 { + typecheck(&n.Left, Erv|Easgn) + } +} + +func checkassignto(src *Type, dst *Node) { + var why string + + if assignop(src, dst.Type, &why) == 0 { + Yyerror("cannot assign %v to %v in multiple assignment%s", src, Nconv(dst, obj.FmtLong), why) + return + } +} + +func typecheckas2(n *Node) { + for ll := n.List; ll != nil; ll = ll.Next { + // delicate little dance. + ll.N = resolve(ll.N) + + if ll.N.Defn != n || ll.N.Ntype != nil { + typecheck(&ll.N, Erv|Easgn) + } + } + + cl := count(n.List) + cr := count(n.Rlist) + if cl > 1 && cr == 1 { + typecheck(&n.Rlist.N, Erv|Efnstruct) + } else { + typechecklist(n.Rlist, Erv) + } + checkassignlist(n, n.List) + + var l *Node + var r *Node + if cl == cr { + // easy + ll := n.List + lr := n.Rlist + for ; ll != nil; ll, lr = ll.Next, lr.Next { + 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 { + defaultlit(&lr.N, nil) + ll.N.Type = lr.N.Type + } + } + + goto out + } + + l = n.List.N + r = n.Rlist.N + + // x,y,z = f() + if cr == 1 { + if r.Type == nil { + goto out + } + switch r.Op { + case OCALLMETH, OCALLINTER, OCALLFUNC: + if r.Type.Etype != TSTRUCT || r.Type.Funarg == 0 { + break + } + cr = structcount(r.Type) + if cr != cl { + goto mismatch + } + n.Op = OAS2FUNC + var s Iter + t := Structfirst(&s, &r.Type) + for ll := n.List; ll != nil; ll = ll.Next { + if t.Type != nil && ll.N.Type != nil { + checkassignto(t.Type, ll.N) + } + if ll.N.Defn == n && ll.N.Ntype == nil { + ll.N.Type = t.Type + } + t = structnext(&s) + } + + goto out + } + } + + // x, ok = y + if cl == 2 && cr == 1 { + if r.Type == nil { + goto out + } + switch r.Op { + case OINDEXMAP, ORECV, ODOTTYPE: + switch r.Op { + case OINDEXMAP: + n.Op = OAS2MAPR + + case ORECV: + n.Op = OAS2RECV + + case ODOTTYPE: + n.Op = OAS2DOTTYPE + r.Op = ODOTTYPE2 + } + + if l.Type != nil { + checkassignto(r.Type, l) + } + if l.Defn == n { + l.Type = r.Type + } + l := n.List.Next.N + if l.Type != nil && l.Type.Etype != TBOOL { + checkassignto(Types[TBOOL], l) + } + if l.Defn == n && l.Ntype == nil { + l.Type = Types[TBOOL] + } + goto out + } + } + +mismatch: + Yyerror("assignment count mismatch: %d = %d", cl, cr) + + // second half of dance +out: + n.Typecheck = 1 + + for ll := n.List; ll != nil; ll = ll.Next { + if ll.N.Typecheck == 0 { + typecheck(&ll.N, Erv|Easgn) + } + } +} + +/* + * type check function definition + */ +func typecheckfunc(n *Node) { + typecheck(&n.Nname, Erv|Easgn) + t := n.Nname.Type + if t == nil { + return + } + n.Type = t + t.Nname = n.Nname + rcvr := getthisx(t).Type + if rcvr != nil && n.Func.Shortname != nil && !isblank(n.Func.Shortname) { + addmethod(n.Func.Shortname.Sym, t, true, n.Nname.Nointerface) + } + + 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.Name.Decldepth = 1 + } + } +} + +func stringtoarraylit(np **Node) { + n := *np + if n.Left.Op != OLITERAL || n.Left.Val.Ctype != CTSTR { + Fatal("stringtoarraylit %v", n) + } + + s := n.Left.Val.U.(string) + var l *NodeList + if n.Type.Type.Etype == TUINT8 { + // []byte + for i := 0; i < len(s); i++ { + l = list(l, Nod(OKEY, Nodintconst(int64(i)), Nodintconst(int64(s[0])))) + } + } else { + // []rune + i := 0 + for _, r := range s { + l = list(l, Nod(OKEY, Nodintconst(int64(i)), Nodintconst(int64(r)))) + i++ + } + } + + nn := Nod(OCOMPLIT, nil, typenod(n.Type)) + nn.List = l + typecheck(&nn, Erv) + *np = nn +} + +var ntypecheckdeftype int + +var methodqueue *NodeList + +func domethod(n *Node) { + nt := n.Type.Nname + typecheck(&nt, Etype) + if nt.Type == nil { + // type check failed; leave empty func + n.Type.Etype = TFUNC + + n.Type.Nod = nil + return + } + + // If we have + // type I interface { + // M(_ int) + // } + // then even though I.M looks like it doesn't care about the + // value of its argument, a specific implementation of I may + // care. The _ would suppress the assignment to that argument + // while generating a call, so remove it. + for t := getinargx(nt.Type).Type; t != nil; t = t.Down { + if t.Sym != nil && t.Sym.Name == "_" { + t.Sym = nil + } + } + + *n.Type = *nt.Type + n.Type.Nod = nil + checkwidth(n.Type) +} + +var mapqueue *NodeList + +func copytype(n *Node, t *Type) { + if t.Etype == TFORW { + // This type isn't computed yet; when it is, update n. + t.Copyto = list(t.Copyto, n) + + return + } + + maplineno := int(n.Type.Maplineno) + embedlineno := int(n.Type.Embedlineno) + + l := n.Type.Copyto + *n.Type = *t + + t = n.Type + t.Sym = n.Sym + t.Local = n.Local + t.Vargen = n.Vargen + t.Siggen = 0 + t.Method = nil + t.Xmethod = nil + t.Nod = nil + t.Printed = 0 + t.Deferwidth = 0 + t.Copyto = nil + + // Update nodes waiting on this type. + for ; l != nil; l = l.Next { + copytype(l.N, t) + } + + // Double-check use of type as embedded type. + lno := int(lineno) + + if embedlineno != 0 { + lineno = int32(embedlineno) + if Isptr[t.Etype] { + Yyerror("embedded type cannot be a pointer") + } + } + + lineno = int32(lno) + + // Queue check for map until all the types are done settling. + if maplineno != 0 { + t.Maplineno = int32(maplineno) + mapqueue = list(mapqueue, n) + } +} + +func typecheckdeftype(n *Node) { + ntypecheckdeftype++ + lno := int(lineno) + setlineno(n) + n.Type.Sym = n.Sym + n.Typecheck = 1 + typecheck(&n.Ntype, Etype) + t := n.Ntype.Type + if t == nil { + n.Diag = 1 + n.Type = nil + goto ret + } + + if n.Type == nil { + n.Diag = 1 + goto ret + } + + // copy new type and clear fields + // that don't come along. + // anything zeroed here must be zeroed in + // typedcl2 too. + copytype(n, t) + +ret: + lineno = int32(lno) + + // if there are no type definitions going on, it's safe to + // try to resolve the method types for the interfaces + // we just read. + if ntypecheckdeftype == 1 { + var l *NodeList + for { + l = methodqueue + if l == nil { + break + } + methodqueue = nil + for ; l != nil; l = l.Next { + domethod(l.N) + } + } + + for l := mapqueue; l != nil; l = l.Next { + lineno = l.N.Type.Maplineno + maptype(l.N.Type, Types[TBOOL]) + } + + lineno = int32(lno) + } + + ntypecheckdeftype-- +} + +func queuemethod(n *Node) { + if ntypecheckdeftype == 0 { + domethod(n) + return + } + + methodqueue = list(methodqueue, n) +} + +func typecheckdef(n *Node) *Node { + lno := int(lineno) + setlineno(n) + + if n.Op == ONONAME { + if n.Diag == 0 { + n.Diag = 1 + if n.Lineno != 0 { + lineno = n.Lineno + } + + // Note: adderrorname looks for this string and + // adds context about the outer expression + Yyerror("undefined: %v", n.Sym) + } + + return n + } + + if n.Walkdef == 1 { + return n + } + + l := new(NodeList) + l.N = n + l.Next = typecheckdefstack + typecheckdefstack = l + + if n.Walkdef == 2 { + Flusherrors() + fmt.Printf("typecheckdef loop:") + for l := typecheckdefstack; l != nil; l = l.Next { + fmt.Printf(" %v", l.N.Sym) + } + fmt.Printf("\n") + Fatal("typecheckdef loop") + } + + n.Walkdef = 2 + + if n.Type != nil || n.Sym == nil { // builtin or no name + goto ret + } + + switch n.Op { + default: + Fatal("typecheckdef %v", Oconv(int(n.Op), 0)) + + // not really syms + case OGOTO, OLABEL: + break + + case OLITERAL: + if n.Ntype != nil { + typecheck(&n.Ntype, Etype) + n.Type = n.Ntype.Type + n.Ntype = nil + if n.Type == nil { + n.Diag = 1 + goto ret + } + } + + e := n.Defn + n.Defn = nil + if e == nil { + lineno = n.Lineno + Dump("typecheckdef nil defn", n) + Yyerror("xxx") + } + + typecheck(&e, Erv|Eiota) + if Isconst(e, CTNIL) { + Yyerror("const initializer cannot be nil") + goto ret + } + + if e.Type != nil && e.Op != OLITERAL || !isgoconst(e) { + if e.Diag == 0 { + Yyerror("const initializer %v is not a constant", e) + e.Diag = 1 + } + + goto ret + } + + t := n.Type + if t != nil { + if !okforconst[t.Etype] { + Yyerror("invalid constant type %v", t) + goto ret + } + + if !isideal(e.Type) && !Eqtype(t, e.Type) { + Yyerror("cannot use %v as type %v in const initializer", Nconv(e, obj.FmtLong), t) + goto ret + } + + Convlit(&e, t) + } + + n.Val = e.Val + n.Type = e.Type + + case ONAME: + if n.Ntype != nil { + typecheck(&n.Ntype, Etype) + n.Type = n.Ntype.Type + + if n.Type == nil { + n.Diag = 1 + goto ret + } + } + + if n.Type != nil { + break + } + if n.Defn == nil { + if n.Etype != 0 { // like OPRINTN + break + } + if nsavederrors+nerrors > 0 { + // Can have undefined variables in x := foo + // that make x have an n->ndefn == nil. + // If there are other errors anyway, don't + // bother adding to the noise. + break + } + + Fatal("var without type, init: %v", n.Sym) + } + + if n.Defn.Op == ONAME { + typecheck(&n.Defn, Erv) + n.Type = n.Defn.Type + break + } + + typecheck(&n.Defn, Etop) // fills in n->type + + case OTYPE: + if Curfn != nil { + defercheckwidth() + } + n.Walkdef = 1 + n.Type = typ(TFORW) + n.Type.Sym = n.Sym + nerrors0 := nerrors + typecheckdeftype(n) + if n.Type.Etype == TFORW && nerrors > nerrors0 { + // Something went wrong during type-checking, + // but it was reported. Silence future errors. + n.Type.Broke = 1 + } + + if Curfn != nil { + resumecheckwidth() + } + + // nothing to see here + case OPACK: + break + } + +ret: + if n.Op != OLITERAL && n.Type != nil && isideal(n.Type) { + Fatal("got %v for %v", n.Type, n) + } + if typecheckdefstack.N != n { + Fatal("typecheckdefstack mismatch") + } + l = typecheckdefstack + typecheckdefstack = l.Next + + lineno = int32(lno) + n.Walkdef = 1 + return n +} + +func checkmake(t *Type, arg string, n *Node) int { + if n.Op == OLITERAL { + switch n.Val.Ctype { + case CTINT, CTRUNE, CTFLT, CTCPLX: + n.Val = toint(n.Val) + if mpcmpfixc(n.Val.U.(*Mpint), 0) < 0 { + Yyerror("negative %s argument in make(%v)", arg, t) + return -1 + } + + if Mpcmpfixfix(n.Val.U.(*Mpint), Maxintval[TINT]) > 0 { + Yyerror("%s argument too large in make(%v)", arg, t) + return -1 + } + + // Delay defaultlit until after we've checked range, to avoid + // a redundant "constant NNN overflows int" error. + defaultlit(&n, Types[TINT]) + + return 0 + + default: + break + } + } + + if !Isint[n.Type.Etype] && n.Type.Etype != TIDEAL { + Yyerror("non-integer %s argument in make(%v) - %v", arg, t, n.Type) + return -1 + } + + // Defaultlit still necessary for non-constant: n might be 1<left may be mutated by typechecking, check it explicitly + // first to track it correctly. + typecheck(&r.Left, Erv) + + base := r.Left + typecheck(&r, Erv) + switch r.Op { + case ODOT, ODOTPTR: + break + + case OCALLPART: + Yyerror("invalid expression %v: argument is a method value", nn) + v = 0 + goto ret + + default: + goto bad + } + + v = 0 + + // add offsets for inserted dots. + var r1 *Node + for r1 = r; r1.Left != base; r1 = r1.Left { + switch r1.Op { + case ODOT: + v += r1.Xoffset + + case ODOTPTR: + Yyerror("invalid expression %v: selector implies indirection of embedded %v", nn, r1.Left) + goto ret + + default: + Dump("unsafenmagic", r) + Fatal("impossible %v node after dot insertion", Oconv(int(r1.Op), obj.FmtSharp)) + goto bad + } + } + + v += r1.Xoffset + goto yes + } + + if s.Name == "Alignof" { + typecheck(&r, Erv) + defaultlit(&r, nil) + tr := r.Type + if tr == nil { + goto bad + } + + // make struct { byte; T; } + t := typ(TSTRUCT) + + t.Type = typ(TFIELD) + t.Type.Type = Types[TUINT8] + t.Type.Down = typ(TFIELD) + t.Type.Down.Type = tr + + // compute struct widths + dowidth(t) + + // the offset of T is its required alignment + v = t.Type.Down.Width + + goto yes + } + + return nil + +bad: + Yyerror("invalid expression %v", nn) + v = 0 + goto ret + +yes: + if args.Next != nil { + Yyerror("extra arguments for %v", s) + } + + // any side effects disappear; ignore init +ret: + var val Val + val.Ctype = CTINT + + val.U = new(Mpint) + Mpmovecfix(val.U.(*Mpint), v) + n := Nod(OLITERAL, nil, nil) + n.Orig = nn + n.Val = val + n.Type = Types[TUINTPTR] + nn.Type = Types[TUINTPTR] + return n +} + +func isunsafebuiltin(n *Node) bool { + if n == nil || n.Op != ONAME || n.Sym == nil || n.Sym.Pkg != unsafepkg { + return false + } + if n.Sym.Name == "Sizeof" { + return true + } + if n.Sym.Name == "Offsetof" { + return true + } + if n.Sym.Name == "Alignof" { + return true + } + return false +} diff --git a/src/cmd/compile/internal/gc/util.go b/src/cmd/compile/internal/gc/util.go new file mode 100644 index 0000000000..c59af0665b --- /dev/null +++ b/src/cmd/compile/internal/gc/util.go @@ -0,0 +1,103 @@ +package gc + +import ( + "os" + "runtime" + "runtime/pprof" + "strconv" + "strings" +) + +func (n *Node) Line() string { + return Ctxt.LineHist.LineString(int(n.Lineno)) +} + +func atoi(s string) int { + // NOTE: Not strconv.Atoi, accepts hex and octal prefixes. + n, _ := strconv.ParseInt(s, 0, 0) + return int(n) +} + +func isalnum(c int) bool { + return isalpha(c) || isdigit(c) +} + +func isalpha(c int) bool { + return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' +} + +func isdigit(c int) bool { + return '0' <= c && c <= '9' +} + +func plan9quote(s string) string { + if s == "" { + return "'" + strings.Replace(s, "'", "''", -1) + "'" + } + for i := 0; i < len(s); i++ { + if s[i] <= ' ' || s[i] == '\'' { + return "'" + strings.Replace(s, "'", "''", -1) + "'" + } + } + return s +} + +// strings.Compare, introduced in Go 1.5. +func stringsCompare(a, b string) int { + if a == b { + return 0 + } + if a < b { + return -1 + } + return +1 +} + +var atExitFuncs []func() + +func AtExit(f func()) { + atExitFuncs = append(atExitFuncs, f) +} + +func Exit(code int) { + for i := len(atExitFuncs) - 1; i >= 0; i-- { + f := atExitFuncs[i] + atExitFuncs = atExitFuncs[:i] + f() + } + os.Exit(code) +} + +var ( + cpuprofile string + memprofile string + memprofilerate int64 +) + +func startProfile() { + if cpuprofile != "" { + f, err := os.Create(cpuprofile) + if err != nil { + Fatal("%v", err) + } + if err := pprof.StartCPUProfile(f); err != nil { + Fatal("%v", err) + } + AtExit(pprof.StopCPUProfile) + } + if memprofile != "" { + if memprofilerate != 0 { + runtime.MemProfileRate = int(memprofilerate) + } + f, err := os.Create(memprofile) + if err != nil { + Fatal("%v", err) + } + AtExit(func() { + runtime.GC() // profile all outstanding allocations + if err := pprof.WriteHeapProfile(f); err != nil { + Fatal("%v", err) + } + }) + } +} diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go new file mode 100644 index 0000000000..b5b8611e5b --- /dev/null +++ b/src/cmd/compile/internal/gc/walk.go @@ -0,0 +1,4090 @@ +// Copyright 2009 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 gc + +import ( + "cmd/internal/obj" + "fmt" + "strings" +) + +var mpzero Mpint + +// The constant is known to runtime. +const ( + tmpstringbufsize = 32 +) + +func walk(fn *Node) { + Curfn = fn + + if Debug['W'] != 0 { + s := fmt.Sprintf("\nbefore %v", Curfn.Nname.Sym) + dumplist(s, Curfn.Nbody) + } + + lno := int(lineno) + + // Final typecheck for any unused variables. + // It's hard to be on the heap when not-used, but best to be consistent about &~PHEAP here and below. + for l := fn.Func.Dcl; l != nil; l = l.Next { + if l.N.Op == ONAME && l.N.Class&^PHEAP == PAUTO { + typecheck(&l.N, Erv|Easgn) + } + } + + // Propagate the used flag for typeswitch variables up to the NONAME in it's definition. + for l := fn.Func.Dcl; l != nil; l = l.Next { + if l.N.Op == ONAME && l.N.Class&^PHEAP == PAUTO && l.N.Defn != nil && l.N.Defn.Op == OTYPESW && l.N.Used { + l.N.Defn.Left.Used = true + } + } + + for l := fn.Func.Dcl; l != nil; l = l.Next { + if l.N.Op != ONAME || l.N.Class&^PHEAP != PAUTO || l.N.Sym.Name[0] == '&' || l.N.Used { + continue + } + if l.N.Defn != nil && l.N.Defn.Op == OTYPESW { + if l.N.Defn.Left.Used { + continue + } + lineno = l.N.Defn.Left.Lineno + Yyerror("%v declared and not used", l.N.Sym) + l.N.Defn.Left.Used = true // suppress repeats + } else { + lineno = l.N.Lineno + Yyerror("%v declared and not used", l.N.Sym) + } + } + + lineno = int32(lno) + if nerrors != 0 { + return + } + walkstmtlist(Curfn.Nbody) + if Debug['W'] != 0 { + s := fmt.Sprintf("after walk %v", Curfn.Nname.Sym) + dumplist(s, Curfn.Nbody) + } + + heapmoves() + if Debug['W'] != 0 && Curfn.Func.Enter != nil { + s := fmt.Sprintf("enter %v", Curfn.Nname.Sym) + dumplist(s, Curfn.Func.Enter) + } +} + +func walkstmtlist(l *NodeList) { + for ; l != nil; l = l.Next { + walkstmt(&l.N) + } +} + +func samelist(a *NodeList, b *NodeList) bool { + for ; a != nil && b != nil; a, b = a.Next, b.Next { + if a.N != b.N { + return false + } + } + return a == b +} + +func paramoutheap(fn *Node) bool { + for l := fn.Func.Dcl; l != nil; l = l.Next { + switch l.N.Class { + case PPARAMOUT, + PPARAMOUT | PHEAP: + return l.N.Addrtaken + + // stop early - parameters are over + case PAUTO, + PAUTO | PHEAP: + return false + } + } + + return false +} + +// adds "adjust" to all the argument locations for the call n. +// n must be a defer or go node that has already been walked. +func adjustargs(n *Node, adjust int) { + var arg *Node + var lhs *Node + + callfunc := n.Left + for args := callfunc.List; args != nil; args = args.Next { + arg = args.N + if arg.Op != OAS { + Yyerror("call arg not assignment") + } + lhs = arg.Left + if lhs.Op == ONAME { + // This is a temporary introduced by reorder1. + // The real store to the stack appears later in the arg list. + continue + } + + if lhs.Op != OINDREG { + Yyerror("call argument store does not use OINDREG") + } + + // can't really check this in machine-indep code. + //if(lhs->val.u.reg != D_SP) + // yyerror("call arg assign not indreg(SP)"); + lhs.Xoffset += int64(adjust) + } +} + +func walkstmt(np **Node) { + n := *np + if n == nil { + return + } + if n.Dodata == 2 { // don't walk, generated by anylit. + return + } + + setlineno(n) + + walkstmtlist(n.Ninit) + + switch n.Op { + default: + if n.Op == ONAME { + Yyerror("%v is not a top level statement", n.Sym) + } else { + Yyerror("%v is not a top level statement", Oconv(int(n.Op), 0)) + } + Dump("nottop", n) + + case OAS, + OASOP, + OAS2, + OAS2DOTTYPE, + OAS2RECV, + OAS2FUNC, + OAS2MAPR, + OCLOSE, + OCOPY, + OCALLMETH, + OCALLINTER, + OCALL, + OCALLFUNC, + ODELETE, + OSEND, + OPRINT, + OPRINTN, + OPANIC, + OEMPTY, + ORECOVER, + OGETG: + if n.Typecheck == 0 { + Fatal("missing typecheck: %v", Nconv(n, obj.FmtSign)) + } + init := n.Ninit + n.Ninit = nil + walkexpr(&n, &init) + addinit(&n, init) + if (*np).Op == OCOPY && n.Op == OCONVNOP { + n.Op = OEMPTY // don't leave plain values as statements. + } + + // special case for a receive where we throw away + // the value received. + case ORECV: + if n.Typecheck == 0 { + Fatal("missing typecheck: %v", Nconv(n, obj.FmtSign)) + } + init := n.Ninit + n.Ninit = nil + + walkexpr(&n.Left, &init) + n = mkcall1(chanfn("chanrecv1", 2, n.Left.Type), nil, &init, typename(n.Left.Type), n.Left, nodnil()) + walkexpr(&n, &init) + + addinit(&n, init) + + case OBREAK, + ODCL, + OCONTINUE, + OFALL, + OGOTO, + OLABEL, + ODCLCONST, + ODCLTYPE, + OCHECKNIL, + OVARKILL: + break + + case OBLOCK: + walkstmtlist(n.List) + + case OXCASE: + Yyerror("case statement out of place") + n.Op = OCASE + fallthrough + + case OCASE: + walkstmt(&n.Right) + + case ODEFER: + Hasdefer = 1 + switch n.Left.Op { + case OPRINT, OPRINTN: + walkprintfunc(&n.Left, &n.Ninit) + + case OCOPY: + n.Left = copyany(n.Left, &n.Ninit, 1) + + default: + walkexpr(&n.Left, &n.Ninit) + } + + // make room for size & fn arguments. + adjustargs(n, 2*Widthptr) + + case OFOR: + if n.Ntest != nil { + walkstmtlist(n.Ntest.Ninit) + init := n.Ntest.Ninit + n.Ntest.Ninit = nil + walkexpr(&n.Ntest, &init) + addinit(&n.Ntest, init) + } + + walkstmt(&n.Nincr) + walkstmtlist(n.Nbody) + + case OIF: + walkexpr(&n.Ntest, &n.Ninit) + walkstmtlist(n.Nbody) + walkstmtlist(n.Nelse) + + case OPROC: + switch n.Left.Op { + case OPRINT, OPRINTN: + walkprintfunc(&n.Left, &n.Ninit) + + case OCOPY: + n.Left = copyany(n.Left, &n.Ninit, 1) + + default: + walkexpr(&n.Left, &n.Ninit) + } + + // make room for size & fn arguments. + adjustargs(n, 2*Widthptr) + + case ORETURN: + walkexprlist(n.List, &n.Ninit) + if n.List == nil { + break + } + if (Curfn.Type.Outnamed != 0 && count(n.List) > 1) || paramoutheap(Curfn) { + // assign to the function out parameters, + // so that reorder3 can fix up conflicts + var rl *NodeList + + var cl uint8 + for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { + cl = ll.N.Class &^ PHEAP + if cl == PAUTO { + break + } + if cl == PPARAMOUT { + rl = list(rl, ll.N) + } + } + + if samelist(rl, n.List) { + // special return in disguise + n.List = nil + + break + } + + if count(n.List) == 1 && count(rl) > 1 { + // OAS2FUNC in disguise + f := n.List.N + + if f.Op != OCALLFUNC && f.Op != OCALLMETH && f.Op != OCALLINTER { + Fatal("expected return of call, have %v", f) + } + n.List = concat(list1(f), ascompatet(int(n.Op), rl, &f.Type, 0, &n.Ninit)) + break + } + + // move function calls out, to make reorder3's job easier. + walkexprlistsafe(n.List, &n.Ninit) + + ll := ascompatee(int(n.Op), rl, n.List, &n.Ninit) + n.List = reorder3(ll) + break + } + + ll := ascompatte(int(n.Op), nil, false, Getoutarg(Curfn.Type), n.List, 1, &n.Ninit) + n.List = ll + + case ORETJMP: + break + + case OSELECT: + walkselect(n) + + case OSWITCH: + walkswitch(n) + + case ORANGE: + walkrange(n) + + case OXFALL: + Yyerror("fallthrough statement out of place") + n.Op = OFALL + } + + if n.Op == ONAME { + Fatal("walkstmt ended up with name: %v", Nconv(n, obj.FmtSign)) + } + + *np = n +} + +/* + * walk the whole tree of the body of an + * expression or simple statement. + * the types expressions are calculated. + * compile-time constants are evaluated. + * complex side effects like statements are appended to init + */ +func walkexprlist(l *NodeList, init **NodeList) { + for ; l != nil; l = l.Next { + walkexpr(&l.N, init) + } +} + +func walkexprlistsafe(l *NodeList, init **NodeList) { + for ; l != nil; l = l.Next { + l.N = safeexpr(l.N, init) + walkexpr(&l.N, init) + } +} + +func walkexprlistcheap(l *NodeList, init **NodeList) { + for ; l != nil; l = l.Next { + l.N = cheapexpr(l.N, init) + walkexpr(&l.N, init) + } +} + +func walkexpr(np **Node, init **NodeList) { + n := *np + + if n == nil { + return + } + + if init == &n.Ninit { + // not okay to use n->ninit when walking n, + // because we might replace n with some other node + // and would lose the init list. + Fatal("walkexpr init == &n->ninit") + } + + if n.Ninit != nil { + walkstmtlist(n.Ninit) + *init = concat(*init, n.Ninit) + n.Ninit = nil + } + + // annoying case - not typechecked + if n.Op == OKEY { + walkexpr(&n.Left, init) + walkexpr(&n.Right, init) + return + } + + lno := setlineno(n) + + if Debug['w'] > 1 { + Dump("walk-before", n) + } + + if n.Typecheck != 1 { + Fatal("missed typecheck: %v\n", Nconv(n, obj.FmtSign)) + } + + switch n.Op { + default: + Dump("walk", n) + Fatal("walkexpr: switch 1 unknown op %v", Nconv(n, obj.FmtShort|obj.FmtSign)) + + case OTYPE, + ONONAME, + OINDREG, + OEMPTY, + OPARAM, + OGETG: + goto ret + + case ONOT, + OMINUS, + OPLUS, + OCOM, + OREAL, + OIMAG, + ODOTMETH, + ODOTINTER: + walkexpr(&n.Left, init) + goto ret + + case OIND: + walkexpr(&n.Left, init) + goto ret + + case ODOT: + usefield(n) + walkexpr(&n.Left, init) + goto ret + + case ODOTPTR: + usefield(n) + if n.Op == ODOTPTR && n.Left.Type.Type.Width == 0 { + // No actual copy will be generated, so emit an explicit nil check. + n.Left = cheapexpr(n.Left, init) + + checknil(n.Left, init) + } + + walkexpr(&n.Left, init) + goto ret + + case OEFACE: + walkexpr(&n.Left, init) + walkexpr(&n.Right, init) + goto ret + + case OSPTR, OITAB: + walkexpr(&n.Left, init) + goto ret + + case OLEN, OCAP: + walkexpr(&n.Left, init) + + // replace len(*[10]int) with 10. + // delayed until now to preserve side effects. + t := n.Left.Type + + if Isptr[t.Etype] { + t = t.Type + } + if Isfixedarray(t) { + safeexpr(n.Left, init) + Nodconst(n, n.Type, t.Bound) + n.Typecheck = 1 + } + + goto ret + + case OLSH, ORSH: + walkexpr(&n.Left, init) + walkexpr(&n.Right, init) + t := n.Left.Type + n.Bounded = bounded(n.Right, 8*t.Width) + if Debug['m'] != 0 && n.Etype != 0 && !Isconst(n.Right, CTINT) { + Warn("shift bounds check elided") + } + goto ret + + // Use results from call expression as arguments for complex. + case OAND, + OSUB, + OHMUL, + OLT, + OLE, + OGE, + OGT, + OADD, + OCOMPLEX, + OLROT: + if n.Op == OCOMPLEX && n.Left == nil && n.Right == nil { + n.Left = n.List.N + n.Right = n.List.Next.N + } + + walkexpr(&n.Left, init) + walkexpr(&n.Right, init) + goto ret + + case OOR, OXOR: + walkexpr(&n.Left, init) + walkexpr(&n.Right, init) + walkrotate(&n) + goto ret + + case OEQ, ONE: + walkexpr(&n.Left, init) + walkexpr(&n.Right, init) + + // Disable safemode while compiling this code: the code we + // generate internally can refer to unsafe.Pointer. + // In this case it can happen if we need to generate an == + // for a struct containing a reflect.Value, which itself has + // an unexported field of type unsafe.Pointer. + old_safemode := safemode + + safemode = 0 + walkcompare(&n, init) + safemode = old_safemode + goto ret + + case OANDAND, OOROR: + walkexpr(&n.Left, init) + + // cannot put side effects from n.Right on init, + // because they cannot run before n.Left is checked. + // save elsewhere and store on the eventual n.Right. + var ll *NodeList + + walkexpr(&n.Right, &ll) + addinit(&n.Right, ll) + goto ret + + case OPRINT, OPRINTN: + walkexprlist(n.List, init) + n = walkprint(n, init) + goto ret + + case OPANIC: + n = mkcall("gopanic", nil, init, n.Left) + goto ret + + case ORECOVER: + n = mkcall("gorecover", n.Type, init, Nod(OADDR, nodfp, nil)) + goto ret + + case OLITERAL: + n.Addable = true + goto ret + + case OCLOSUREVAR, OCFUNC: + n.Addable = true + goto ret + + case ONAME: + if n.Class&PHEAP == 0 && n.Class != PPARAMREF { + n.Addable = true + } + goto ret + + case OCALLINTER: + t := n.Left.Type + if n.List != nil && n.List.N.Op == OAS { + goto ret + } + walkexpr(&n.Left, init) + walkexprlist(n.List, init) + ll := ascompatte(int(n.Op), n, n.Isddd, getinarg(t), n.List, 0, init) + n.List = reorder1(ll) + goto ret + + case OCALLFUNC: + if n.Left.Op == OCLOSURE { + // Transform direct call of a closure to call of a normal function. + // transformclosure already did all preparation work. + + // Append captured variables to argument list. + n.List = concat(n.List, n.Left.Func.Enter) + + n.Left.Func.Enter = nil + + // Replace OCLOSURE with ONAME/PFUNC. + n.Left = n.Left.Closure.Nname + + // Update type of OCALLFUNC node. + // Output arguments had not changed, but their offsets could. + if n.Left.Type.Outtuple == 1 { + t := getoutargx(n.Left.Type).Type + if t.Etype == TFIELD { + t = t.Type + } + n.Type = t + } else { + n.Type = getoutargx(n.Left.Type) + } + } + + t := n.Left.Type + if n.List != nil && n.List.N.Op == OAS { + goto ret + } + + walkexpr(&n.Left, init) + walkexprlist(n.List, init) + + if n.Left.Op == ONAME && n.Left.Sym.Name == "Sqrt" && n.Left.Sym.Pkg.Path == "math" { + switch Thearch.Thechar { + case '5', '6', '7': + n.Op = OSQRT + n.Left = n.List.N + n.List = nil + goto ret + } + } + + ll := ascompatte(int(n.Op), n, n.Isddd, getinarg(t), n.List, 0, init) + n.List = reorder1(ll) + goto ret + + case OCALLMETH: + t := n.Left.Type + if n.List != nil && n.List.N.Op == OAS { + goto ret + } + walkexpr(&n.Left, init) + walkexprlist(n.List, init) + ll := ascompatte(int(n.Op), n, false, getthis(t), list1(n.Left.Left), 0, init) + lr := ascompatte(int(n.Op), n, n.Isddd, getinarg(t), n.List, 0, init) + ll = concat(ll, lr) + n.Left.Left = nil + ullmancalc(n.Left) + n.List = reorder1(ll) + goto ret + + case OAS: + *init = concat(*init, n.Ninit) + n.Ninit = nil + + walkexpr(&n.Left, init) + n.Left = safeexpr(n.Left, init) + + if oaslit(n, init) { + goto ret + } + + if n.Right == nil || iszero(n.Right) && flag_race == 0 { + goto ret + } + + switch n.Right.Op { + default: + walkexpr(&n.Right, init) + + case ODOTTYPE: + // TODO(rsc): The Isfat is for consistency with componentgen and orderexpr. + // It needs to be removed in all three places. + // That would allow inlining x.(struct{*int}) the same as x.(*int). + if isdirectiface(n.Right.Type) && !Isfat(n.Right.Type) && flag_race == 0 { + // handled directly during cgen + walkexpr(&n.Right, init) + break + } + + // x = i.(T); n.Left is x, n.Right.Left is i. + // orderstmt made sure x is addressable. + walkexpr(&n.Right.Left, init) + + n1 := Nod(OADDR, n.Left, nil) + r := n.Right // i.(T) + + if Debug_typeassert > 0 { + Warn("type assertion not inlined") + } + + buf := "assert" + type2IET(r.Left.Type) + "2" + type2IET(r.Type) + fn := syslook(buf, 1) + substArgTypes(fn, r.Left.Type, r.Type) + + n = mkcall1(fn, nil, init, typename(r.Type), r.Left, n1) + walkexpr(&n, init) + goto ret + + case ORECV: + // x = <-c; n.Left is x, n.Right.Left is c. + // orderstmt made sure x is addressable. + walkexpr(&n.Right.Left, init) + + n1 := Nod(OADDR, n.Left, nil) + r := n.Right.Left // the channel + 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 { + r := convas(Nod(OAS, n.Left, n.Right), init) + r.Dodata = n.Dodata + n = r + n = applywritebarrier(n, init) + } + + goto ret + + case OAS2: + *init = concat(*init, n.Ninit) + n.Ninit = nil + walkexprlistsafe(n.List, init) + walkexprlistsafe(n.Rlist, init) + ll := ascompatee(OAS, n.List, n.Rlist, init) + ll = reorder3(ll) + for lr := ll; lr != nil; lr = lr.Next { + lr.N = applywritebarrier(lr.N, init) + } + n = liststmt(ll) + goto ret + + // a,b,... = fn() + case OAS2FUNC: + *init = concat(*init, n.Ninit) + + n.Ninit = nil + r := n.Rlist.N + walkexprlistsafe(n.List, init) + walkexpr(&r, init) + + ll := ascompatet(int(n.Op), n.List, &r.Type, 0, init) + for lr := ll; lr != nil; lr = lr.Next { + lr.N = applywritebarrier(lr.N, init) + } + n = liststmt(concat(list1(r), ll)) + goto ret + + // x, y = <-c + // orderstmt made sure x is addressable. + case OAS2RECV: + *init = concat(*init, n.Ninit) + + n.Ninit = nil + r := n.Rlist.N + walkexprlistsafe(n.List, init) + walkexpr(&r.Left, init) + var n1 *Node + if isblank(n.List.N) { + n1 = nodnil() + } else { + n1 = Nod(OADDR, n.List.N, nil) + } + n1.Etype = 1 // addr does not escape + fn := chanfn("chanrecv2", 2, r.Left.Type) + r = mkcall1(fn, n.List.Next.N.Type, init, typename(r.Left.Type), r.Left, n1) + n = Nod(OAS, n.List.Next.N, r) + typecheck(&n, Etop) + goto ret + + // a,b = m[i]; + case OAS2MAPR: + *init = concat(*init, n.Ninit) + + n.Ninit = nil + r := n.Rlist.N + walkexprlistsafe(n.List, init) + walkexpr(&r.Left, init) + walkexpr(&r.Right, init) + t := r.Left.Type + p := "" + if t.Type.Width <= 128 { // Check ../../runtime/hashmap.go:maxValueSize before changing. + switch Simsimtype(t.Down) { + case TINT32, TUINT32: + p = "mapaccess2_fast32" + + case TINT64, TUINT64: + p = "mapaccess2_fast64" + + case TSTRING: + p = "mapaccess2_faststr" + } + } + + var key *Node + if p != "" { + // fast versions take key by value + key = r.Right + } else { + // standard version takes key by reference + // orderexpr made sure key is addressable. + key = Nod(OADDR, r.Right, nil) + + p = "mapaccess2" + } + + // from: + // a,b = m[i] + // to: + // var,b = mapaccess2*(t, m, i) + // a = *var + a := n.List.N + + fn := mapfn(p, t) + r = mkcall1(fn, getoutargx(fn.Type), init, typename(t), r.Left, key) + + // mapaccess2* returns a typed bool, but due to spec changes, + // the boolean result of i.(T) is now untyped so we make it the + // same type as the variable on the lhs. + if !isblank(n.List.Next.N) { + r.Type.Type.Down.Type = n.List.Next.N.Type + } + n.Rlist = list1(r) + n.Op = OAS2FUNC + + // don't generate a = *var if a is _ + if !isblank(a) { + var_ := temp(Ptrto(t.Type)) + var_.Typecheck = 1 + n.List.N = var_ + walkexpr(&n, init) + *init = list(*init, n) + n = Nod(OAS, a, Nod(OIND, var_, nil)) + } + + typecheck(&n, Etop) + walkexpr(&n, init) + + // mapaccess needs a zero value to be at least this big. + if zerosize < t.Type.Width { + zerosize = t.Type.Width + } + + // TODO: ptr is always non-nil, so disable nil check for this OIND op. + goto ret + + case ODELETE: + *init = concat(*init, n.Ninit) + n.Ninit = nil + map_ := n.List.N + key := n.List.Next.N + walkexpr(&map_, init) + walkexpr(&key, init) + + // orderstmt made sure key is addressable. + key = Nod(OADDR, key, nil) + + t := map_.Type + n = mkcall1(mapfndel("mapdelete", t), nil, init, typename(t), map_, key) + goto ret + + case OAS2DOTTYPE: + e := n.Rlist.N // i.(T) + // TODO(rsc): The Isfat is for consistency with componentgen and orderexpr. + // It needs to be removed in all three places. + // That would allow inlining x.(struct{*int}) the same as x.(*int). + if isdirectiface(e.Type) && !Isfat(e.Type) && flag_race == 0 { + // handled directly during gen. + walkexprlistsafe(n.List, init) + walkexpr(&e.Left, init) + goto ret + } + + // res, ok = i.(T) + // orderstmt made sure a is addressable. + *init = concat(*init, n.Ninit) + n.Ninit = nil + + walkexprlistsafe(n.List, init) + walkexpr(&e.Left, init) + t := e.Type // T + from := e.Left // i + + oktype := Types[TBOOL] + ok := n.List.Next.N + if !isblank(ok) { + oktype = ok.Type + } + + fromKind := type2IET(from.Type) + toKind := type2IET(t) + + // Avoid runtime calls in a few cases of the form _, ok := i.(T). + // This is faster and shorter and allows the corresponding assertX2X2 + // routines to skip nil checks on their last argument. + if isblank(n.List.N) { + var fast *Node + switch { + case fromKind == "E" && toKind == "T": + tab := Nod(OITAB, from, nil) // type:eface::tab:iface + typ := Nod(OCONVNOP, typename(t), nil) + typ.Type = Ptrto(Types[TUINTPTR]) + fast = Nod(OEQ, tab, typ) + case fromKind == "I" && toKind == "E", + fromKind == "E" && toKind == "E": + tab := Nod(OITAB, from, nil) + fast = Nod(ONE, nodnil(), tab) + } + if fast != nil { + if Debug_typeassert > 0 { + Warn("type assertion (ok only) inlined") + } + n = Nod(OAS, ok, fast) + typecheck(&n, Etop) + goto ret + } + } + + var resptr *Node // &res + if isblank(n.List.N) { + resptr = nodnil() + } else { + resptr = Nod(OADDR, n.List.N, nil) + } + resptr.Etype = 1 // addr does not escape + + if Debug_typeassert > 0 { + Warn("type assertion not inlined") + } + buf := "assert" + fromKind + "2" + toKind + "2" + fn := syslook(buf, 1) + substArgTypes(fn, from.Type, t) + call := mkcall1(fn, oktype, init, typename(t), from, resptr) + n = Nod(OAS, ok, call) + typecheck(&n, Etop) + goto ret + + case ODOTTYPE, ODOTTYPE2: + if !isdirectiface(n.Type) || Isfat(n.Type) { + Fatal("walkexpr ODOTTYPE") // should see inside OAS only + } + walkexpr(&n.Left, init) + goto ret + + case OCONVIFACE: + walkexpr(&n.Left, init) + + // Optimize convT2E as a two-word copy when T is pointer-shaped. + if isnilinter(n.Type) && isdirectiface(n.Left.Type) { + l := Nod(OEFACE, typename(n.Left.Type), n.Left) + l.Type = n.Type + l.Typecheck = n.Typecheck + n = l + goto ret + } + + // Build name of function: convI2E etc. + // Not all names are possible + // (e.g., we'll never generate convE2E or convE2I). + buf := "conv" + type2IET(n.Left.Type) + "2" + type2IET(n.Type) + fn := syslook(buf, 1) + var ll *NodeList + if !Isinter(n.Left.Type) { + ll = list(ll, typename(n.Left.Type)) + } + if !isnilinter(n.Type) { + ll = list(ll, typename(n.Type)) + } + if !Isinter(n.Left.Type) && !isnilinter(n.Type) { + sym := Pkglookup(Tconv(n.Left.Type, obj.FmtLeft)+"."+Tconv(n.Type, obj.FmtLeft), itabpkg) + if sym.Def == nil { + l := Nod(ONAME, nil, nil) + l.Sym = sym + l.Type = Ptrto(Types[TUINT8]) + l.Addable = true + l.Class = PEXTERN + l.Xoffset = 0 + sym.Def = l + ggloblsym(sym, int32(Widthptr), obj.DUPOK|obj.NOPTR) + } + + l := Nod(OADDR, sym.Def, nil) + l.Addable = true + ll = list(ll, l) + + if isdirectiface(n.Left.Type) { + /* For pointer types, we can make a special form of optimization + * + * These statements are put onto the expression init list: + * Itab *tab = atomicloadtype(&cache); + * if(tab == nil) + * tab = typ2Itab(type, itype, &cache); + * + * The CONVIFACE expression is replaced with this: + * OEFACE{tab, ptr}; + */ + l := temp(Ptrto(Types[TUINT8])) + + n1 := Nod(OAS, l, sym.Def) + typecheck(&n1, Etop) + *init = list(*init, n1) + + fn := syslook("typ2Itab", 1) + n1 = Nod(OCALL, fn, nil) + n1.List = ll + typecheck(&n1, Erv) + walkexpr(&n1, init) + + n2 := Nod(OIF, nil, nil) + n2.Ntest = Nod(OEQ, l, nodnil()) + n2.Nbody = list1(Nod(OAS, l, n1)) + n2.Likely = -1 + typecheck(&n2, Etop) + *init = list(*init, n2) + + l = Nod(OEFACE, l, n.Left) + l.Typecheck = n.Typecheck + l.Type = n.Type + n = l + goto ret + } + } + + if Isinter(n.Left.Type) { + ll = list(ll, n.Left) + } else { + // regular types are passed by reference to avoid C vararg calls + // orderexpr arranged for n.Left to be a temporary for all + // the conversions it could see. comparison of an interface + // with a non-interface, especially in a switch on interface value + // with non-interface cases, is not visible to orderstmt, so we + // have to fall back on allocating a temp here. + if islvalue(n.Left) { + ll = list(ll, Nod(OADDR, n.Left, nil)) + } else { + ll = list(ll, Nod(OADDR, copyexpr(n.Left, n.Left.Type, init), nil)) + } + dowidth(n.Left.Type) + r := nodnil() + if n.Esc == EscNone && n.Left.Type.Width <= 1024 { + // Allocate stack buffer for value stored in interface. + r = temp(n.Left.Type) + r = Nod(OAS, r, nil) // zero temp + typecheck(&r, Etop) + *init = list(*init, r) + r = Nod(OADDR, r.Left, nil) + typecheck(&r, Erv) + } + ll = list(ll, r) + } + + if !Isinter(n.Left.Type) { + substArgTypes(fn, n.Left.Type, n.Left.Type, n.Type) + } else { + substArgTypes(fn, n.Left.Type, n.Type) + } + dowidth(fn.Type) + n = Nod(OCALL, fn, nil) + n.List = ll + typecheck(&n, Erv) + walkexpr(&n, init) + goto ret + + case OCONV, OCONVNOP: + if Thearch.Thechar == '5' { + if Isfloat[n.Left.Type.Etype] { + if n.Type.Etype == TINT64 { + n = mkcall("float64toint64", n.Type, init, conv(n.Left, Types[TFLOAT64])) + goto ret + } + + if n.Type.Etype == TUINT64 { + n = mkcall("float64touint64", n.Type, init, conv(n.Left, Types[TFLOAT64])) + goto ret + } + } + + if Isfloat[n.Type.Etype] { + if n.Left.Type.Etype == TINT64 { + n = mkcall("int64tofloat64", n.Type, init, conv(n.Left, Types[TINT64])) + goto ret + } + + if n.Left.Type.Etype == TUINT64 { + n = mkcall("uint64tofloat64", n.Type, init, conv(n.Left, Types[TUINT64])) + goto ret + } + } + } + + walkexpr(&n.Left, init) + goto ret + + case OANDNOT: + walkexpr(&n.Left, init) + n.Op = OAND + n.Right = Nod(OCOM, n.Right, nil) + typecheck(&n.Right, Erv) + walkexpr(&n.Right, init) + goto ret + + case OMUL: + walkexpr(&n.Left, init) + walkexpr(&n.Right, init) + walkmul(&n, init) + goto ret + + case ODIV, OMOD: + walkexpr(&n.Left, init) + walkexpr(&n.Right, init) + + /* + * rewrite complex div into function call. + */ + et := int(n.Left.Type.Etype) + + if Iscomplex[et] && n.Op == ODIV { + t := n.Type + n = mkcall("complex128div", Types[TCOMPLEX128], init, conv(n.Left, Types[TCOMPLEX128]), conv(n.Right, Types[TCOMPLEX128])) + n = conv(n, t) + goto ret + } + + // Nothing to do for float divisions. + if Isfloat[et] { + goto ret + } + + // Try rewriting as shifts or magic multiplies. + walkdiv(&n, init) + + /* + * rewrite 64-bit div and mod into function calls + * on 32-bit architectures. + */ + switch n.Op { + case OMOD, ODIV: + if Widthreg >= 8 || (et != TUINT64 && et != TINT64) { + goto ret + } + var fn string + if et == TINT64 { + fn = "int64" + } else { + fn = "uint64" + } + if n.Op == ODIV { + fn += "div" + } else { + fn += "mod" + } + n = mkcall(fn, n.Type, init, conv(n.Left, Types[et]), conv(n.Right, Types[et])) + + default: + break + } + + goto ret + + case OINDEX: + walkexpr(&n.Left, init) + + // save the original node for bounds checking elision. + // If it was a ODIV/OMOD walk might rewrite it. + r := n.Right + + walkexpr(&n.Right, init) + + // if range of type cannot exceed static array bound, + // disable bounds check. + if n.Bounded { + goto ret + } + t := n.Left.Type + if t != nil && Isptr[t.Etype] { + t = t.Type + } + if Isfixedarray(t) { + n.Bounded = bounded(r, t.Bound) + if Debug['m'] != 0 && n.Bounded && !Isconst(n.Right, CTINT) { + Warn("index bounds check elided") + } + if Smallintconst(n.Right) && !n.Bounded { + Yyerror("index out of bounds") + } + } else if Isconst(n.Left, CTSTR) { + 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") + } + if Smallintconst(n.Right) { + if !n.Bounded { + Yyerror("index out of bounds") + } else { + // replace "abc"[1] with 'b'. + // delayed until now because "abc"[1] is not + // an ideal constant. + v := Mpgetfix(n.Right.Val.U.(*Mpint)) + + 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.(*Mpint), &mpzero) < 0 || Mpcmpfixfix(n.Right.Val.U.(*Mpint), Maxintval[TINT]) > 0 { + Yyerror("index out of bounds") + } + } + goto ret + + case OINDEXMAP: + if n.Etype == 1 { + goto ret + } + walkexpr(&n.Left, init) + walkexpr(&n.Right, init) + + t := n.Left.Type + p := "" + if t.Type.Width <= 128 { // Check ../../runtime/hashmap.go:maxValueSize before changing. + switch Simsimtype(t.Down) { + case TINT32, TUINT32: + p = "mapaccess1_fast32" + + case TINT64, TUINT64: + p = "mapaccess1_fast64" + + case TSTRING: + p = "mapaccess1_faststr" + } + } + + var key *Node + if p != "" { + // fast versions take key by value + key = n.Right + } else { + // standard version takes key by reference. + // orderexpr made sure key is addressable. + key = Nod(OADDR, n.Right, nil) + + p = "mapaccess1" + } + + n = mkcall1(mapfn(p, t), Ptrto(t.Type), init, typename(t), n.Left, key) + n = Nod(OIND, n, nil) + n.Type = t.Type + n.Typecheck = 1 + + // mapaccess needs a zero value to be at least this big. + if zerosize < t.Type.Width { + zerosize = t.Type.Width + } + goto ret + + case ORECV: + Fatal("walkexpr ORECV") // should see inside OAS only + + case OSLICE, OSLICEARR, OSLICESTR: + walkexpr(&n.Left, init) + walkexpr(&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 = reduceSlice(n) + goto ret + + case OSLICE3, OSLICE3ARR: + 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 + } + goto ret + + case OADDR: + walkexpr(&n.Left, init) + goto ret + + case ONEW: + if n.Esc == EscNone && n.Type.Type.Width < 1<<16 { + r := temp(n.Type.Type) + r = Nod(OAS, r, nil) // zero temp + typecheck(&r, Etop) + *init = list(*init, r) + r = Nod(OADDR, r.Left, nil) + typecheck(&r, Erv) + n = r + } else { + n = callnew(n.Type.Type) + } + + goto ret + + // If one argument to the comparison is an empty string, + // 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.(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) + r.Type = n.Type + n = r + goto ret + } + + // s + "badgerbadgerbadger" == "badgerbadgerbadger" + if (n.Etype == OEQ || n.Etype == ONE) && Isconst(n.Right, CTSTR) && n.Left.Op == OADDSTR && count(n.Left.List) == 2 && Isconst(n.Left.List.Next.N, CTSTR) && cmpslit(n.Right, n.Left.List.Next.N) == 0 { + r := Nod(int(n.Etype), Nod(OLEN, n.Left.List.N, nil), Nodintconst(0)) + typecheck(&r, Erv) + walkexpr(&r, init) + r.Type = n.Type + n = r + goto ret + } + + var r *Node + if n.Etype == OEQ || n.Etype == ONE { + // prepare for rewrite below + n.Left = cheapexpr(n.Left, init) + + n.Right = cheapexpr(n.Right, init) + + r = mkcall("eqstring", Types[TBOOL], init, conv(n.Left, Types[TSTRING]), conv(n.Right, Types[TSTRING])) + + // quick check of len before full compare for == or != + // eqstring assumes that the lengths are equal + if n.Etype == OEQ { + // len(left) == len(right) && eqstring(left, right) + r = Nod(OANDAND, Nod(OEQ, Nod(OLEN, n.Left, nil), Nod(OLEN, n.Right, nil)), r) + } else { + // len(left) != len(right) || !eqstring(left, right) + r = Nod(ONOT, r, nil) + + r = Nod(OOROR, Nod(ONE, Nod(OLEN, n.Left, nil), Nod(OLEN, n.Right, nil)), r) + } + + typecheck(&r, Erv) + walkexpr(&r, nil) + } else { + // sys_cmpstring(s1, s2) :: 0 + r = mkcall("cmpstring", Types[TINT], init, conv(n.Left, Types[TSTRING]), conv(n.Right, Types[TSTRING])) + + r = Nod(int(n.Etype), r, Nodintconst(0)) + } + + typecheck(&r, Erv) + if n.Type.Etype != TBOOL { + Fatal("cmp %v", n.Type) + } + r.Type = n.Type + n = r + goto ret + + case OADDSTR: + n = addstr(n, init) + goto ret + + case OAPPEND: + // 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) + goto ret + + // cannot use chanfn - closechan takes any, not chan any + case OCLOSE: + fn := syslook("closechan", 1) + + substArgTypes(fn, n.Left.Type) + n = mkcall1(fn, nil, init, n.Left) + goto ret + + case OMAKECHAN: + n = mkcall1(chanfn("makechan", 1, n.Type), n.Type, init, typename(n.Type), conv(n.Left, Types[TINT64])) + goto ret + + case OMAKEMAP: + t := n.Type + + fn := syslook("makemap", 1) + + a := nodnil() // hmap buffer + r := nodnil() // bucket buffer + if n.Esc == EscNone { + // Allocate hmap buffer on stack. + var_ := temp(hmap(t)) + + a = Nod(OAS, var_, nil) // zero temp + typecheck(&a, Etop) + *init = list(*init, a) + a = Nod(OADDR, var_, nil) + + // Allocate one bucket on stack. + // Maximum key/value size is 128 bytes, larger objects + // are stored with an indirection. So max bucket size is 2048+eps. + var_ = temp(mapbucket(t)) + + r = Nod(OAS, var_, nil) // zero temp + typecheck(&r, Etop) + *init = list(*init, r) + r = Nod(OADDR, var_, nil) + } + + substArgTypes(fn, hmap(t), mapbucket(t), t.Down, t.Type) + n = mkcall1(fn, n.Type, init, typename(n.Type), conv(n.Left, Types[TINT64]), a, r) + goto ret + + case OMAKESLICE: + l := n.Left + r := n.Right + if r == nil { + r = safeexpr(l, init) + 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) { + // var arr [r]T + // n = arr[:l] + t = aindex(r, t.Type) // [r]T + var_ := temp(t) + a := Nod(OAS, var_, nil) // zero temp + typecheck(&a, Etop) + *init = list(*init, a) + r := Nod(OSLICE, var_, Nod(OKEY, nil, l)) // arr[:l] + r = conv(r, n.Type) // in case n.Type is named. + typecheck(&r, Erv) + walkexpr(&r, init) + n = r + } else { + // makeslice(t *Type, nel int64, max int64) (ary []any) + fn := syslook("makeslice", 1) + + substArgTypes(fn, t.Type) // any-1 + n = mkcall1(fn, n.Type, init, typename(n.Type), conv(l, Types[TINT64]), conv(r, Types[TINT64])) + } + + goto ret + + case ORUNESTR: + a := nodnil() + if n.Esc == EscNone { + t := aindex(Nodintconst(4), Types[TUINT8]) + var_ := temp(t) + a = Nod(OADDR, var_, nil) + } + + // intstring(*[4]byte, rune) + n = mkcall("intstring", n.Type, init, a, conv(n.Left, Types[TINT64])) + + goto ret + + case OARRAYBYTESTR: + a := nodnil() + if n.Esc == EscNone { + // Create temporary buffer for string on stack. + t := aindex(Nodintconst(tmpstringbufsize), Types[TUINT8]) + + a = Nod(OADDR, temp(t), nil) + } + + // slicebytetostring(*[32]byte, []byte) string; + n = mkcall("slicebytetostring", n.Type, init, a, n.Left) + + goto ret + + // slicebytetostringtmp([]byte) string; + case OARRAYBYTESTRTMP: + n = mkcall("slicebytetostringtmp", n.Type, init, n.Left) + + goto ret + + // slicerunetostring(*[32]byte, []rune) string; + case OARRAYRUNESTR: + a := nodnil() + + if n.Esc == EscNone { + // Create temporary buffer for string on stack. + t := aindex(Nodintconst(tmpstringbufsize), Types[TUINT8]) + + a = Nod(OADDR, temp(t), nil) + } + + n = mkcall("slicerunetostring", n.Type, init, a, n.Left) + goto ret + + // stringtoslicebyte(*32[byte], string) []byte; + case OSTRARRAYBYTE: + a := nodnil() + + if n.Esc == EscNone { + // Create temporary buffer for slice on stack. + t := aindex(Nodintconst(tmpstringbufsize), Types[TUINT8]) + + a = Nod(OADDR, temp(t), nil) + } + + n = mkcall("stringtoslicebyte", n.Type, init, a, conv(n.Left, Types[TSTRING])) + goto ret + + // stringtoslicebytetmp(string) []byte; + case OSTRARRAYBYTETMP: + n = mkcall("stringtoslicebytetmp", n.Type, init, conv(n.Left, Types[TSTRING])) + + goto ret + + // stringtoslicerune(*[32]rune, string) []rune + case OSTRARRAYRUNE: + a := nodnil() + + if n.Esc == EscNone { + // Create temporary buffer for slice on stack. + t := aindex(Nodintconst(tmpstringbufsize), Types[TINT32]) + + a = Nod(OADDR, temp(t), nil) + } + + n = mkcall("stringtoslicerune", n.Type, init, a, n.Left) + goto ret + + // ifaceeq(i1 any-1, i2 any-2) (ret bool); + case OCMPIFACE: + if !Eqtype(n.Left.Type, n.Right.Type) { + Fatal("ifaceeq %v %v %v", Oconv(int(n.Op), 0), n.Left.Type, n.Right.Type) + } + var fn *Node + if isnilinter(n.Left.Type) { + fn = syslook("efaceeq", 1) + } else { + fn = syslook("ifaceeq", 1) + } + + n.Right = cheapexpr(n.Right, init) + n.Left = cheapexpr(n.Left, init) + substArgTypes(fn, n.Right.Type, n.Left.Type) + r := mkcall1(fn, n.Type, init, n.Left, n.Right) + if n.Etype == ONE { + r = Nod(ONOT, r, nil) + } + + // check itable/type before full compare. + if n.Etype == OEQ { + r = Nod(OANDAND, Nod(OEQ, Nod(OITAB, n.Left, nil), Nod(OITAB, n.Right, nil)), r) + } else { + r = Nod(OOROR, Nod(ONE, Nod(OITAB, n.Left, nil), Nod(OITAB, n.Right, nil)), r) + } + typecheck(&r, Erv) + walkexpr(&r, init) + r.Type = n.Type + n = r + goto ret + + case OARRAYLIT, OMAPLIT, OSTRUCTLIT, OPTRLIT: + var_ := temp(n.Type) + anylit(0, n, var_, init) + n = var_ + goto ret + + case OSEND: + n1 := n.Right + n1 = assignconv(n1, n.Left.Type.Type, "chan send") + walkexpr(&n1, init) + n1 = Nod(OADDR, n1, nil) + n = mkcall1(chanfn("chansend1", 2, n.Left.Type), nil, init, typename(n.Left.Type), n.Left, n1) + goto ret + + case OCLOSURE: + n = walkclosure(n, init) + goto ret + + case OCALLPART: + n = walkpartialcall(n, init) + goto ret + } + + Fatal("missing switch %v", Oconv(int(n.Op), 0)) + + // Expressions that are constant at run time but not + // considered const by the language spec are not turned into + // constants until walk. For example, if n is y%1 == 0, the + // walk of y%1 may have replaced it by 0. + // Check whether n with its updated args is itself now a constant. +ret: + t := n.Type + + evconst(n) + n.Type = t + if n.Op == OLITERAL { + typecheck(&n, Erv) + } + + ullmancalc(n) + + if Debug['w'] != 0 && n != nil { + Dump("walk", n) + } + + lineno = lno + *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. + n := Nod(OAS, l, r) + + if l.Op == OINDEXMAP { + return n + } + + return convas(n, init) +} + +func ascompatee(op int, nl *NodeList, nr *NodeList, init **NodeList) *NodeList { + /* + * check assign expression list to + * a expression list. called in + * expr-list = expr-list + */ + + // ensure order of evaluation for function calls + for ll := nl; ll != nil; ll = ll.Next { + ll.N = safeexpr(ll.N, init) + } + for lr := nr; lr != nil; lr = lr.Next { + lr.N = safeexpr(lr.N, init) + } + + var nn *NodeList + ll := nl + lr := nr + for ; ll != nil && lr != nil; ll, lr = ll.Next, lr.Next { + // Do not generate 'x = x' during return. See issue 4014. + if op == ORETURN && ll.N == lr.N { + continue + } + nn = list(nn, ascompatee1(op, ll.N, lr.N, init)) + } + + // cannot happen: caller checked that lists had same length + if ll != nil || lr != nil { + Yyerror("error in shape across %v %v %v / %d %d [%s]", Hconv(nl, obj.FmtSign), Oconv(int(op), 0), Hconv(nr, obj.FmtSign), count(nl), count(nr), Curfn.Nname.Sym.Name) + } + return nn +} + +/* + * l is an lv and rt is the type of an rv + * return 1 if this implies a function call + * evaluating the lv or a function call + * in the conversion of the types + */ +func fncall(l *Node, rt *Type) bool { + if l.Ullman >= UINF || l.Op == OINDEXMAP { + return true + } + var r Node + if needwritebarrier(l, &r) { + return true + } + if Eqtype(l.Type, rt) { + return false + } + return true +} + +func ascompatet(op int, nl *NodeList, nr **Type, fp int, init **NodeList) *NodeList { + var l *Node + var tmp *Node + var a *Node + var ll *NodeList + var saver Iter + + /* + * check assign type list to + * a expression list. called in + * expr-list = func() + */ + r := Structfirst(&saver, nr) + + var nn *NodeList + var mm *NodeList + ucount := 0 + for ll = nl; ll != nil; ll = ll.Next { + if r == nil { + break + } + l = ll.N + if isblank(l) { + r = structnext(&saver) + continue + } + + // any lv that causes a fn call must be + // deferred until all the return arguments + // have been pulled from the output arguments + if fncall(l, r.Type) { + tmp = temp(r.Type) + typecheck(&tmp, Erv) + a = Nod(OAS, l, tmp) + a = convas(a, init) + mm = list(mm, a) + l = tmp + } + + a = Nod(OAS, l, nodarg(r, fp)) + a = convas(a, init) + ullmancalc(a) + if a.Ullman >= UINF { + Dump("ascompatet ucount", a) + ucount++ + } + + nn = list(nn, a) + r = structnext(&saver) + } + + if ll != nil || r != nil { + Yyerror("ascompatet: assignment count mismatch: %d = %d", count(nl), structcount(*nr)) + } + + if ucount != 0 { + Fatal("ascompatet: too many function calls evaluating parameters") + } + return concat(nn, mm) +} + +/* +* package all the arguments that match a ... T parameter into a []T. + */ +func mkdotargslice(lr0 *NodeList, nn *NodeList, l *Type, fp int, init **NodeList, ddd *Node) *NodeList { + esc := uint16(EscUnknown) + if ddd != nil { + esc = ddd.Esc + } + + tslice := typ(TARRAY) + tslice.Type = l.Type.Type + tslice.Bound = -1 + + var n *Node + if count(lr0) == 0 { + n = nodnil() + n.Type = tslice + } else { + n = Nod(OCOMPLIT, nil, typenod(tslice)) + if ddd != nil { + n.Alloc = ddd.Alloc // temporary to use + } + n.List = lr0 + n.Esc = esc + typecheck(&n, Erv) + if n.Type == nil { + Fatal("mkdotargslice: typecheck failed") + } + walkexpr(&n, init) + } + + a := Nod(OAS, nodarg(l, fp), n) + nn = list(nn, convas(a, init)) + return nn +} + +/* + * helpers for shape errors + */ +func dumptypes(nl **Type, what string) string { + var savel Iter + + fmt_ := "" + fmt_ += "\t" + first := 1 + for l := Structfirst(&savel, nl); l != nil; l = structnext(&savel) { + if first != 0 { + first = 0 + } else { + fmt_ += ", " + } + fmt_ += Tconv(l, 0) + } + + if first != 0 { + fmt_ += fmt.Sprintf("[no arguments %s]", what) + } + return fmt_ +} + +func dumpnodetypes(l *NodeList, what string) string { + var r *Node + + fmt_ := "" + fmt_ += "\t" + first := 1 + for ; l != nil; l = l.Next { + r = l.N + if first != 0 { + first = 0 + } else { + fmt_ += ", " + } + fmt_ += Tconv(r.Type, 0) + } + + if first != 0 { + fmt_ += fmt.Sprintf("[no arguments %s]", what) + } + return fmt_ +} + +/* + * check assign expression list to + * a type list. called in + * return expr-list + * func(expr-list) + */ +func ascompatte(op int, call *Node, isddd bool, nl **Type, lr *NodeList, fp int, init **NodeList) *NodeList { + var savel Iter + + lr0 := lr + l := Structfirst(&savel, nl) + var r *Node + if lr != nil { + r = lr.N + } + var nn *NodeList + + // f(g()) where g has multiple return values + var a *Node + var l2 string + var ll *Type + var l1 string + if r != nil && lr.Next == nil && r.Type.Etype == TSTRUCT && r.Type.Funarg != 0 { + // optimization - can do block copy + if eqtypenoname(r.Type, *nl) { + a := nodarg(*nl, fp) + r = Nod(OCONVNOP, r, nil) + r.Type = a.Type + nn = list1(convas(Nod(OAS, a, r), init)) + goto ret + } + + // conversions involved. + // copy into temporaries. + var alist *NodeList + + for l := Structfirst(&savel, &r.Type); l != nil; l = structnext(&savel) { + a = temp(l.Type) + alist = list(alist, a) + } + + a = Nod(OAS2, nil, nil) + a.List = alist + a.Rlist = lr + typecheck(&a, Etop) + walkstmt(&a) + *init = list(*init, a) + lr = alist + r = lr.N + l = Structfirst(&savel, nl) + } + +loop: + if l != nil && l.Isddd { + // the ddd parameter must be last + ll = structnext(&savel) + + if ll != nil { + Yyerror("... must be last argument") + } + + // special case -- + // only if we are assigning a single ddd + // argument to a ddd parameter then it is + // passed thru unencapsulated + if r != nil && lr.Next == nil && isddd && Eqtype(l.Type, r.Type) { + a = Nod(OAS, nodarg(l, fp), r) + a = convas(a, init) + nn = list(nn, a) + goto ret + } + + // normal case -- make a slice of all + // remaining arguments and pass it to + // the ddd parameter. + nn = mkdotargslice(lr, nn, l, fp, init, call.Right) + + goto ret + } + + if l == nil || r == nil { + if l != nil || r != nil { + l1 = dumptypes(nl, "expected") + l2 = dumpnodetypes(lr0, "given") + if l != nil { + Yyerror("not enough arguments to %v\n%s\n%s", Oconv(int(op), 0), l1, l2) + } else { + Yyerror("too many arguments to %v\n%s\n%s", Oconv(int(op), 0), l1, l2) + } + } + + goto ret + } + + a = Nod(OAS, nodarg(l, fp), r) + a = convas(a, init) + nn = list(nn, a) + + l = structnext(&savel) + r = nil + lr = lr.Next + if lr != nil { + r = lr.N + } + goto loop + +ret: + for lr = nn; lr != nil; lr = lr.Next { + lr.N.Typecheck = 1 + } + return nn +} + +// generate code for print +func walkprint(nn *Node, init **NodeList) *Node { + var r *Node + var n *Node + var on *Node + var t *Type + var et int + + op := int(nn.Op) + all := nn.List + var calls *NodeList + notfirst := false + + // Hoist all the argument evaluation up before the lock. + walkexprlistcheap(all, init) + + calls = list(calls, mkcall("printlock", nil, init)) + + for l := all; l != nil; l = l.Next { + if notfirst { + calls = list(calls, mkcall("printsp", nil, init)) + } + + notfirst = op == OPRINTN + + n = l.N + if n.Op == OLITERAL { + switch n.Val.Ctype { + case CTRUNE: + defaultlit(&n, runetype) + + case CTINT: + defaultlit(&n, Types[TINT64]) + + case CTFLT: + defaultlit(&n, Types[TFLOAT64]) + } + } + + if n.Op != OLITERAL && n.Type != nil && n.Type.Etype == TIDEAL { + defaultlit(&n, Types[TINT64]) + } + defaultlit(&n, nil) + l.N = n + if n.Type == nil || n.Type.Etype == TFORW { + continue + } + + t = n.Type + et = int(n.Type.Etype) + if Isinter(n.Type) { + if isnilinter(n.Type) { + on = syslook("printeface", 1) + } else { + on = syslook("printiface", 1) + } + substArgTypes(on, n.Type) // any-1 + } else if Isptr[et] || et == TCHAN || et == TMAP || et == TFUNC || et == TUNSAFEPTR { + on = syslook("printpointer", 1) + substArgTypes(on, n.Type) // any-1 + } else if Isslice(n.Type) { + on = syslook("printslice", 1) + substArgTypes(on, n.Type) // any-1 + } else if Isint[et] { + if et == TUINT64 { + if (t.Sym.Pkg == Runtimepkg || compiling_runtime != 0) && t.Sym.Name == "hex" { + on = syslook("printhex", 0) + } else { + on = syslook("printuint", 0) + } + } else { + on = syslook("printint", 0) + } + } else if Isfloat[et] { + on = syslook("printfloat", 0) + } else if Iscomplex[et] { + on = syslook("printcomplex", 0) + } else if et == TBOOL { + on = syslook("printbool", 0) + } else if et == TSTRING { + on = syslook("printstring", 0) + } else { + badtype(OPRINT, n.Type, nil) + continue + } + + t = *getinarg(on.Type) + if t != nil { + t = t.Type + } + if t != nil { + t = t.Type + } + + if !Eqtype(t, n.Type) { + n = Nod(OCONV, n, nil) + n.Type = t + } + + r = Nod(OCALL, on, nil) + r.List = list1(n) + calls = list(calls, r) + } + + if op == OPRINTN { + calls = list(calls, mkcall("printnl", nil, nil)) + } + + calls = list(calls, mkcall("printunlock", nil, init)) + + typechecklist(calls, Etop) + walkexprlist(calls, init) + + r = Nod(OEMPTY, nil, nil) + typecheck(&r, Etop) + walkexpr(&r, init) + r.Ninit = calls + return r +} + +func callnew(t *Type) *Node { + dowidth(t) + fn := syslook("newobject", 1) + substArgTypes(fn, t) + return mkcall1(fn, Ptrto(t), nil, typename(t)) +} + +func isstack(n *Node) bool { + n = outervalue(n) + + // If n is *autotmp and autotmp = &foo, replace n with foo. + // We introduce such temps when initializing struct literals. + if n.Op == OIND && n.Left.Op == ONAME && strings.HasPrefix(n.Left.Sym.Name, "autotmp_") { + defn := n.Left.Defn + if defn != nil && defn.Op == OAS && defn.Right.Op == OADDR { + n = defn.Right.Left + } + } + + switch n.Op { + case OINDREG: + return n.Reg == int16(Thearch.REGSP) + + case ONAME: + switch n.Class { + case PAUTO, PPARAM, PPARAMOUT: + return true + } + } + + return false +} + +func isglobal(n *Node) bool { + n = outervalue(n) + + switch n.Op { + case ONAME: + switch n.Class { + case PEXTERN: + return true + } + } + + return false +} + +// Do we need a write barrier for the assignment l = r? +func needwritebarrier(l *Node, r *Node) bool { + if use_writebarrier == 0 { + return false + } + + if l == nil || isblank(l) { + return false + } + + // No write barrier for write of non-pointers. + dowidth(l.Type) + + if !haspointers(l.Type) { + return false + } + + // No write barrier for write to stack. + if isstack(l) { + return false + } + + // No write barrier for implicit or explicit zeroing. + if r == nil || iszero(r) { + return false + } + + // No write barrier for initialization to constant. + if r.Op == OLITERAL { + return false + } + + // No write barrier for storing static (read-only) data. + if r.Op == ONAME && strings.HasPrefix(r.Sym.Name, "statictmp_") { + return false + } + + // No write barrier for storing address of stack values, + // which are guaranteed only to be written to the stack. + if r.Op == OADDR && isstack(r.Left) { + return false + } + + // No write barrier for storing address of global, which + // is live no matter what. + if r.Op == OADDR && isglobal(r.Left) { + return false + } + + // Otherwise, be conservative and use write barrier. + return true +} + +// TODO(rsc): Perhaps componentgen should run before this. + +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) + } + } + return n +} + +func convas(n *Node, init **NodeList) *Node { + if n.Op != OAS { + Fatal("convas: not OAS %v", Oconv(int(n.Op), 0)) + } + + n.Typecheck = 1 + + var lt *Type + var rt *Type + if n.Left == nil || n.Right == nil { + goto out + } + + lt = n.Left.Type + rt = n.Right.Type + if lt == nil || rt == nil { + goto out + } + + if isblank(n.Left) { + defaultlit(&n.Right, nil) + goto out + } + + if n.Left.Op == OINDEXMAP { + map_ := n.Left.Left + key := n.Left.Right + val := n.Right + walkexpr(&map_, init) + walkexpr(&key, init) + walkexpr(&val, init) + + // orderexpr made sure key and val are addressable. + key = Nod(OADDR, key, nil) + + val = Nod(OADDR, val, nil) + n = mkcall1(mapfn("mapassign1", map_.Type), nil, init, typename(map_.Type), map_, key, val) + goto out + } + + if !Eqtype(lt, rt) { + n.Right = assignconv(n.Right, lt, "assignment") + walkexpr(&n.Right, init) + } + +out: + ullmancalc(n) + return n +} + +/* + * from ascompat[te] + * evaluating actual function arguments. + * f(a,b) + * if there is exactly one function expr, + * then it is done first. otherwise must + * make temp variables + */ +func reorder1(all *NodeList) *NodeList { + var n *Node + + c := 0 // function calls + t := 0 // total parameters + + for l := all; l != nil; l = l.Next { + n = l.N + t++ + ullmancalc(n) + if n.Ullman >= UINF { + c++ + } + } + + if c == 0 || t == 1 { + return all + } + + var g *NodeList // fncalls assigned to tempnames + var f *Node // last fncall assigned to stack + var r *NodeList // non fncalls and tempnames assigned to stack + d := 0 + var a *Node + for l := all; l != nil; l = l.Next { + n = l.N + if n.Ullman < UINF { + r = list(r, n) + continue + } + + d++ + if d == c { + f = n + continue + } + + // make assignment of fncall to tempname + a = temp(n.Right.Type) + + a = Nod(OAS, a, n.Right) + g = list(g, a) + + // put normal arg assignment on list + // with fncall replaced by tempname + n.Right = a.Left + + r = list(r, n) + } + + if f != nil { + g = list(g, f) + } + return concat(g, r) +} + +/* + * from ascompat[ee] + * a,b = c,d + * simultaneous assignment. there cannot + * be later use of an earlier lvalue. + * + * function calls have been removed. + */ +func reorder3(all *NodeList) *NodeList { + var l *Node + + // If a needed expression may be affected by an + // earlier assignment, make an early copy of that + // expression and use the copy instead. + var early *NodeList + + var mapinit *NodeList + for list := all; list != nil; list = list.Next { + l = list.N.Left + + // Save subexpressions needed on left side. + // Drill through non-dereferences. + for { + if l.Op == ODOT || l.Op == OPAREN { + l = l.Left + continue + } + + if l.Op == OINDEX && Isfixedarray(l.Left.Type) { + reorder3save(&l.Right, all, list, &early) + l = l.Left + continue + } + + break + } + + switch l.Op { + default: + Fatal("reorder3 unexpected lvalue %v", Oconv(int(l.Op), obj.FmtSharp)) + + case ONAME: + break + + case OINDEX, OINDEXMAP: + reorder3save(&l.Left, all, list, &early) + reorder3save(&l.Right, all, list, &early) + if l.Op == OINDEXMAP { + list.N = convas(list.N, &mapinit) + } + + case OIND, ODOTPTR: + reorder3save(&l.Left, all, list, &early) + } + + // Save expression on right side. + reorder3save(&list.N.Right, all, list, &early) + } + + early = concat(mapinit, early) + return concat(early, all) +} + +/* + * if the evaluation of *np would be affected by the + * assignments in all up to but not including stop, + * copy into a temporary during *early and + * replace *np with that temp. + */ +func reorder3save(np **Node, all *NodeList, stop *NodeList, early **NodeList) { + n := *np + if !aliased(n, all, stop) { + return + } + + q := temp(n.Type) + q = Nod(OAS, q, n) + typecheck(&q, Etop) + *early = list(*early, q) + *np = q.Left +} + +/* + * what's the outer value that a write to n affects? + * outer value means containing struct or array. + */ +func outervalue(n *Node) *Node { + for { + if n.Op == OXDOT { + Fatal("OXDOT in walk") + } + if n.Op == ODOT || n.Op == OPAREN || n.Op == OCONVNOP { + n = n.Left + continue + } + + if n.Op == OINDEX && Isfixedarray(n.Left.Type) { + n = n.Left + continue + } + + break + } + + return n +} + +/* + * Is it possible that the computation of n might be + * affected by writes in as up to but not including stop? + */ +func aliased(n *Node, all *NodeList, stop *NodeList) bool { + if n == nil { + return false + } + + // Look for obvious aliasing: a variable being assigned + // during the all list and appearing in n. + // Also record whether there are any writes to main memory. + // Also record whether there are any writes to variables + // whose addresses have been taken. + memwrite := 0 + + varwrite := 0 + var a *Node + for l := all; l != stop; l = l.Next { + a = outervalue(l.N.Left) + if a.Op != ONAME { + memwrite = 1 + continue + } + + switch n.Class { + default: + varwrite = 1 + continue + + case PAUTO, PPARAM, PPARAMOUT: + if n.Addrtaken { + varwrite = 1 + continue + } + + if vmatch2(a, n) { + // Direct hit. + return true + } + } + } + + // The variables being written do not appear in n. + // However, n might refer to computed addresses + // that are being written. + + // If no computed addresses are affected by the writes, no aliasing. + if memwrite == 0 && varwrite == 0 { + return false + } + + // If n does not refer to computed addresses + // (that is, if n only refers to variables whose addresses + // have not been taken), no aliasing. + if varexpr(n) { + return false + } + + // Otherwise, both the writes and n refer to computed memory addresses. + // Assume that they might conflict. + return true +} + +/* + * does the evaluation of n only refer to variables + * whose addresses have not been taken? + * (and no other memory) + */ +func varexpr(n *Node) bool { + if n == nil { + return true + } + + switch n.Op { + case OLITERAL: + return true + + case ONAME: + switch n.Class { + case PAUTO, PPARAM, PPARAMOUT: + if !n.Addrtaken { + return true + } + } + + return false + + case OADD, + OSUB, + OOR, + OXOR, + OMUL, + ODIV, + OMOD, + OLSH, + ORSH, + OAND, + OANDNOT, + OPLUS, + OMINUS, + OCOM, + OPAREN, + OANDAND, + OOROR, + ODOT, // but not ODOTPTR + OCONV, + OCONVNOP, + OCONVIFACE, + ODOTTYPE: + return varexpr(n.Left) && varexpr(n.Right) + } + + // Be conservative. + return false +} + +/* + * is the name l mentioned in r? + */ +func vmatch2(l *Node, r *Node) bool { + if r == nil { + return false + } + switch r.Op { + // match each right given left + case ONAME: + return l == r + + case OLITERAL: + return false + } + + if vmatch2(l, r.Left) { + return true + } + if vmatch2(l, r.Right) { + return true + } + for ll := r.List; ll != nil; ll = ll.Next { + if vmatch2(l, ll.N) { + return true + } + } + return false +} + +/* + * is any name mentioned in l also mentioned in r? + * called by sinit.go + */ +func vmatch1(l *Node, r *Node) bool { + /* + * isolate all left sides + */ + if l == nil || r == nil { + return false + } + switch l.Op { + case ONAME: + switch l.Class { + case PPARAM, PPARAMREF, PAUTO: + break + + // assignment to non-stack variable + // must be delayed if right has function calls. + default: + if r.Ullman >= UINF { + return true + } + } + + return vmatch2(l, r) + + case OLITERAL: + return false + } + + if vmatch1(l.Left, r) { + return true + } + if vmatch1(l.Right, r) { + return true + } + for ll := l.List; ll != nil; ll = ll.Next { + if vmatch1(ll.N, r) { + return true + } + } + return false +} + +/* + * walk through argin parameters. + * generate and return code to allocate + * copies of escaped parameters to the heap. + */ +func paramstoheap(argin **Type, out int) *NodeList { + var savet Iter + var v *Node + var as *Node + + var nn *NodeList + for t := Structfirst(&savet, argin); t != nil; t = structnext(&savet) { + v = t.Nname + if v != nil && v.Sym != nil && v.Sym.Name[0] == '~' && v.Sym.Name[1] == 'r' { // unnamed result + v = nil + } + + // For precise stacks, the garbage collector assumes results + // are always live, so zero them always. + if out != 0 { + // Defer might stop a panic and show the + // return values as they exist at the time of panic. + // Make sure to zero them on entry to the function. + nn = list(nn, Nod(OAS, nodarg(t, 1), nil)) + } + + if v == nil || v.Class&PHEAP == 0 { + continue + } + + // generate allocation & copying code + if compiling_runtime != 0 { + Yyerror("%v escapes to heap, not allowed in runtime.", v) + } + if v.Alloc == nil { + v.Alloc = callnew(v.Type) + } + 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 + typecheck(&as, Etop) + as = applywritebarrier(as, &nn) + nn = list(nn, as) + } + } + + return nn +} + +/* + * walk through argout parameters copying back to stack + */ +func returnsfromheap(argin **Type) *NodeList { + var savet Iter + var v *Node + + var nn *NodeList + for t := Structfirst(&savet, argin); t != nil; t = structnext(&savet) { + v = t.Nname + if v == nil || v.Class != PHEAP|PPARAMOUT { + continue + } + nn = list(nn, Nod(OAS, v.Stackparam, v)) + } + + return nn +} + +/* + * take care of migrating any function in/out args + * between the stack and the heap. adds code to + * curfn's before and after lists. + */ +func heapmoves() { + lno := lineno + lineno = Curfn.Lineno + nn := paramstoheap(getthis(Curfn.Type), 0) + nn = concat(nn, paramstoheap(getinarg(Curfn.Type), 0)) + nn = concat(nn, paramstoheap(Getoutarg(Curfn.Type), 1)) + Curfn.Func.Enter = concat(Curfn.Func.Enter, nn) + lineno = Curfn.Func.Endlineno + Curfn.Func.Exit = returnsfromheap(Getoutarg(Curfn.Type)) + lineno = lno +} + +func vmkcall(fn *Node, t *Type, init **NodeList, va []*Node) *Node { + if fn.Type == nil || fn.Type.Etype != TFUNC { + Fatal("mkcall %v %v", fn, fn.Type) + } + + var args *NodeList + n := fn.Type.Intuple + for i := 0; i < n; i++ { + args = list(args, va[i]) + } + + r := Nod(OCALL, fn, nil) + r.List = args + if fn.Type.Outtuple > 0 { + typecheck(&r, Erv|Efnstruct) + } else { + typecheck(&r, Etop) + } + walkexpr(&r, init) + r.Type = t + return r +} + +func mkcall(name string, t *Type, init **NodeList, args ...*Node) *Node { + return vmkcall(syslook(name, 0), t, init, args) +} + +func mkcall1(fn *Node, t *Type, init **NodeList, args ...*Node) *Node { + return vmkcall(fn, t, init, args) +} + +func conv(n *Node, t *Type) *Node { + if Eqtype(n.Type, t) { + return n + } + n = Nod(OCONV, n, nil) + n.Type = t + typecheck(&n, Erv) + return n +} + +func chanfn(name string, n int, t *Type) *Node { + if t.Etype != TCHAN { + Fatal("chanfn %v", t) + } + fn := syslook(name, 1) + switch n { + default: + Fatal("chanfn %d", n) + case 1: + substArgTypes(fn, t.Type) + case 2: + substArgTypes(fn, t.Type, t.Type) + } + return fn +} + +func mapfn(name string, t *Type) *Node { + if t.Etype != TMAP { + Fatal("mapfn %v", t) + } + fn := syslook(name, 1) + substArgTypes(fn, t.Down, t.Type, t.Down, t.Type) + return fn +} + +func mapfndel(name string, t *Type) *Node { + if t.Etype != TMAP { + Fatal("mapfn %v", t) + } + fn := syslook(name, 1) + substArgTypes(fn, t.Down, t.Type, t.Down) + return fn +} + +func writebarrierfn(name string, l *Type, r *Type) *Node { + fn := syslook(name, 1) + substArgTypes(fn, l, r) + return fn +} + +func addstr(n *Node, init **NodeList) *Node { + // orderexpr rewrote OADDSTR to have a list of strings. + c := count(n.List) + + if c < 2 { + Yyerror("addstr count %d too small", c) + } + + buf := nodnil() + if n.Esc == EscNone { + sz := int64(0) + for l := n.List; l != nil; l = l.Next { + if n.Op == OLITERAL { + sz += int64(len(n.Val.U.(string))) + } + } + + // Don't allocate the buffer if the result won't fit. + if sz < tmpstringbufsize { + // Create temporary buffer for result string on stack. + t := aindex(Nodintconst(tmpstringbufsize), Types[TUINT8]) + + buf = Nod(OADDR, temp(t), nil) + } + } + + // build list of string arguments + args := list1(buf) + + for l := n.List; l != nil; l = l.Next { + args = list(args, conv(l.N, Types[TSTRING])) + } + + var fn string + if c <= 5 { + // small numbers of strings use direct runtime helpers. + // note: orderexpr knows this cutoff too. + fn = fmt.Sprintf("concatstring%d", c) + } else { + // large numbers of strings are passed to the runtime as a slice. + fn = "concatstrings" + + t := typ(TARRAY) + t.Type = Types[TSTRING] + t.Bound = -1 + slice := Nod(OCOMPLIT, nil, typenod(t)) + slice.Alloc = n.Alloc + slice.List = args.Next // skip buf arg + args = list1(buf) + args = list(args, slice) + slice.Esc = EscNone + } + + cat := syslook(fn, 1) + r := Nod(OCALL, cat, nil) + r.List = args + typecheck(&r, Erv) + walkexpr(&r, init) + r.Type = n.Type + + return r +} + +// expand append(l1, l2...) to +// init { +// s := l1 +// if n := len(l1) + len(l2) - cap(s); n > 0 { +// s = growslice(s, n) +// } +// s = s[:len(l1)+len(l2)] +// memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T)) +// } +// s +// +// l2 is allowed to be a string. +func appendslice(n *Node, init **NodeList) *Node { + walkexprlistsafe(n.List, 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 { + l.N = cheapexpr(l.N, init) + } + + l1 := n.List.N + l2 := n.List.Next.N + + s := temp(l1.Type) // var s []T + var l *NodeList + l = list(l, Nod(OAS, s, l1)) // s = l1 + + nt := temp(Types[TINT]) + + nif := Nod(OIF, nil, nil) + + // n := len(s) + len(l2) - cap(s) + nif.Ninit = list1(Nod(OAS, nt, Nod(OSUB, Nod(OADD, Nod(OLEN, s, nil), Nod(OLEN, l2, nil)), Nod(OCAP, s, nil)))) + + nif.Ntest = Nod(OGT, nt, Nodintconst(0)) + + // instantiate growslice(Type*, []any, int) []any + fn := syslook("growslice", 1) // growslice(, old []T, n int64) (ret []T) + substArgTypes(fn, s.Type.Type, s.Type.Type) + + // s = growslice(T, s, n) + nif.Nbody = list1(Nod(OAS, s, mkcall1(fn, s.Type, &nif.Ninit, typename(s.Type), s, nt))) + + l = list(l, nif) + + if haspointers(l1.Type.Type) { + // copy(s[len(l1):len(l1)+len(l2)], l2) + nptr1 := Nod(OSLICE, s, Nod(OKEY, Nod(OLEN, l1, nil), Nod(OADD, Nod(OLEN, l1, nil), Nod(OLEN, l2, nil)))) + + nptr1.Etype = 1 + nptr2 := l2 + fn := syslook("typedslicecopy", 1) + substArgTypes(fn, l1.Type, l2.Type) + nt := mkcall1(fn, Types[TINT], &l, typename(l1.Type.Type), nptr1, nptr2) + l = list(l, nt) + } else if flag_race != 0 { + // rely on runtime to instrument copy. + // copy(s[len(l1):len(l1)+len(l2)], l2) + nptr1 := Nod(OSLICE, s, Nod(OKEY, Nod(OLEN, l1, nil), Nod(OADD, Nod(OLEN, l1, nil), Nod(OLEN, l2, nil)))) + + nptr1.Etype = 1 + nptr2 := l2 + var fn *Node + if l2.Type.Etype == TSTRING { + fn = syslook("slicestringcopy", 1) + } else { + fn = syslook("slicecopy", 1) + } + substArgTypes(fn, l1.Type, l2.Type) + nt := mkcall1(fn, Types[TINT], &l, nptr1, nptr2, Nodintconst(s.Type.Type.Width)) + l = list(l, nt) + } else { + // memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T)) + nptr1 := Nod(OINDEX, s, Nod(OLEN, l1, nil)) + + nptr1.Bounded = true + nptr1 = Nod(OADDR, nptr1, nil) + + nptr2 := Nod(OSPTR, l2, nil) + + fn := syslook("memmove", 1) + substArgTypes(fn, s.Type.Type, s.Type.Type) + + nwid := cheapexpr(conv(Nod(OLEN, l2, nil), Types[TUINTPTR]), &l) + + nwid = Nod(OMUL, nwid, Nodintconst(s.Type.Type.Width)) + nt := mkcall1(fn, nil, &l, nptr1, nptr2, nwid) + l = list(l, nt) + } + + // s = s[:len(l1)+len(l2)] + nt = Nod(OADD, Nod(OLEN, l1, nil), Nod(OLEN, l2, nil)) + + nt = Nod(OSLICE, s, Nod(OKEY, nil, nt)) + nt.Etype = 1 + l = list(l, Nod(OAS, s, nt)) + + typechecklist(l, Etop) + walkstmtlist(l) + *init = concat(*init, l) + return s +} + +// 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 +// const argc = len(args) - 1 +// if cap(s) - len(s) < argc { +// s = growslice(s, argc) +// } +// n := len(s) +// s = s[:n+argc] +// s[n] = a +// s[n+1] = b +// ... +// } +// s +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. + // 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) + } + + nsrc := n.List.N + + // Resolve slice type of multi-valued return. + if Istype(nsrc.Type, TSTRUCT) { + nsrc.Type = nsrc.Type.Type.Type + } + argc := count(n.List) - 1 + if argc < 1 { + 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) + l = list(l, Nod(OAS, ns, nsrc)) // s = src + + na := Nodintconst(int64(argc)) // const argc + nx := Nod(OIF, nil, nil) // if cap(s) - len(s) < argc + nx.Ntest = Nod(OLT, Nod(OSUB, Nod(OCAP, ns, nil), Nod(OLEN, ns, nil)), na) + + fn := syslook("growslice", 1) // growslice(, old []T, n int) (ret []T) + substArgTypes(fn, ns.Type.Type, ns.Type.Type) + + nx.Nbody = list1(Nod(OAS, ns, mkcall1(fn, ns.Type, &nx.Ninit, typename(ns.Type), ns, na))) + + l = list(l, nx) + + nn := temp(Types[TINT]) + l = list(l, Nod(OAS, nn, Nod(OLEN, ns, nil))) // n = len(s) + + nx = Nod(OSLICE, ns, Nod(OKEY, nil, Nod(OADD, nn, na))) // ...s[:n+argc] + nx.Etype = 1 + l = list(l, Nod(OAS, ns, nx)) // s = s[:n+argc] + + for a := n.List.Next; a != nil; a = a.Next { + nx = Nod(OINDEX, ns, nn) // s[n] ... + nx.Bounded = true + l = list(l, Nod(OAS, nx, a.N)) // s[n] = arg + if a.Next != nil { + l = list(l, Nod(OAS, nn, Nod(OADD, nn, Nodintconst(1)))) // n = n + 1 + } + } + + typechecklist(l, Etop) + walkstmtlist(l) + *init = concat(*init, l) + return ns +} + +// Lower copy(a, b) to a memmove call or a runtime call. +// +// init { +// n := len(a) +// if n > len(b) { n = len(b) } +// memmove(a.ptr, b.ptr, n*sizeof(elem(a))) +// } +// n; +// +// Also works if b is a string. +// +func copyany(n *Node, init **NodeList, runtimecall int) *Node { + if haspointers(n.Left.Type.Type) { + fn := writebarrierfn("typedslicecopy", n.Left.Type, n.Right.Type) + return mkcall1(fn, n.Type, init, typename(n.Left.Type.Type), n.Left, n.Right) + } + + if runtimecall != 0 { + var fn *Node + if n.Right.Type.Etype == TSTRING { + fn = syslook("slicestringcopy", 1) + } else { + fn = syslook("slicecopy", 1) + } + substArgTypes(fn, n.Left.Type, n.Right.Type) + return mkcall1(fn, n.Type, init, n.Left, n.Right, Nodintconst(n.Left.Type.Type.Width)) + } + + walkexpr(&n.Left, init) + walkexpr(&n.Right, init) + nl := temp(n.Left.Type) + nr := temp(n.Right.Type) + var l *NodeList + l = list(l, Nod(OAS, nl, n.Left)) + l = list(l, Nod(OAS, nr, n.Right)) + + nfrm := Nod(OSPTR, nr, nil) + nto := Nod(OSPTR, nl, nil) + + nlen := temp(Types[TINT]) + + // n = len(to) + l = list(l, Nod(OAS, nlen, Nod(OLEN, nl, nil))) + + // if n > len(frm) { n = len(frm) } + nif := Nod(OIF, nil, nil) + + nif.Ntest = Nod(OGT, nlen, Nod(OLEN, nr, nil)) + nif.Nbody = list(nif.Nbody, Nod(OAS, nlen, Nod(OLEN, nr, nil))) + l = list(l, nif) + + // Call memmove. + fn := syslook("memmove", 1) + + substArgTypes(fn, nl.Type.Type, nl.Type.Type) + nwid := temp(Types[TUINTPTR]) + l = list(l, Nod(OAS, nwid, conv(nlen, Types[TUINTPTR]))) + nwid = Nod(OMUL, nwid, Nodintconst(nl.Type.Type.Width)) + l = list(l, mkcall1(fn, nil, init, nto, nfrm, nwid)) + + typechecklist(l, Etop) + walkstmtlist(l) + *init = concat(*init, l) + return nlen +} + +func eqfor(t *Type, needsize *int) *Node { + // Should only arrive here with large memory or + // a struct/array containing a non-memory field/element. + // Small memory is handled inline, and single non-memory + // is handled during type check (OCMPSTR etc). + a := algtype1(t, nil) + + if a != AMEM && a != -1 { + Fatal("eqfor %v", t) + } + + if a == AMEM { + n := syslook("memequal", 1) + substArgTypes(n, t, t) + *needsize = 1 + return n + } + + sym := typesymprefix(".eq", t) + n := newname(sym) + n.Class = PFUNC + ntype := Nod(OTFUNC, nil, nil) + ntype.List = list(ntype.List, Nod(ODCLFIELD, nil, typenod(Ptrto(t)))) + ntype.List = list(ntype.List, Nod(ODCLFIELD, nil, typenod(Ptrto(t)))) + ntype.Rlist = list(ntype.Rlist, Nod(ODCLFIELD, nil, typenod(Types[TBOOL]))) + typecheck(&ntype, Etype) + n.Type = ntype.Type + *needsize = 0 + return n +} + +func countfield(t *Type) int { + n := 0 + for t1 := t.Type; t1 != nil; t1 = t1.Down { + n++ + } + return n +} + +func walkcompare(np **Node, init **NodeList) { + n := *np + + // Given interface value l and concrete value r, rewrite + // l == r + // to + // x, ok := l.(type(r)); ok && x == r + // Handle != similarly. + // This avoids the allocation that would be required + // to convert r to l for comparison. + var l *Node + + var r *Node + if Isinter(n.Left.Type) && !Isinter(n.Right.Type) { + l = n.Left + r = n.Right + } else if !Isinter(n.Left.Type) && Isinter(n.Right.Type) { + l = n.Right + r = n.Left + } + + if l != nil { + x := temp(r.Type) + ok := temp(Types[TBOOL]) + + // l.(type(r)) + a := Nod(ODOTTYPE, l, nil) + + a.Type = r.Type + + // x, ok := l.(type(r)) + expr := Nod(OAS2, nil, nil) + + expr.List = list1(x) + expr.List = list(expr.List, ok) + expr.Rlist = list1(a) + typecheck(&expr, Etop) + walkexpr(&expr, init) + + if n.Op == OEQ { + r = Nod(OANDAND, ok, Nod(OEQ, x, r)) + } else { + r = Nod(OOROR, Nod(ONOT, ok, nil), Nod(ONE, x, r)) + } + *init = list(*init, expr) + finishcompare(np, n, r, init) + return + } + + // Must be comparison of array or struct. + // Otherwise back end handles it. + t := n.Left.Type + + switch t.Etype { + default: + return + + case TARRAY: + if Isslice(t) { + return + } + + case TSTRUCT: + break + } + + cmpl := n.Left + for cmpl != nil && cmpl.Op == OCONVNOP { + cmpl = cmpl.Left + } + cmpr := n.Right + for cmpr != nil && cmpr.Op == OCONVNOP { + cmpr = cmpr.Left + } + + if !islvalue(cmpl) || !islvalue(cmpr) { + Fatal("arguments of comparison must be lvalues - %v %v", cmpl, cmpr) + } + + l = temp(Ptrto(t)) + a := Nod(OAS, l, Nod(OADDR, cmpl, nil)) + a.Right.Etype = 1 // addr does not escape + typecheck(&a, Etop) + *init = list(*init, a) + + r = temp(Ptrto(t)) + a = Nod(OAS, r, Nod(OADDR, cmpr, nil)) + a.Right.Etype = 1 // addr does not escape + typecheck(&a, Etop) + *init = list(*init, a) + + andor := OANDAND + if n.Op == ONE { + andor = OOROR + } + + var expr *Node + if t.Etype == TARRAY && t.Bound <= 4 && issimple[t.Type.Etype] { + // Four or fewer elements of a basic type. + // Unroll comparisons. + var li *Node + var ri *Node + for i := 0; int64(i) < t.Bound; i++ { + li = Nod(OINDEX, l, Nodintconst(int64(i))) + ri = Nod(OINDEX, r, Nodintconst(int64(i))) + a = Nod(int(n.Op), li, ri) + if expr == nil { + expr = a + } else { + expr = Nod(andor, expr, a) + } + } + + if expr == nil { + expr = Nodbool(n.Op == OEQ) + } + finishcompare(np, n, expr, init) + return + } + + if t.Etype == TSTRUCT && countfield(t) <= 4 { + // Struct of four or fewer fields. + // Inline comparisons. + var li *Node + var ri *Node + for t1 := t.Type; t1 != nil; t1 = t1.Down { + if isblanksym(t1.Sym) { + continue + } + li = Nod(OXDOT, l, newname(t1.Sym)) + ri = Nod(OXDOT, r, newname(t1.Sym)) + a = Nod(int(n.Op), li, ri) + if expr == nil { + expr = a + } else { + expr = Nod(andor, expr, a) + } + } + + if expr == nil { + expr = Nodbool(n.Op == OEQ) + } + finishcompare(np, n, expr, init) + return + } + + // Chose not to inline. Call equality function directly. + var needsize int + call := Nod(OCALL, eqfor(t, &needsize), nil) + + call.List = list(call.List, l) + call.List = list(call.List, r) + if needsize != 0 { + call.List = list(call.List, Nodintconst(t.Width)) + } + r = call + if n.Op != OEQ { + r = Nod(ONOT, r, nil) + } + + finishcompare(np, n, r, init) + return +} + +func finishcompare(np **Node, n, r *Node, init **NodeList) { + // Using np here to avoid passing &r to typecheck. + *np = r + typecheck(np, Erv) + walkexpr(np, init) + r = *np + if r.Type != n.Type { + r = Nod(OCONVNOP, r, nil) + r.Type = n.Type + r.Typecheck = 1 + *np = r + } +} + +func samecheap(a *Node, b *Node) bool { + var ar *Node + var br *Node + for a != nil && b != nil && a.Op == b.Op { + switch a.Op { + default: + return false + + case ONAME: + return a == b + + case ODOT, ODOTPTR: + ar = a.Right + br = b.Right + if ar.Op != ONAME || br.Op != ONAME || ar.Sym != br.Sym { + return false + } + + case OINDEX: + ar = a.Right + br = b.Right + if !Isconst(ar, CTINT) || !Isconst(br, CTINT) || Mpcmpfixfix(ar.Val.U.(*Mpint), br.Val.U.(*Mpint)) != 0 { + return false + } + } + + a = a.Left + b = b.Left + } + + return false +} + +func walkrotate(np **Node) { + if Thearch.Thechar == '7' || Thearch.Thechar == '9' { + return + } + + n := *np + + // Want << | >> or >> | << or << ^ >> or >> ^ << on unsigned value. + l := n.Left + + r := n.Right + if (n.Op != OOR && n.Op != OXOR) || (l.Op != OLSH && l.Op != ORSH) || (r.Op != OLSH && r.Op != ORSH) || n.Type == nil || Issigned[n.Type.Etype] || l.Op == r.Op { + return + } + + // Want same, side effect-free expression on lhs of both shifts. + if !samecheap(l.Left, r.Left) { + return + } + + // Constants adding to width? + w := int(l.Type.Width * 8) + + if Smallintconst(l.Right) && Smallintconst(r.Right) { + sl := int(Mpgetfix(l.Right.Val.U.(*Mpint))) + if sl >= 0 { + 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 { + n = l + } else { + n = r + } + n.Op = OLROT + + // Remove rotate 0 and rotate w. + s := int(Mpgetfix(n.Right.Val.U.(*Mpint))) + + if s == 0 || s == w { + n = n.Left + } + + *np = n + return + } + } + return + } + + // TODO: Could allow s and 32-s if s is bounded (maybe s&31 and 32-s&31). + return +} + +/* + * walkmul rewrites integer multiplication by powers of two as shifts. + */ +func walkmul(np **Node, init **NodeList) { + n := *np + if !Isint[n.Type.Etype] { + return + } + + var nr *Node + var nl *Node + if n.Right.Op == OLITERAL { + nl = n.Left + nr = n.Right + } else if n.Left.Op == OLITERAL { + nl = n.Right + nr = n.Left + } else { + return + } + + neg := 0 + + // x*0 is 0 (and side effects of x). + var pow int + var w int + if Mpgetfix(nr.Val.U.(*Mpint)) == 0 { + cheapexpr(nl, init) + Nodconst(n, n.Type, 0) + goto ret + } + + // nr is a constant. + pow = powtwo(nr) + + if pow < 0 { + return + } + if pow >= 1000 { + // negative power of 2, like -16 + neg = 1 + + pow -= 1000 + } + + w = int(nl.Type.Width * 8) + if pow+1 >= w { // too big, shouldn't happen + return + } + + nl = cheapexpr(nl, init) + + if pow == 0 { + // x*1 is x + n = nl + + goto ret + } + + n = Nod(OLSH, nl, Nodintconst(int64(pow))) + +ret: + if neg != 0 { + n = Nod(OMINUS, n, nil) + } + + typecheck(&n, Erv) + walkexpr(&n, init) + *np = n +} + +/* + * walkdiv rewrites division by a constant as less expensive + * operations. + */ +func walkdiv(np **Node, init **NodeList) { + // if >= 0, nr is 1<= 0, nr is 1<= 1000 { + // negative power of 2 + s = 1 + + pow -= 1000 + } + + if pow+1 >= w { + // divisor too large. + return + } + + if pow < 0 { + // try to do division by multiply by (2^w)/d + // see hacker's delight chapter 10 + // TODO: support 64-bit magic multiply here. + var m Magic + m.W = w + + if Issigned[nl.Type.Etype] { + m.Sd = Mpgetfix(nr.Val.U.(*Mpint)) + Smagic(&m) + } else { + m.Ud = uint64(Mpgetfix(nr.Val.U.(*Mpint))) + Umagic(&m) + } + + if m.Bad != 0 { + return + } + + // We have a quick division method so use it + // for modulo too. + if n.Op == OMOD { + // rewrite as A%B = A - (A/B*B). + n1 := Nod(ODIV, nl, nr) + + n2 := Nod(OMUL, n1, nr) + n = Nod(OSUB, nl, n2) + goto ret + } + + switch Simtype[nl.Type.Etype] { + default: + return + + // n1 = nl * magic >> w (HMUL) + case TUINT8, TUINT16, TUINT32: + nc := Nod(OXXX, nil, nil) + + Nodconst(nc, nl.Type, int64(m.Um)) + n1 := Nod(OMUL, nl, nc) + typecheck(&n1, Erv) + n1.Op = OHMUL + if m.Ua != 0 { + // Select a Go type with (at least) twice the width. + var twide *Type + switch Simtype[nl.Type.Etype] { + default: + return + + case TUINT8, TUINT16: + twide = Types[TUINT32] + + case TUINT32: + twide = Types[TUINT64] + + case TINT8, TINT16: + twide = Types[TINT32] + + case TINT32: + twide = Types[TINT64] + } + + // add numerator (might overflow). + // n2 = (n1 + nl) + n2 := Nod(OADD, conv(n1, twide), conv(nl, twide)) + + // shift by m.s + nc := Nod(OXXX, nil, nil) + + Nodconst(nc, Types[TUINT], int64(m.S)) + n = conv(Nod(ORSH, n2, nc), nl.Type) + } else { + // n = n1 >> m.s + nc := Nod(OXXX, nil, nil) + + Nodconst(nc, Types[TUINT], int64(m.S)) + n = Nod(ORSH, n1, nc) + } + + // n1 = nl * magic >> w + case TINT8, TINT16, TINT32: + nc := Nod(OXXX, nil, nil) + + Nodconst(nc, nl.Type, m.Sm) + n1 := Nod(OMUL, nl, nc) + typecheck(&n1, Erv) + n1.Op = OHMUL + if m.Sm < 0 { + // add the numerator. + n1 = Nod(OADD, n1, nl) + } + + // shift by m.s + nc = Nod(OXXX, nil, nil) + + Nodconst(nc, Types[TUINT], int64(m.S)) + n2 := conv(Nod(ORSH, n1, nc), nl.Type) + + // add 1 iff n1 is negative. + nc = Nod(OXXX, nil, nil) + + Nodconst(nc, Types[TUINT], int64(w)-1) + n3 := Nod(ORSH, nl, nc) // n4 = -1 iff n1 is negative. + n = Nod(OSUB, n2, n3) + + // apply sign. + if m.Sd < 0 { + n = Nod(OMINUS, n, nil) + } + } + + goto ret + } + + switch pow { + case 0: + if n.Op == OMOD { + // nl % 1 is zero. + Nodconst(n, n.Type, 0) + } else if s != 0 { + // divide by -1 + n.Op = OMINUS + + n.Right = nil + } else { + // divide by 1 + n = nl + } + + default: + if Issigned[n.Type.Etype] { + if n.Op == OMOD { + // signed modulo 2^pow is like ANDing + // with the last pow bits, but if nl < 0, + // nl & (2^pow-1) is (nl+1)%2^pow - 1. + nc := Nod(OXXX, nil, nil) + + Nodconst(nc, Types[Simtype[TUINT]], int64(w)-1) + n1 := Nod(ORSH, nl, nc) // n1 = -1 iff nl < 0. + if pow == 1 { + typecheck(&n1, Erv) + n1 = cheapexpr(n1, init) + + // n = (nl+ε)&1 -ε where ε=1 iff nl<0. + n2 := Nod(OSUB, nl, n1) + + nc := Nod(OXXX, nil, nil) + Nodconst(nc, nl.Type, 1) + n3 := Nod(OAND, n2, nc) + n = Nod(OADD, n3, n1) + } else { + // n = (nl+ε)&(nr-1) - ε where ε=2^pow-1 iff nl<0. + nc := Nod(OXXX, nil, nil) + + Nodconst(nc, nl.Type, (1<= 0, nl >> n == nl / nr + // if nl < 0, we want to add 2^n-1 first. + nc := Nod(OXXX, nil, nil) + + Nodconst(nc, Types[Simtype[TUINT]], int64(w)-1) + n1 := Nod(ORSH, nl, nc) // n1 = -1 iff nl < 0. + if pow == 1 { + // nl+1 is nl-(-1) + n.Left = Nod(OSUB, nl, n1) + } else { + // Do a logical right right on -1 to keep pow bits. + nc := Nod(OXXX, nil, nil) + + Nodconst(nc, Types[Simtype[TUINT]], int64(w)-int64(pow)) + n2 := Nod(ORSH, conv(n1, tounsigned(nl.Type)), nc) + n.Left = Nod(OADD, nl, conv(n2, nl.Type)) + } + + // n = (nl + 2^pow-1) >> pow + n.Op = ORSH + + nc = Nod(OXXX, nil, nil) + Nodconst(nc, Types[Simtype[TUINT]], int64(pow)) + n.Right = nc + n.Typecheck = 0 + } + + if s != 0 { + n = Nod(OMINUS, n, nil) + } + break + } + + nc := Nod(OXXX, nil, nil) + if n.Op == OMOD { + // n = nl & (nr-1) + n.Op = OAND + + Nodconst(nc, nl.Type, Mpgetfix(nr.Val.U.(*Mpint))-1) + } else { + // n = nl >> pow + n.Op = ORSH + + Nodconst(nc, Types[Simtype[TUINT]], int64(pow)) + } + + n.Typecheck = 0 + n.Right = nc + } + + goto ret + +ret: + typecheck(&n, Erv) + walkexpr(&n, init) + *np = n +} + +// return 1 if integer n must be in range [0, max), 0 otherwise +func bounded(n *Node, max int64) bool { + if n.Type == nil || !Isint[n.Type.Etype] { + return false + } + + sign := Issigned[n.Type.Etype] + bits := int32(8 * n.Type.Width) + + if Smallintconst(n) { + v := Mpgetfix(n.Val.U.(*Mpint)) + return 0 <= v && v < max + } + + switch n.Op { + case OAND: + v := int64(-1) + if Smallintconst(n.Left) { + v = Mpgetfix(n.Left.Val.U.(*Mpint)) + } else if Smallintconst(n.Right) { + v = Mpgetfix(n.Right.Val.U.(*Mpint)) + } + + if 0 <= v && v < max { + return true + } + + case OMOD: + if !sign && Smallintconst(n.Right) { + v := Mpgetfix(n.Right.Val.U.(*Mpint)) + if 0 <= v && v <= max { + return true + } + } + + case ODIV: + if !sign && Smallintconst(n.Right) { + v := Mpgetfix(n.Right.Val.U.(*Mpint)) + for bits > 0 && v >= 2 { + bits-- + v >>= 1 + } + } + + case ORSH: + if !sign && Smallintconst(n.Right) { + v := Mpgetfix(n.Right.Val.U.(*Mpint)) + if v > int64(bits) { + return true + } + bits -= int32(v) + } + } + + if !sign && bits <= 62 && 1<= 1 && c-1 < len(yyToknames) { + if yyToknames[c-1] != "" { + return yyToknames[c-1] + } + } + return __yyfmt__.Sprintf("tok-%v", c) +} + +func yyStatname(s int) string { + if s >= 0 && s < len(yyStatenames) { + if yyStatenames[s] != "" { + return yyStatenames[s] + } + } + return __yyfmt__.Sprintf("state-%v", s) +} + +func yyErrorMessage(state, lookAhead int) string { + const TOKSTART = 4 + + 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. + expected := make([]int, 0, 4) + + // Look for shiftable tokens. + base := yyPact[state] + for tok := TOKSTART; tok-1 < len(yyToknames); tok++ { + if n := base + tok; n >= 0 && n < yyLast && yyChk[yyAct[n]] == tok { + if len(expected) == cap(expected) { + return res + } + expected = append(expected, tok) + } + } + + if yyDef[state] == -2 { + i := 0 + for yyExca[i] != -1 || yyExca[i+1] != state { + i += 2 + } + + // Look for tokens that we accept or reduce. + for i += 2; yyExca[i] >= 0; i += 2 { + tok := yyExca[i] + if tok < TOKSTART || yyExca[i+1] == 0 { + continue + } + if len(expected) == cap(expected) { + return res + } + expected = append(expected, tok) + } + + // If the default action is to accept or reduce, give up. + if yyExca[i+1] != 0 { + return res + } + } + + for i, tok := range expected { + if i == 0 { + res += ", expecting " + } else { + res += " or " + } + res += yyTokname(tok) + } + return res +} + +func yylex1(lex yyLexer, lval *yySymType) (char, token int) { + token = 0 + char = lex.Lex(lval) + if char <= 0 { + token = yyTok1[0] + goto out + } + if char < len(yyTok1) { + token = yyTok1[char] + goto out + } + if char >= yyPrivate { + if char < yyPrivate+len(yyTok2) { + token = yyTok2[char-yyPrivate] + goto out + } + } + for i := 0; i < len(yyTok3); i += 2 { + token = yyTok3[i+0] + if token == char { + token = yyTok3[i+1] + goto out + } + } + +out: + if token == 0 { + token = yyTok2[1] /* unknown char */ + } + if yyDebug >= 3 { + __yyfmt__.Printf("lex %s(%d)\n", yyTokname(token), uint(char)) + } + return char, token +} + +func yyParse(yylex yyLexer) int { + return yyNewParser().Parse(yylex) +} + +func (yyrcvr *yyParserImpl) Parse(yylex yyLexer) int { + var yyn int + var yylval yySymType + var yyVAL yySymType + var yyDollar []yySymType + yyS := make([]yySymType, yyMaxDepth) + + Nerrs := 0 /* number of errors */ + Errflag := 0 /* error recovery flag */ + yystate := 0 + yychar := -1 + yytoken := -1 // yychar translated into internal numbering + yyrcvr.lookahead = func() int { return yychar } + defer func() { + // Make sure we report no lookahead when not parsing. + yystate = -1 + yychar = -1 + yytoken = -1 + }() + yyp := -1 + goto yystack + +ret0: + return 0 + +ret1: + return 1 + +yystack: + /* put a state and value onto the stack */ + if yyDebug >= 4 { + __yyfmt__.Printf("char %v in %v\n", yyTokname(yytoken), yyStatname(yystate)) + } + + yyp++ + if yyp >= len(yyS) { + nyys := make([]yySymType, len(yyS)*2) + copy(nyys, yyS) + yyS = nyys + } + yyS[yyp] = yyVAL + yyS[yyp].yys = yystate + +yynewstate: + yyn = yyPact[yystate] + if yyn <= yyFlag { + goto yydefault /* simple state */ + } + if yychar < 0 { + yychar, yytoken = yylex1(yylex, &yylval) + } + yyn += yytoken + if yyn < 0 || yyn >= yyLast { + goto yydefault + } + yyn = yyAct[yyn] + if yyChk[yyn] == yytoken { /* valid shift */ + yychar = -1 + yytoken = -1 + yyVAL = yylval + yystate = yyn + if Errflag > 0 { + Errflag-- + } + goto yystack + } + +yydefault: + /* default state action */ + yyn = yyDef[yystate] + if yyn == -2 { + if yychar < 0 { + yychar, yytoken = yylex1(yylex, &yylval) + } + + /* look through exception table */ + xi := 0 + for { + if yyExca[xi+0] == -1 && yyExca[xi+1] == yystate { + break + } + xi += 2 + } + for xi += 2; ; xi += 2 { + yyn = yyExca[xi+0] + if yyn < 0 || yyn == yytoken { + break + } + } + yyn = yyExca[xi+1] + if yyn < 0 { + goto ret0 + } + } + if yyn == 0 { + /* error ... attempt to resume parsing */ + switch Errflag { + case 0: /* brand new error */ + yylex.Error(yyErrorMessage(yystate, yytoken)) + Nerrs++ + if yyDebug >= 1 { + __yyfmt__.Printf("%s", yyStatname(yystate)) + __yyfmt__.Printf(" saw %s\n", yyTokname(yytoken)) + } + fallthrough + + case 1, 2: /* incompletely recovered error ... try again */ + Errflag = 3 + + /* find a state where "error" is a legal shift action */ + for yyp >= 0 { + yyn = yyPact[yyS[yyp].yys] + yyErrCode + if yyn >= 0 && yyn < yyLast { + yystate = yyAct[yyn] /* simulate a shift of "error" */ + if yyChk[yystate] == yyErrCode { + goto yystack + } + } + + /* the current p has no shift on "error", pop stack */ + if yyDebug >= 2 { + __yyfmt__.Printf("error recovery pops state %d\n", yyS[yyp].yys) + } + yyp-- + } + /* there is no state on the stack with an error shift ... abort */ + goto ret1 + + case 3: /* no shift yet; clobber input char */ + if yyDebug >= 2 { + __yyfmt__.Printf("error recovery discards %s\n", yyTokname(yytoken)) + } + if yytoken == yyEofCode { + goto ret1 + } + yychar = -1 + yytoken = -1 + goto yynewstate /* try again in the same state */ + } + } + + /* reduction by production yyn */ + if yyDebug >= 2 { + __yyfmt__.Printf("reduce %v in:\n\t%v\n", yyn, yyStatname(yystate)) + } + + yynt := yyn + yypt := yyp + _ = yypt // guard against "declared and not used" + + yyp -= yyR2[yyn] + // yyp is now the index of $0. Perform the default action. Iff the + // reduced production is ε, $1 is possibly out of range. + if yyp+1 >= len(yyS) { + nyys := make([]yySymType, len(yyS)*2) + copy(nyys, yyS) + yyS = nyys + } + yyVAL = yyS[yyp+1] + + /* consult goto table to find next state */ + yyn = yyR1[yyn] + yyg := yyPgo[yyn] + yyj := yyg + yyS[yyp].yys + 1 + + if yyj >= yyLast { + yystate = yyAct[yyg] + } else { + yystate = yyAct[yyj] + if yyChk[yystate] != -yyn { + yystate = yyAct[yyg] + } + } + // dummy call; replaced with literal code + switch yynt { + + case 1: + yyDollar = yyS[yypt-4 : yypt+1] + //line go.y:189 + { + xtop = concat(xtop, yyDollar[4].list) + } + case 2: + yyDollar = yyS[yypt-0 : yypt+1] + //line go.y:195 + { + prevlineno = lineno + Yyerror("package statement must be first") + errorexit() + } + case 3: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:201 + { + mkpackage(yyDollar[2].sym.Name) + } + case 4: + yyDollar = yyS[yypt-0 : yypt+1] + //line go.y:211 + { + importpkg = Runtimepkg + + if Debug['A'] != 0 { + cannedimports("runtime.Builtin", "package runtime\n\n$$\n\n") + } else { + cannedimports("runtime.Builtin", runtimeimport) + } + curio.importsafe = true + } + case 5: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:223 + { + importpkg = nil + } + case 11: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:237 + { + ipkg := importpkg + my := importmyname + importpkg = nil + importmyname = nil + + if my == nil { + my = Lookup(ipkg.Name) + } + + pack := Nod(OPACK, nil, nil) + pack.Sym = my + pack.Pkg = ipkg + pack.Lineno = int32(yyDollar[1].i) + + if strings.HasPrefix(my.Name, ".") { + importdot(ipkg, pack) + break + } + if my.Name == "init" { + Yyerror("cannot import package as init - init must be a func") + break + } + if my.Name == "_" { + break + } + if my.Def != nil { + lineno = int32(yyDollar[1].i) + redeclare(my, "as imported package name") + } + my.Def = pack + my.Lastlineno = int32(yyDollar[1].i) + my.Block = 1 // at top level + } + case 12: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:272 + { + // When an invalid import path is passed to importfile, + // it calls Yyerror and then sets up a fake import with + // no package statement. This allows us to test more + // than one invalid import statement in a single file. + if nerrors == 0 { + Fatal("phase error in import") + } + } + case 15: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:288 + { + // import with original name + yyVAL.i = parserline() + importmyname = nil + importfile(&yyDollar[1].val, yyVAL.i) + } + case 16: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:295 + { + // import with given name + yyVAL.i = parserline() + importmyname = yyDollar[1].sym + importfile(&yyDollar[2].val, yyVAL.i) + } + case 17: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:302 + { + // import into my name space + yyVAL.i = parserline() + importmyname = Lookup(".") + importfile(&yyDollar[2].val, yyVAL.i) + } + case 18: + yyDollar = yyS[yypt-4 : yypt+1] + //line go.y:311 + { + if importpkg.Name == "" { + importpkg.Name = yyDollar[2].sym.Name + numImport[yyDollar[2].sym.Name]++ + } else if importpkg.Name != yyDollar[2].sym.Name { + Yyerror("conflicting names %s and %s for package %q", importpkg.Name, yyDollar[2].sym.Name, importpkg.Path) + } + importpkg.Direct = 1 + importpkg.Safe = curio.importsafe + + if safemode != 0 && !curio.importsafe { + Yyerror("cannot import unsafe package %q", importpkg.Path) + } + } + case 20: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:328 + { + if yyDollar[1].sym.Name == "safe" { + curio.importsafe = true + } + } + case 21: + yyDollar = yyS[yypt-0 : yypt+1] + //line go.y:335 + { + defercheckwidth() + } + case 22: + yyDollar = yyS[yypt-4 : yypt+1] + //line go.y:339 + { + resumecheckwidth() + unimportfile() + } + case 23: + yyDollar = yyS[yypt-0 : yypt+1] + //line go.y:348 + { + Yyerror("empty top-level declaration") + yyVAL.list = nil + } + case 25: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:354 + { + yyVAL.list = list1(yyDollar[1].node) + } + case 26: + yyDollar = yyS[yypt-1 : yypt+1] + //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:363 + { + yyVAL.list = nil + } + case 28: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:369 + { + yyVAL.list = yyDollar[2].list + } + case 29: + yyDollar = yyS[yypt-5 : yypt+1] + //line go.y:373 + { + yyVAL.list = yyDollar[3].list + } + case 30: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:377 + { + yyVAL.list = nil + } + case 31: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:381 + { + yyVAL.list = yyDollar[2].list + iota_ = -100000 + lastconst = nil + } + case 32: + yyDollar = yyS[yypt-5 : yypt+1] + //line go.y:387 + { + yyVAL.list = yyDollar[3].list + iota_ = -100000 + lastconst = nil + } + case 33: + yyDollar = yyS[yypt-7 : yypt+1] + //line go.y:393 + { + yyVAL.list = concat(yyDollar[3].list, yyDollar[5].list) + iota_ = -100000 + lastconst = nil + } + case 34: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:399 + { + yyVAL.list = nil + iota_ = -100000 + } + case 35: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:404 + { + yyVAL.list = list1(yyDollar[2].node) + } + case 36: + yyDollar = yyS[yypt-5 : yypt+1] + //line go.y:408 + { + yyVAL.list = yyDollar[3].list + } + case 37: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:412 + { + yyVAL.list = nil + } + case 38: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:418 + { + iota_ = 0 + } + case 39: + yyDollar = yyS[yypt-2 : yypt+1] + //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:428 + { + yyVAL.list = variter(yyDollar[1].list, yyDollar[2].node, yyDollar[4].list) + } + case 41: + yyDollar = yyS[yypt-3 : yypt+1] + //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:438 + { + yyVAL.list = constiter(yyDollar[1].list, yyDollar[2].node, yyDollar[4].list) + } + case 43: + yyDollar = yyS[yypt-3 : yypt+1] + //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:449 + { + yyVAL.list = constiter(yyDollar[1].list, yyDollar[2].node, nil) + } + case 46: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:453 + { + yyVAL.list = constiter(yyDollar[1].list, nil, nil) + } + case 47: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:459 + { + // different from dclname because the name + // becomes visible right here, not at the end + // of the declaration. + yyVAL.node = typedcl0(yyDollar[1].sym) + } + case 48: + yyDollar = yyS[yypt-2 : yypt+1] + //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:474 + { + yyVAL.node = yyDollar[1].node + + // These nodes do not carry line numbers. + // Since a bare name used as an expression is an error, + // introduce a wrapper node to give the correct line. + switch yyVAL.node.Op { + case ONAME, ONONAME, OTYPE, OPACK, OLITERAL: + yyVAL.node = Nod(OPAREN, yyVAL.node, nil) + yyVAL.node.Implicit = true + break + } + } + case 50: + yyDollar = yyS[yypt-3 : yypt+1] + //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:493 + { + if yyDollar[1].list.Next == nil && yyDollar[3].list.Next == nil { + // simple + yyVAL.node = Nod(OAS, yyDollar[1].list.N, yyDollar[3].list.N) + break + } + // multiple + yyVAL.node = Nod(OAS2, nil, nil) + yyVAL.node.List = yyDollar[1].list + yyVAL.node.Rlist = yyDollar[3].list + } + case 52: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:505 + { + if yyDollar[3].list.N.Op == OTYPESW { + yyVAL.node = Nod(OTYPESW, nil, yyDollar[3].list.N.Right) + if yyDollar[3].list.Next != nil { + Yyerror("expr.(type) must be alone in list") + } + if yyDollar[1].list.Next != nil { + Yyerror("argument count mismatch: %d = %d", count(yyDollar[1].list), 1) + } else if (yyDollar[1].list.N.Op != ONAME && yyDollar[1].list.N.Op != OTYPE && yyDollar[1].list.N.Op != ONONAME) || isblank(yyDollar[1].list.N) { + Yyerror("invalid variable name %s in type switch", yyDollar[1].list.N) + } else { + yyVAL.node.Left = dclname(yyDollar[1].list.N.Sym) + } // it's a colas, so must not re-use an oldname. + break + } + yyVAL.node = colas(yyDollar[1].list, yyDollar[3].list, int32(yyDollar[2].i)) + } + case 53: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:523 + { + yyVAL.node = Nod(OASOP, yyDollar[1].node, Nodintconst(1)) + yyVAL.node.Implicit = true + yyVAL.node.Etype = OADD + } + case 54: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:529 + { + yyVAL.node = Nod(OASOP, yyDollar[1].node, Nodintconst(1)) + yyVAL.node.Implicit = true + yyVAL.node.Etype = OSUB + } + case 55: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:537 + { + var n, nn *Node + + // will be converted to OCASE + // right will point to next case + // done in casebody() + markdcl() + yyVAL.node = Nod(OXCASE, nil, nil) + yyVAL.node.List = yyDollar[2].list + if typesw != nil && typesw.Right != nil { + n = typesw.Right.Left + if n != nil { + // type switch - declare variable + nn = newname(n.Sym) + declare(nn, dclcontext) + yyVAL.node.Nname = nn + + // keep track of the instances for reporting unused + nn.Defn = typesw.Right + } + } + } + case 56: + yyDollar = yyS[yypt-5 : yypt+1] + //line go.y:560 + { + var n *Node + + // will be converted to OCASE + // right will point to next case + // done in casebody() + markdcl() + yyVAL.node = Nod(OXCASE, nil, nil) + if yyDollar[2].list.Next == nil { + n = Nod(OAS, yyDollar[2].list.N, yyDollar[4].node) + } else { + n = Nod(OAS2, nil, nil) + n.List = yyDollar[2].list + n.Rlist = list1(yyDollar[4].node) + } + yyVAL.node.List = list1(n) + } + case 57: + yyDollar = yyS[yypt-5 : yypt+1] + //line go.y:578 + { + // will be converted to OCASE + // right will point to next case + // done in casebody() + markdcl() + yyVAL.node = Nod(OXCASE, nil, nil) + yyVAL.node.List = list1(colas(yyDollar[2].list, list1(yyDollar[4].node), int32(yyDollar[3].i))) + } + case 58: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:587 + { + var n, nn *Node + + markdcl() + yyVAL.node = Nod(OXCASE, nil, nil) + if typesw != nil && typesw.Right != nil { + n = typesw.Right.Left + if n != nil { + // type switch - declare variable + nn = newname(n.Sym) + declare(nn, dclcontext) + yyVAL.node.Nname = nn + + // keep track of the instances for reporting unused + nn.Defn = typesw.Right + } + } + } + case 59: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:608 + { + markdcl() + } + case 60: + yyDollar = yyS[yypt-4 : yypt+1] + //line go.y:612 + { + if yyDollar[3].list == nil { + yyVAL.node = Nod(OEMPTY, nil, nil) + } else { + yyVAL.node = liststmt(yyDollar[3].list) + } + popdcl() + } + case 61: + yyDollar = yyS[yypt-1 : yypt+1] + //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). + // If the last token read by the lexer was the lookahead + // leave it alone (parser has it cached in yychar). + // This is so that the stmt_list action doesn't look at + // the case tokens if the stmt_list is empty. + yylast = yychar + yyDollar[1].node.Xoffset = int64(block) + } + case 62: + yyDollar = yyS[yypt-3 : yypt+1] + //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 + // it's the only place where a statement list is not followed + // by a closing brace. Handle the error for pedantry. + + // Find the final token of the statement list. + // yylast is lookahead; yyprev is last of stmt_list + last := yyprev + + if last > 0 && last != ';' && yychar != '}' { + Yyerror("missing statement after label") + } + yyVAL.node = yyDollar[1].node + yyVAL.node.Nbody = yyDollar[3].list + popdcl() + } + case 63: + yyDollar = yyS[yypt-0 : yypt+1] + //line go.y:653 + { + yyVAL.list = nil + } + case 64: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:657 + { + yyVAL.list = list(yyDollar[1].list, yyDollar[2].node) + } + case 65: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:663 + { + markdcl() + } + case 66: + yyDollar = yyS[yypt-4 : yypt+1] + //line go.y:667 + { + yyVAL.list = yyDollar[3].list + popdcl() + } + case 67: + yyDollar = yyS[yypt-4 : yypt+1] + //line go.y:674 + { + yyVAL.node = Nod(ORANGE, nil, yyDollar[4].node) + yyVAL.node.List = yyDollar[1].list + yyVAL.node.Etype = 0 // := flag + } + case 68: + yyDollar = yyS[yypt-4 : yypt+1] + //line go.y:680 + { + yyVAL.node = Nod(ORANGE, nil, yyDollar[4].node) + yyVAL.node.List = yyDollar[1].list + yyVAL.node.Colas = true + colasdefn(yyDollar[1].list, yyVAL.node) + } + case 69: + yyDollar = yyS[yypt-2 : yypt+1] + //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:694 + { + // init ; test ; incr + if yyDollar[5].node != nil && yyDollar[5].node.Colas { + Yyerror("cannot declare in the for-increment") + } + yyVAL.node = Nod(OFOR, nil, nil) + if yyDollar[1].node != nil { + yyVAL.node.Ninit = list1(yyDollar[1].node) + } + yyVAL.node.Ntest = yyDollar[3].node + yyVAL.node.Nincr = yyDollar[5].node + } + case 71: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:707 + { + // normal test + yyVAL.node = Nod(OFOR, nil, nil) + yyVAL.node.Ntest = yyDollar[1].node + } + case 73: + yyDollar = yyS[yypt-2 : yypt+1] + //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:723 + { + markdcl() + } + case 75: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:727 + { + yyVAL.node = yyDollar[3].node + popdcl() + } + case 76: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:734 + { + // test + yyVAL.node = Nod(OIF, nil, nil) + yyVAL.node.Ntest = yyDollar[1].node + } + case 77: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:740 + { + // init ; test + yyVAL.node = Nod(OIF, nil, nil) + if yyDollar[1].node != nil { + yyVAL.node.Ninit = list1(yyDollar[1].node) + } + yyVAL.node.Ntest = yyDollar[3].node + } + case 78: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:752 + { + markdcl() + } + case 79: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:756 + { + if yyDollar[3].node.Ntest == nil { + Yyerror("missing condition in if statement") + } + } + case 80: + yyDollar = yyS[yypt-5 : yypt+1] + //line go.y:762 + { + yyDollar[3].node.Nbody = yyDollar[5].list + } + case 81: + yyDollar = yyS[yypt-8 : yypt+1] + //line go.y:766 + { + var n *Node + var nn *NodeList + + yyVAL.node = yyDollar[3].node + n = yyDollar[3].node + popdcl() + for nn = concat(yyDollar[7].list, yyDollar[8].list); nn != nil; nn = nn.Next { + if nn.N.Op == OIF { + popdcl() + } + n.Nelse = list1(nn.N) + n = nn.N + } + } + case 82: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:784 + { + markdcl() + } + case 83: + yyDollar = yyS[yypt-5 : yypt+1] + //line go.y:788 + { + if yyDollar[4].node.Ntest == nil { + Yyerror("missing condition in if statement") + } + yyDollar[4].node.Nbody = yyDollar[5].list + yyVAL.list = list1(yyDollar[4].node) + } + case 84: + yyDollar = yyS[yypt-0 : yypt+1] + //line go.y:797 + { + yyVAL.list = nil + } + case 85: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:801 + { + yyVAL.list = concat(yyDollar[1].list, yyDollar[2].list) + } + case 86: + yyDollar = yyS[yypt-0 : yypt+1] + //line go.y:806 + { + yyVAL.list = nil + } + case 87: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:810 + { + l := &NodeList{N: yyDollar[2].node} + l.End = l + yyVAL.list = l + } + case 88: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:818 + { + markdcl() + } + case 89: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:822 + { + var n *Node + n = yyDollar[3].node.Ntest + if n != nil && n.Op != OTYPESW { + n = nil + } + typesw = Nod(OXXX, typesw, n) + } + case 90: + yyDollar = yyS[yypt-7 : yypt+1] + //line go.y:831 + { + yyVAL.node = yyDollar[3].node + yyVAL.node.Op = OSWITCH + yyVAL.node.List = yyDollar[6].list + typesw = typesw.Left + popdcl() + } + case 91: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:841 + { + typesw = Nod(OXXX, typesw, nil) + } + case 92: + yyDollar = yyS[yypt-5 : yypt+1] + //line go.y:845 + { + yyVAL.node = Nod(OSELECT, nil, nil) + yyVAL.node.Lineno = typesw.Lineno + yyVAL.node.List = yyDollar[4].list + typesw = typesw.Left + } + case 94: + yyDollar = yyS[yypt-3 : yypt+1] + //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:862 + { + yyVAL.node = Nod(OANDAND, yyDollar[1].node, yyDollar[3].node) + } + case 96: + yyDollar = yyS[yypt-3 : yypt+1] + //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:870 + { + yyVAL.node = Nod(ONE, yyDollar[1].node, yyDollar[3].node) + } + case 98: + yyDollar = yyS[yypt-3 : yypt+1] + //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:878 + { + yyVAL.node = Nod(OLE, yyDollar[1].node, yyDollar[3].node) + } + case 100: + yyDollar = yyS[yypt-3 : yypt+1] + //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:886 + { + yyVAL.node = Nod(OGT, yyDollar[1].node, yyDollar[3].node) + } + case 102: + yyDollar = yyS[yypt-3 : yypt+1] + //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:894 + { + yyVAL.node = Nod(OSUB, yyDollar[1].node, yyDollar[3].node) + } + case 104: + yyDollar = yyS[yypt-3 : yypt+1] + //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:902 + { + yyVAL.node = Nod(OXOR, yyDollar[1].node, yyDollar[3].node) + } + case 106: + yyDollar = yyS[yypt-3 : yypt+1] + //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:910 + { + yyVAL.node = Nod(ODIV, yyDollar[1].node, yyDollar[3].node) + } + case 108: + yyDollar = yyS[yypt-3 : yypt+1] + //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:918 + { + yyVAL.node = Nod(OAND, yyDollar[1].node, yyDollar[3].node) + } + case 110: + yyDollar = yyS[yypt-3 : yypt+1] + //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:926 + { + yyVAL.node = Nod(OLSH, yyDollar[1].node, yyDollar[3].node) + } + case 112: + yyDollar = yyS[yypt-3 : yypt+1] + //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:935 + { + yyVAL.node = Nod(OSEND, yyDollar[1].node, yyDollar[3].node) + } + case 115: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:942 + { + yyVAL.node = Nod(OIND, yyDollar[2].node, nil) + } + case 116: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:946 + { + if yyDollar[2].node.Op == OCOMPLIT { + // Special case for &T{...}: turn into (*T){...}. + yyVAL.node = yyDollar[2].node + yyVAL.node.Right = Nod(OIND, yyVAL.node.Right, nil) + yyVAL.node.Right.Implicit = true + } else { + yyVAL.node = Nod(OADDR, yyDollar[2].node, nil) + } + } + case 117: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:957 + { + yyVAL.node = Nod(OPLUS, yyDollar[2].node, nil) + } + case 118: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:961 + { + yyVAL.node = Nod(OMINUS, yyDollar[2].node, nil) + } + case 119: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:965 + { + yyVAL.node = Nod(ONOT, yyDollar[2].node, nil) + } + case 120: + yyDollar = yyS[yypt-2 : yypt+1] + //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:974 + { + yyVAL.node = Nod(OCOM, yyDollar[2].node, nil) + } + case 122: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:978 + { + yyVAL.node = Nod(ORECV, yyDollar[2].node, nil) + } + case 123: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:988 + { + yyVAL.node = Nod(OCALL, yyDollar[1].node, nil) + } + case 124: + yyDollar = yyS[yypt-5 : yypt+1] + //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:997 + { + yyVAL.node = Nod(OCALL, yyDollar[1].node, nil) + yyVAL.node.List = yyDollar[3].list + yyVAL.node.Isddd = true + } + case 126: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:1005 + { + yyVAL.node = nodlit(yyDollar[1].val) + } + case 128: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:1010 + { + if yyDollar[1].node.Op == OPACK { + var s *Sym + s = restrictlookup(yyDollar[3].sym.Name, yyDollar[1].node.Pkg) + yyDollar[1].node.Used = true + yyVAL.node = oldname(s) + break + } + yyVAL.node = Nod(OXDOT, yyDollar[1].node, newname(yyDollar[3].sym)) + } + case 129: + yyDollar = yyS[yypt-5 : yypt+1] + //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:1025 + { + yyVAL.node = Nod(OTYPESW, nil, yyDollar[1].node) + } + case 131: + yyDollar = yyS[yypt-4 : yypt+1] + //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: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:1037 + { + if yyDollar[5].node == nil { + Yyerror("middle index required in 3-index slice") + } + if yyDollar[7].node == nil { + Yyerror("final index required in 3-index slice") + } + yyVAL.node = Nod(OSLICE3, yyDollar[1].node, Nod(OKEY, yyDollar[3].node, Nod(OKEY, yyDollar[5].node, yyDollar[7].node))) + } + case 135: + yyDollar = yyS[yypt-5 : yypt+1] + //line go.y:1048 + { + // conversion + yyVAL.node = Nod(OCALL, yyDollar[1].node, nil) + yyVAL.node.List = list1(yyDollar[3].node) + } + case 136: + yyDollar = yyS[yypt-5 : yypt+1] + //line go.y:1054 + { + yyVAL.node = yyDollar[3].node + yyVAL.node.Right = yyDollar[1].node + yyVAL.node.List = yyDollar[4].list + fixlbrace(yyDollar[2].i) + } + case 137: + yyDollar = yyS[yypt-5 : yypt+1] + //line go.y:1061 + { + yyVAL.node = yyDollar[3].node + yyVAL.node.Right = yyDollar[1].node + yyVAL.node.List = yyDollar[4].list + } + case 138: + yyDollar = yyS[yypt-7 : yypt+1] + //line go.y:1067 + { + Yyerror("cannot parenthesize type in composite literal") + yyVAL.node = yyDollar[5].node + yyVAL.node.Right = yyDollar[2].node + yyVAL.node.List = yyDollar[6].list + } + case 140: + yyDollar = yyS[yypt-0 : yypt+1] + //line go.y:1076 + { + // composite expression. + // make node early so we get the right line number. + yyVAL.node = Nod(OCOMPLIT, nil, nil) + } + case 141: + yyDollar = yyS[yypt-3 : yypt+1] + //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:1090 + { + // These nodes do not carry line numbers. + // Since a composite literal commonly spans several lines, + // the line number on errors may be misleading. + // Introduce a wrapper node to give the correct line. + yyVAL.node = yyDollar[1].node + switch yyVAL.node.Op { + case ONAME, ONONAME, OTYPE, OPACK, OLITERAL: + yyVAL.node = Nod(OPAREN, yyVAL.node, nil) + yyVAL.node.Implicit = true + } + } + case 143: + yyDollar = yyS[yypt-4 : yypt+1] + //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:1111 + { + yyVAL.node = yyDollar[2].node + yyVAL.node.List = yyDollar[3].list + } + case 147: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:1119 + { + yyVAL.node = yyDollar[2].node + + // Need to know on lhs of := whether there are ( ). + // Don't bother with the OPAREN in other cases: + // it's just a waste of memory and time. + switch yyVAL.node.Op { + case ONAME, ONONAME, OPACK, OTYPE, OLITERAL, OTYPESW: + yyVAL.node = Nod(OPAREN, yyVAL.node, nil) + } + } + case 151: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:1140 + { + yyVAL.i = LBODY + } + case 152: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:1144 + { + yyVAL.i = '{' + } + case 153: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:1155 + { + if yyDollar[1].sym == nil { + yyVAL.node = nil + } else { + yyVAL.node = newname(yyDollar[1].sym) + } + } + case 154: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:1165 + { + yyVAL.node = dclname(yyDollar[1].sym) + } + case 155: + yyDollar = yyS[yypt-0 : yypt+1] + //line go.y:1170 + { + yyVAL.node = nil + } + case 157: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:1177 + { + yyVAL.sym = yyDollar[1].sym + // during imports, unqualified non-exported identifiers are from builtinpkg + if importpkg != nil && !exportname(yyDollar[1].sym.Name) { + yyVAL.sym = Pkglookup(yyDollar[1].sym.Name, builtinpkg) + } + } + case 159: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:1186 + { + yyVAL.sym = nil + } + case 160: + yyDollar = yyS[yypt-4 : yypt+1] + //line go.y:1192 + { + var p *Pkg + + if yyDollar[2].val.U.(string) == "" { + p = importpkg + } else { + if isbadimport(yyDollar[2].val.U.(string)) { + errorexit() + } + p = mkpkg(yyDollar[2].val.U.(string)) + } + yyVAL.sym = Pkglookup(yyDollar[4].sym.Name, p) + } + case 161: + yyDollar = yyS[yypt-4 : yypt+1] + //line go.y:1206 + { + var p *Pkg + + if yyDollar[2].val.U.(string) == "" { + p = importpkg + } else { + if isbadimport(yyDollar[2].val.U.(string)) { + errorexit() + } + p = mkpkg(yyDollar[2].val.U.(string)) + } + yyVAL.sym = Pkglookup("?", p) + } + case 162: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:1222 + { + yyVAL.node = oldname(yyDollar[1].sym) + if yyVAL.node.Pack != nil { + yyVAL.node.Pack.Used = true + } + } + case 164: + yyDollar = yyS[yypt-1 : yypt+1] + //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:1248 + { + yyVAL.node = Nod(ODDD, yyDollar[2].node, nil) + } + case 171: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:1259 + { + yyVAL.node = yyDollar[2].node + } + case 175: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:1268 + { + yyVAL.node = Nod(OIND, yyDollar[2].node, nil) + } + case 180: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:1278 + { + yyVAL.node = yyDollar[2].node + } + case 190: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:1299 + { + if yyDollar[1].node.Op == OPACK { + var s *Sym + s = restrictlookup(yyDollar[3].sym.Name, yyDollar[1].node.Pkg) + yyDollar[1].node.Used = true + yyVAL.node = oldname(s) + break + } + yyVAL.node = Nod(OXDOT, yyDollar[1].node, newname(yyDollar[3].sym)) + } + case 191: + yyDollar = yyS[yypt-4 : yypt+1] + //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: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:1321 + { + yyVAL.node = Nod(OTCHAN, yyDollar[2].node, nil) + yyVAL.node.Etype = Cboth + } + case 194: + yyDollar = yyS[yypt-3 : yypt+1] + //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:1331 + { + yyVAL.node = Nod(OTMAP, yyDollar[3].node, yyDollar[5].node) + } + case 198: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:1339 + { + yyVAL.node = Nod(OIND, yyDollar[2].node, nil) + } + case 199: + yyDollar = yyS[yypt-3 : yypt+1] + //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:1352 + { + yyVAL.node = Nod(OTSTRUCT, nil, nil) + yyVAL.node.List = yyDollar[3].list + fixlbrace(yyDollar[2].i) + } + case 201: + yyDollar = yyS[yypt-3 : yypt+1] + //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:1365 + { + yyVAL.node = Nod(OTINTER, nil, nil) + yyVAL.node.List = yyDollar[3].list + fixlbrace(yyDollar[2].i) + } + case 203: + yyDollar = yyS[yypt-3 : yypt+1] + //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:1382 + { + yyVAL.node = yyDollar[2].node + if yyVAL.node == nil { + break + } + if noescape && yyDollar[3].list != nil { + Yyerror("can only use //go:noescape with external func implementations") + } + yyVAL.node.Nbody = yyDollar[3].list + yyVAL.node.Func.Endlineno = lineno + yyVAL.node.Noescape = noescape + yyVAL.node.Func.Nosplit = nosplit + yyVAL.node.Func.Nowritebarrier = nowritebarrier + funcbody(yyVAL.node) + } + case 205: + yyDollar = yyS[yypt-5 : yypt+1] + //line go.y:1400 + { + var t *Node + + yyVAL.node = nil + yyDollar[3].list = checkarglist(yyDollar[3].list, 1) + + if yyDollar[1].sym.Name == "init" { + yyDollar[1].sym = renameinit() + if yyDollar[3].list != nil || yyDollar[5].list != nil { + Yyerror("func init must have no arguments and no return values") + } + } + if localpkg.Name == "main" && yyDollar[1].sym.Name == "main" { + if yyDollar[3].list != nil || yyDollar[5].list != nil { + Yyerror("func main must have no arguments and no return values") + } + } + + t = Nod(OTFUNC, nil, nil) + t.List = yyDollar[3].list + t.Rlist = yyDollar[5].list + + 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 + declare(yyVAL.node.Nname, PFUNC) + + funchdr(yyVAL.node) + } + case 206: + yyDollar = yyS[yypt-8 : yypt+1] + //line go.y:1431 + { + var rcvr, t *Node + + yyVAL.node = nil + yyDollar[2].list = checkarglist(yyDollar[2].list, 0) + yyDollar[6].list = checkarglist(yyDollar[6].list, 1) + + if yyDollar[2].list == nil { + Yyerror("method has no receiver") + break + } + if yyDollar[2].list.Next != nil { + Yyerror("method has multiple receivers") + break + } + rcvr = yyDollar[2].list.N + if rcvr.Op != ODCLFIELD { + Yyerror("bad receiver in method") + break + } + + t = Nod(OTFUNC, rcvr, nil) + t.List = yyDollar[6].list + t.Rlist = yyDollar[8].list + + yyVAL.node = Nod(ODCLFUNC, nil, nil) + 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.Nointerface = nointerface + declare(yyVAL.node.Nname, PFUNC) + + funchdr(yyVAL.node) + } + case 207: + yyDollar = yyS[yypt-5 : yypt+1] + //line go.y:1469 + { + var s *Sym + var t *Type + + yyVAL.node = nil + + s = yyDollar[1].sym + t = functype(nil, yyDollar[3].list, yyDollar[5].list) + + importsym(s, ONAME) + if s.Def != nil && s.Def.Op == ONAME { + if Eqtype(t, s.Def.Type) { + dclcontext = PDISCARD // since we skip funchdr below + break + } + Yyerror("inconsistent definition for func %v during import\n\t%v\n\t%v", s, s.Def.Type, t) + } + + yyVAL.node = newfuncname(s) + yyVAL.node.Type = t + declare(yyVAL.node, PFUNC) + + funchdr(yyVAL.node) + } + case 208: + yyDollar = yyS[yypt-8 : yypt+1] + //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) + + checkwidth(yyVAL.node.Type) + addmethod(yyDollar[4].sym, yyVAL.node.Type, false, nointerface) + nointerface = false + funchdr(yyVAL.node) + + // inl.C's inlnode in on a dotmeth node expects to find the inlineable body as + // (dotmeth's type).Nname.Inl, and dotmeth's type has been pulled + // out by typecheck's lookdot as this $$.ttype. So by providing + // this back link here we avoid special casing there. + yyVAL.node.Type.Nname = yyVAL.node + } + case 209: + yyDollar = yyS[yypt-5 : yypt+1] + //line go.y:1512 + { + yyDollar[3].list = checkarglist(yyDollar[3].list, 1) + yyVAL.node = Nod(OTFUNC, nil, nil) + yyVAL.node.List = yyDollar[3].list + yyVAL.node.Rlist = yyDollar[5].list + } + case 210: + yyDollar = yyS[yypt-0 : yypt+1] + //line go.y:1520 + { + yyVAL.list = nil + } + case 211: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:1524 + { + yyVAL.list = yyDollar[2].list + if yyVAL.list == nil { + yyVAL.list = list1(Nod(OEMPTY, nil, nil)) + } + } + case 212: + yyDollar = yyS[yypt-0 : yypt+1] + //line go.y:1533 + { + yyVAL.list = nil + } + case 213: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:1537 + { + yyVAL.list = list1(Nod(ODCLFIELD, nil, yyDollar[1].node)) + } + case 214: + yyDollar = yyS[yypt-3 : yypt+1] + //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:1548 + { + closurehdr(yyDollar[1].node) + } + case 216: + yyDollar = yyS[yypt-4 : yypt+1] + //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:1559 + { + yyVAL.node = closurebody(nil) + } + case 218: + yyDollar = yyS[yypt-0 : yypt+1] + //line go.y:1570 + { + yyVAL.list = nil + } + case 219: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:1574 + { + yyVAL.list = concat(yyDollar[1].list, yyDollar[2].list) + if nsyntaxerrors == 0 { + testdclstack() + } + nointerface = false + noescape = false + nosplit = false + nowritebarrier = false + } + case 221: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:1588 + { + yyVAL.list = concat(yyDollar[1].list, yyDollar[3].list) + } + case 223: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:1595 + { + yyVAL.list = concat(yyDollar[1].list, yyDollar[3].list) + } + case 224: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:1601 + { + yyVAL.list = list1(yyDollar[1].node) + } + case 225: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:1605 + { + yyVAL.list = list(yyDollar[1].list, yyDollar[3].node) + } + case 227: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:1612 + { + yyVAL.list = concat(yyDollar[1].list, yyDollar[3].list) + } + case 228: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:1618 + { + yyVAL.list = list1(yyDollar[1].node) + } + case 229: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:1622 + { + yyVAL.list = list(yyDollar[1].list, yyDollar[3].node) + } + case 230: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:1628 + { + var l *NodeList + + var n *Node + l = yyDollar[1].list + if l == nil { + // ? symbol, during import (list1(nil) == nil) + n = yyDollar[2].node + if n.Op == OIND { + n = n.Left + } + n = embedded(n.Sym, importpkg) + n.Right = yyDollar[2].node + n.Val = yyDollar[3].val + yyVAL.list = list1(n) + break + } + + for l = yyDollar[1].list; l != nil; l = l.Next { + l.N = Nod(ODCLFIELD, l.N, yyDollar[2].node) + l.N.Val = yyDollar[3].val + } + } + case 231: + yyDollar = yyS[yypt-2 : yypt+1] + //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:1657 + { + yyDollar[2].node.Val = yyDollar[4].val + yyVAL.list = list1(yyDollar[2].node) + Yyerror("cannot parenthesize embedded type") + } + case 233: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:1663 + { + yyDollar[2].node.Right = Nod(OIND, yyDollar[2].node.Right, nil) + yyDollar[2].node.Val = yyDollar[3].val + yyVAL.list = list1(yyDollar[2].node) + } + case 234: + yyDollar = yyS[yypt-5 : yypt+1] + //line go.y:1669 + { + yyDollar[3].node.Right = Nod(OIND, yyDollar[3].node.Right, nil) + yyDollar[3].node.Val = yyDollar[5].val + yyVAL.list = list1(yyDollar[3].node) + Yyerror("cannot parenthesize embedded type") + } + case 235: + yyDollar = yyS[yypt-5 : yypt+1] + //line go.y:1676 + { + yyDollar[3].node.Right = Nod(OIND, yyDollar[3].node.Right, nil) + yyDollar[3].node.Val = yyDollar[5].val + yyVAL.list = list1(yyDollar[3].node) + Yyerror("cannot parenthesize embedded type") + } + case 236: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:1685 + { + var n *Node + + yyVAL.sym = yyDollar[1].sym + n = oldname(yyDollar[1].sym) + if n.Pack != nil { + n.Pack.Used = true + } + } + case 237: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:1695 + { + var pkg *Pkg + + if yyDollar[1].sym.Def == nil || yyDollar[1].sym.Def.Op != OPACK { + Yyerror("%v is not a package", yyDollar[1].sym) + pkg = localpkg + } else { + yyDollar[1].sym.Def.Used = true + pkg = yyDollar[1].sym.Def.Pkg + } + yyVAL.sym = restrictlookup(yyDollar[3].sym.Name, pkg) + } + case 238: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:1710 + { + yyVAL.node = embedded(yyDollar[1].sym, localpkg) + } + case 239: + yyDollar = yyS[yypt-2 : yypt+1] + //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:1721 + { + yyVAL.node = Nod(ODCLFIELD, nil, oldname(yyDollar[1].sym)) + } + case 241: + yyDollar = yyS[yypt-3 : yypt+1] + //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:1732 + { + // without func keyword + yyDollar[2].list = checkarglist(yyDollar[2].list, 1) + yyVAL.node = Nod(OTFUNC, fakethis(), nil) + yyVAL.node.List = yyDollar[2].list + yyVAL.node.Rlist = yyDollar[4].list + } + case 244: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:1746 + { + yyVAL.node = Nod(ONONAME, nil, nil) + yyVAL.node.Sym = yyDollar[1].sym + yyVAL.node = Nod(OKEY, yyVAL.node, yyDollar[2].node) + } + case 245: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:1752 + { + yyVAL.node = Nod(ONONAME, nil, nil) + yyVAL.node.Sym = yyDollar[1].sym + yyVAL.node = Nod(OKEY, yyVAL.node, yyDollar[2].node) + } + case 247: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:1761 + { + yyVAL.list = list1(yyDollar[1].node) + } + case 248: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:1765 + { + yyVAL.list = list(yyDollar[1].list, yyDollar[3].node) + } + case 249: + yyDollar = yyS[yypt-0 : yypt+1] + //line go.y:1770 + { + yyVAL.list = nil + } + case 250: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:1774 + { + yyVAL.list = yyDollar[1].list + } + case 251: + yyDollar = yyS[yypt-0 : yypt+1] + //line go.y:1782 + { + yyVAL.node = nil + } + case 253: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:1787 + { + yyVAL.node = liststmt(yyDollar[1].list) + } + case 255: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:1792 + { + yyVAL.node = nil + } + case 261: + yyDollar = yyS[yypt-2 : yypt+1] + //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:1808 + { + var l *NodeList + + yyDollar[1].node.Defn = yyDollar[4].node + l = list1(yyDollar[1].node) + if yyDollar[4].node != nil { + l = list(l, yyDollar[4].node) + } + yyVAL.node = liststmt(l) + } + case 263: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:1819 + { + // will be converted to OFALL + yyVAL.node = Nod(OXFALL, nil, nil) + yyVAL.node.Xoffset = int64(block) + } + case 264: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:1825 + { + yyVAL.node = Nod(OBREAK, yyDollar[2].node, nil) + } + case 265: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:1829 + { + yyVAL.node = Nod(OCONTINUE, yyDollar[2].node, nil) + } + case 266: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:1833 + { + yyVAL.node = Nod(OPROC, yyDollar[2].node, nil) + } + case 267: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:1837 + { + yyVAL.node = Nod(ODEFER, yyDollar[2].node, nil) + } + case 268: + yyDollar = yyS[yypt-2 : yypt+1] + //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:1846 + { + yyVAL.node = Nod(ORETURN, nil, nil) + yyVAL.node.List = yyDollar[2].list + if yyVAL.node.List == nil && Curfn != nil { + var l *NodeList + + for l = Curfn.Func.Dcl; l != nil; l = l.Next { + if l.N.Class == PPARAM { + continue + } + if l.N.Class != PPARAMOUT { + break + } + if l.N.Sym.Def != l.N { + Yyerror("%s is shadowed during return", l.N.Sym.Name) + } + } + } + } + case 270: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:1868 + { + yyVAL.list = nil + if yyDollar[1].node != nil { + yyVAL.list = list1(yyDollar[1].node) + } + } + case 271: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:1875 + { + yyVAL.list = yyDollar[1].list + if yyDollar[3].node != nil { + yyVAL.list = list(yyVAL.list, yyDollar[3].node) + } + } + case 272: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:1884 + { + yyVAL.list = list1(yyDollar[1].node) + } + case 273: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:1888 + { + yyVAL.list = list(yyDollar[1].list, yyDollar[3].node) + } + case 274: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:1894 + { + yyVAL.list = list1(yyDollar[1].node) + } + case 275: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:1898 + { + yyVAL.list = list(yyDollar[1].list, yyDollar[3].node) + } + case 276: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:1904 + { + yyVAL.list = list1(yyDollar[1].node) + } + case 277: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:1908 + { + yyVAL.list = list(yyDollar[1].list, yyDollar[3].node) + } + case 278: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:1914 + { + yyVAL.list = list1(yyDollar[1].node) + } + case 279: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:1918 + { + yyVAL.list = list(yyDollar[1].list, yyDollar[3].node) + } + case 280: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:1927 + { + yyVAL.list = list1(yyDollar[1].node) + } + case 281: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:1931 + { + yyVAL.list = list1(yyDollar[1].node) + } + case 282: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:1935 + { + yyVAL.list = list(yyDollar[1].list, yyDollar[3].node) + } + case 283: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:1939 + { + yyVAL.list = list(yyDollar[1].list, yyDollar[3].node) + } + case 284: + yyDollar = yyS[yypt-0 : yypt+1] + //line go.y:1944 + { + yyVAL.list = nil + } + case 285: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:1948 + { + yyVAL.list = yyDollar[1].list + } + case 290: + yyDollar = yyS[yypt-0 : yypt+1] + //line go.y:1962 + { + yyVAL.node = nil + } + case 292: + yyDollar = yyS[yypt-0 : yypt+1] + //line go.y:1968 + { + yyVAL.list = nil + } + case 294: + yyDollar = yyS[yypt-0 : yypt+1] + //line go.y:1974 + { + yyVAL.node = nil + } + case 296: + yyDollar = yyS[yypt-0 : yypt+1] + //line go.y:1980 + { + yyVAL.list = nil + } + case 298: + yyDollar = yyS[yypt-0 : yypt+1] + //line go.y:1986 + { + yyVAL.list = nil + } + case 300: + yyDollar = yyS[yypt-0 : yypt+1] + //line go.y:1992 + { + yyVAL.list = nil + } + case 302: + yyDollar = yyS[yypt-0 : yypt+1] + //line go.y:1998 + { + yyVAL.val.Ctype = CTxxx + } + case 304: + yyDollar = yyS[yypt-4 : yypt+1] + //line go.y:2008 + { + importimport(yyDollar[2].sym, yyDollar[3].val.U.(string)) + } + case 305: + yyDollar = yyS[yypt-4 : yypt+1] + //line go.y:2012 + { + importvar(yyDollar[2].sym, yyDollar[3].typ) + } + case 306: + yyDollar = yyS[yypt-5 : yypt+1] + //line go.y:2016 + { + importconst(yyDollar[2].sym, Types[TIDEAL], yyDollar[4].node) + } + case 307: + yyDollar = yyS[yypt-6 : yypt+1] + //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:2024 + { + importtype(yyDollar[2].typ, yyDollar[3].typ) + } + case 309: + yyDollar = yyS[yypt-4 : yypt+1] + //line go.y:2028 + { + if yyDollar[2].node == nil { + dclcontext = PEXTERN // since we skip the funcbody below + break + } + + yyDollar[2].node.Func.Inl = yyDollar[3].list + + funcbody(yyDollar[2].node) + importlist = list(importlist, yyDollar[2].node) + + if Debug['E'] > 0 { + fmt.Printf("import [%q] func %v \n", importpkg.Path, yyDollar[2].node) + if Debug['m'] > 2 && yyDollar[2].node.Func.Inl != nil { + fmt.Printf("inl body:%v\n", yyDollar[2].node.Func.Inl) + } + } + } + case 310: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:2049 + { + yyVAL.sym = yyDollar[1].sym + structpkg = yyVAL.sym.Pkg + } + case 311: + yyDollar = yyS[yypt-1 : yypt+1] + //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:2076 + { + yyVAL.typ = pkgtype(yyDollar[1].sym) + } + case 318: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:2080 + { + // predefined name like uint8 + yyDollar[1].sym = Pkglookup(yyDollar[1].sym.Name, builtinpkg) + if yyDollar[1].sym.Def == nil || yyDollar[1].sym.Def.Op != OTYPE { + Yyerror("%s is not a type", yyDollar[1].sym.Name) + yyVAL.typ = nil + } else { + yyVAL.typ = yyDollar[1].sym.Def.Type + } + } + case 319: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:2091 + { + yyVAL.typ = aindex(nil, yyDollar[3].typ) + } + case 320: + yyDollar = yyS[yypt-4 : yypt+1] + //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:2099 + { + yyVAL.typ = maptype(yyDollar[3].typ, yyDollar[5].typ) + } + case 322: + yyDollar = yyS[yypt-4 : yypt+1] + //line go.y:2103 + { + yyVAL.typ = tostruct(yyDollar[3].list) + } + case 323: + yyDollar = yyS[yypt-4 : yypt+1] + //line go.y:2107 + { + yyVAL.typ = tointerface(yyDollar[3].list) + } + case 324: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:2111 + { + yyVAL.typ = Ptrto(yyDollar[2].typ) + } + case 325: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:2115 + { + yyVAL.typ = typ(TCHAN) + yyVAL.typ.Type = yyDollar[2].typ + yyVAL.typ.Chan = Cboth + } + case 326: + yyDollar = yyS[yypt-4 : yypt+1] + //line go.y:2121 + { + yyVAL.typ = typ(TCHAN) + yyVAL.typ.Type = yyDollar[3].typ + yyVAL.typ.Chan = Cboth + } + case 327: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:2127 + { + yyVAL.typ = typ(TCHAN) + yyVAL.typ.Type = yyDollar[3].typ + yyVAL.typ.Chan = Csend + } + case 328: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:2135 + { + yyVAL.typ = typ(TCHAN) + yyVAL.typ.Type = yyDollar[3].typ + yyVAL.typ.Chan = Crecv + } + case 329: + yyDollar = yyS[yypt-5 : yypt+1] + //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:2149 + { + yyVAL.node = Nod(ODCLFIELD, nil, typenod(yyDollar[2].typ)) + if yyDollar[1].sym != nil { + yyVAL.node.Left = newname(yyDollar[1].sym) + } + yyVAL.node.Val = yyDollar[3].val + } + case 331: + yyDollar = yyS[yypt-4 : yypt+1] + //line go.y:2157 + { + var t *Type + + t = typ(TARRAY) + t.Bound = -1 + t.Type = yyDollar[3].typ + + yyVAL.node = Nod(ODCLFIELD, nil, typenod(t)) + if yyDollar[1].sym != nil { + yyVAL.node.Left = newname(yyDollar[1].sym) + } + yyVAL.node.Isddd = true + yyVAL.node.Val = yyDollar[4].val + } + case 332: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:2174 + { + var s *Sym + var p *Pkg + + if yyDollar[1].sym != nil && yyDollar[1].sym.Name != "?" { + yyVAL.node = Nod(ODCLFIELD, newname(yyDollar[1].sym), typenod(yyDollar[2].typ)) + yyVAL.node.Val = yyDollar[3].val + } else { + s = yyDollar[2].typ.Sym + if s == nil && Isptr[yyDollar[2].typ.Etype] { + s = yyDollar[2].typ.Type.Sym + } + p = importpkg + if yyDollar[1].sym != nil { + p = yyDollar[1].sym.Pkg + } + yyVAL.node = embedded(s, p) + yyVAL.node.Right = typenod(yyDollar[2].typ) + yyVAL.node.Val = yyDollar[3].val + } + } + case 333: + yyDollar = yyS[yypt-5 : yypt+1] + //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:2202 + { + yyVAL.node = Nod(ODCLFIELD, nil, typenod(yyDollar[1].typ)) + } + case 335: + yyDollar = yyS[yypt-0 : yypt+1] + //line go.y:2207 + { + yyVAL.list = nil + } + case 337: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:2214 + { + yyVAL.list = yyDollar[2].list + } + case 338: + yyDollar = yyS[yypt-1 : yypt+1] + //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:2228 + { + yyVAL.node = nodlit(yyDollar[1].val) + } + case 340: + yyDollar = yyS[yypt-2 : yypt+1] + //line go.y:2232 + { + yyVAL.node = nodlit(yyDollar[2].val) + switch yyVAL.node.Val.Ctype { + case CTINT, CTRUNE: + mpnegfix(yyVAL.node.Val.U.(*Mpint)) + break + case CTFLT: + mpnegflt(yyVAL.node.Val.U.(*Mpflt)) + break + case CTCPLX: + mpnegflt(&yyVAL.node.Val.U.(*Mpcplx).Real) + mpnegflt(&yyVAL.node.Val.U.(*Mpcplx).Imag) + break + default: + Yyerror("bad negated constant") + } + } + case 341: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:2250 + { + yyVAL.node = oldname(Pkglookup(yyDollar[1].sym.Name, builtinpkg)) + if yyVAL.node.Op != OLITERAL { + Yyerror("bad constant %v", yyVAL.node.Sym) + } + } + case 343: + yyDollar = yyS[yypt-5 : yypt+1] + //line go.y:2260 + { + if yyDollar[2].node.Val.Ctype == CTRUNE && yyDollar[4].node.Val.Ctype == CTINT { + yyVAL.node = yyDollar[2].node + mpaddfixfix(yyDollar[2].node.Val.U.(*Mpint), yyDollar[4].node.Val.U.(*Mpint), 0) + break + } + 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: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:2276 + { + yyVAL.list = list1(yyDollar[1].node) + } + case 347: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:2280 + { + yyVAL.list = list(yyDollar[1].list, yyDollar[3].node) + } + case 348: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:2286 + { + yyVAL.list = list1(yyDollar[1].node) + } + case 349: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:2290 + { + yyVAL.list = list(yyDollar[1].list, yyDollar[3].node) + } + case 350: + yyDollar = yyS[yypt-1 : yypt+1] + //line go.y:2296 + { + yyVAL.list = list1(yyDollar[1].node) + } + case 351: + yyDollar = yyS[yypt-3 : yypt+1] + //line go.y:2300 + { + yyVAL.list = list(yyDollar[1].list, yyDollar[3].node) + } + } + goto yystack /* stack new state and value */ +} diff --git a/src/cmd/compile/internal/gc/y.output b/src/cmd/compile/internal/gc/y.output new file mode 100644 index 0000000000..2821702aea --- /dev/null +++ b/src/cmd/compile/internal/gc/y.output @@ -0,0 +1,10411 @@ + +state 0 + $accept: .file $end + $$4: . (4) + + . reduce 4 (src line 210) + + file goto 1 + loadsys goto 2 + $$4 goto 3 + +state 1 + $accept: file.$end + + $end accept + . error + + +state 2 + file: loadsys.package imports xdcl_list + package: . (2) + + LPACKAGE shift 5 + . reduce 2 (src line 193) + + package goto 4 + +state 3 + loadsys: $$4.import_package import_there + + LPACKAGE shift 7 + . error + + import_package goto 6 + +state 4 + file: loadsys package.imports xdcl_list + imports: . (6) + + . reduce 6 (src line 227) + + imports goto 8 + +state 5 + package: LPACKAGE.sym ';' + + LNAME shift 10 + '?' shift 12 + '@' shift 13 + . error + + sym goto 9 + hidden_importsym goto 11 + +state 6 + loadsys: $$4 import_package.import_there + $$21: . (21) + + . reduce 21 (src line 334) + + import_there goto 14 + $$21 goto 15 + +state 7 + import_package: LPACKAGE.LNAME import_safety ';' + + LNAME shift 16 + . error + + +state 8 + file: loadsys package imports.xdcl_list + imports: imports.import ';' + xdcl_list: . (218) + + LIMPORT shift 19 + . reduce 218 (src line 1569) + + xdcl_list goto 17 + import goto 18 + +state 9 + package: LPACKAGE sym.';' + + ';' shift 20 + . error + + +state 10 + sym: LNAME. (157) + + . reduce 157 (src line 1175) + + +state 11 + sym: hidden_importsym. (158) + + . reduce 158 (src line 1184) + + +state 12 + sym: '?'. (159) + + . reduce 159 (src line 1185) + + +state 13 + hidden_importsym: '@'.LLITERAL '.' LNAME + hidden_importsym: '@'.LLITERAL '.' '?' + + LLITERAL shift 21 + . error + + +state 14 + loadsys: $$4 import_package import_there. (5) + + . reduce 5 (src line 221) + + +state 15 + import_there: $$21.hidden_import_list '$' '$' + hidden_import_list: . (344) + + . reduce 344 (src line 2271) + + hidden_import_list goto 22 + +state 16 + import_package: LPACKAGE LNAME.import_safety ';' + import_safety: . (19) + + LNAME shift 24 + . reduce 19 (src line 326) + + import_safety goto 23 + +state 17 + file: loadsys package imports xdcl_list. (1) + xdcl_list: xdcl_list.xdcl ';' + xdcl: . (23) + + $end reduce 1 (src line 184) + error shift 29 + LLITERAL shift 68 + LBREAK shift 41 + LCHAN shift 78 + LCONST shift 47 + LCONTINUE shift 42 + LDEFER shift 44 + LFALL shift 40 + LFOR shift 50 + LFUNC shift 33 + LGO shift 43 + LGOTO shift 45 + LIF shift 53 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LRETURN shift 46 + LSELECT shift 52 + LSTRUCT shift 82 + LSWITCH shift 51 + LTYPE shift 32 + LVAR shift 30 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + ';' reduce 23 (src line 347) + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 56 + expr goto 48 + fnliteral goto 73 + for_stmt goto 35 + if_stmt goto 38 + non_dcl_stmt goto 28 + labelname goto 39 + name goto 69 + new_name goto 54 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + select_stmt goto 37 + simple_stmt goto 34 + switch_stmt goto 36 + uexpr goto 55 + xfndcl goto 27 + xdcl goto 25 + expr_list goto 49 + common_dcl goto 26 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + lconst goto 31 + fnlitdcl goto 76 + +state 18 + imports: imports import.';' + + ';' shift 84 + . error + + +state 19 + import: LIMPORT.import_stmt + import: LIMPORT.'(' import_stmt_list osemi ')' + import: LIMPORT.'(' ')' + + LLITERAL shift 88 + LNAME shift 10 + '(' shift 86 + '.' shift 90 + '?' shift 12 + '@' shift 13 + . error + + import_here goto 87 + sym goto 89 + hidden_importsym goto 11 + import_stmt goto 85 + +state 20 + package: LPACKAGE sym ';'. (3) + + . reduce 3 (src line 200) + + +state 21 + hidden_importsym: '@' LLITERAL.'.' LNAME + hidden_importsym: '@' LLITERAL.'.' '?' + + '.' shift 91 + . error + + +state 22 + import_there: $$21 hidden_import_list.'$' '$' + hidden_import_list: hidden_import_list.hidden_import + + LCONST shift 96 + LFUNC shift 98 + LIMPORT shift 94 + LTYPE shift 97 + LVAR shift 95 + '$' shift 92 + . error + + hidden_import goto 93 + +state 23 + import_package: LPACKAGE LNAME import_safety.';' + + ';' shift 99 + . error + + +state 24 + import_safety: LNAME. (20) + + . reduce 20 (src line 327) + + +state 25 + xdcl_list: xdcl_list xdcl.';' + + ';' shift 100 + . error + + +state 26 + xdcl: common_dcl. (24) + + . reduce 24 (src line 352) + + +state 27 + xdcl: xfndcl. (25) + + . reduce 25 (src line 353) + + +state 28 + xdcl: non_dcl_stmt. (26) + + . reduce 26 (src line 357) + + +state 29 + xdcl: error. (27) + + . reduce 27 (src line 362) + + +state 30 + common_dcl: LVAR.vardcl + common_dcl: LVAR.'(' vardcl_list osemi ')' + common_dcl: LVAR.'(' ')' + + LNAME shift 10 + '(' shift 102 + '?' shift 12 + '@' shift 13 + . error + + sym goto 105 + dcl_name goto 104 + dcl_name_list goto 103 + vardcl goto 101 + hidden_importsym goto 11 + +state 31 + common_dcl: lconst.constdcl + common_dcl: lconst.'(' constdcl osemi ')' + common_dcl: lconst.'(' constdcl ';' constdcl_list osemi ')' + common_dcl: lconst.'(' ')' + + LNAME shift 10 + '(' shift 107 + '?' shift 12 + '@' shift 13 + . error + + sym goto 105 + dcl_name goto 104 + dcl_name_list goto 108 + constdcl goto 106 + hidden_importsym goto 11 + +state 32 + common_dcl: LTYPE.typedcl + common_dcl: LTYPE.'(' typedcl_list osemi ')' + common_dcl: LTYPE.'(' ')' + + LNAME shift 10 + '(' shift 110 + '?' shift 12 + '@' shift 13 + . error + + sym goto 112 + typedclname goto 111 + typedcl goto 109 + hidden_importsym goto 11 + +state 33 + xfndcl: LFUNC.fndcl fnbody + fntype: LFUNC.'(' oarg_type_list_ocomma ')' fnres + + LNAME shift 10 + '(' shift 114 + '?' shift 12 + '@' shift 13 + . error + + sym goto 115 + fndcl goto 113 + hidden_importsym goto 11 + +state 34 + non_dcl_stmt: simple_stmt. (256) + + . reduce 256 (src line 1796) + + +state 35 + non_dcl_stmt: for_stmt. (257) + + . reduce 257 (src line 1798) + + +state 36 + non_dcl_stmt: switch_stmt. (258) + + . reduce 258 (src line 1799) + + +state 37 + non_dcl_stmt: select_stmt. (259) + + . reduce 259 (src line 1800) + + +state 38 + non_dcl_stmt: if_stmt. (260) + + . reduce 260 (src line 1801) + + +state 39 + non_dcl_stmt: labelname.':' $$261 stmt + + ':' shift 116 + . error + + +state 40 + non_dcl_stmt: LFALL. (263) + + . reduce 263 (src line 1818) + + +state 41 + non_dcl_stmt: LBREAK.onew_name + onew_name: . (155) + + LNAME shift 10 + '?' shift 12 + '@' shift 13 + . reduce 155 (src line 1169) + + sym goto 119 + new_name goto 118 + onew_name goto 117 + hidden_importsym goto 11 + +state 42 + non_dcl_stmt: LCONTINUE.onew_name + onew_name: . (155) + + LNAME shift 10 + '?' shift 12 + '@' shift 13 + . reduce 155 (src line 1169) + + sym goto 119 + new_name goto 118 + onew_name goto 120 + hidden_importsym goto 11 + +state 43 + non_dcl_stmt: LGO.pseudocall + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + '(' shift 67 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + fnliteral goto 73 + name goto 69 + pexpr goto 122 + pexpr_no_paren goto 66 + pseudocall goto 121 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 44 + non_dcl_stmt: LDEFER.pseudocall + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + '(' shift 67 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + fnliteral goto 73 + name goto 69 + pexpr goto 122 + pexpr_no_paren goto 66 + pseudocall goto 125 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 45 + non_dcl_stmt: LGOTO.new_name + + LNAME shift 10 + '?' shift 12 + '@' shift 13 + . error + + sym goto 119 + new_name goto 126 + hidden_importsym goto 11 + +state 46 + non_dcl_stmt: LRETURN.oexpr_list + oexpr_list: . (292) + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 292 (src line 1967) + + sym goto 123 + expr goto 129 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + expr_list goto 128 + oexpr_list goto 127 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 47 + lconst: LCONST. (38) + + . reduce 38 (src line 416) + + +state 48 + simple_stmt: expr. (49) + simple_stmt: expr.LASOP expr + simple_stmt: expr.LINC + simple_stmt: expr.LDEC + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + expr_list: expr. (276) + + LASOP shift 130 + LCOLAS reduce 276 (src line 1902) + LANDAND shift 134 + LANDNOT shift 149 + LCOMM shift 152 + LDEC shift 132 + LEQ shift 135 + LGE shift 139 + LGT shift 140 + LINC shift 131 + LLE shift 138 + LLSH shift 150 + LLT shift 137 + LNE shift 136 + LOROR shift 133 + LRSH shift 151 + '+' shift 141 + '-' shift 142 + '|' shift 143 + '^' shift 144 + '*' shift 145 + '/' shift 146 + '%' shift 147 + '&' shift 148 + '=' reduce 276 (src line 1902) + ',' reduce 276 (src line 1902) + . reduce 49 (src line 472) + + +state 49 + simple_stmt: expr_list.'=' expr_list + simple_stmt: expr_list.LCOLAS expr_list + expr_list: expr_list.',' expr + + LCOLAS shift 154 + '=' shift 153 + ',' shift 155 + . error + + +state 50 + for_stmt: LFOR.$$74 for_body + $$74: . (74) + + . reduce 74 (src line 721) + + $$74 goto 156 + +state 51 + switch_stmt: LSWITCH.$$88 if_header $$89 LBODY caseblock_list '}' + $$88: . (88) + + . reduce 88 (src line 816) + + $$88 goto 157 + +state 52 + select_stmt: LSELECT.$$91 LBODY caseblock_list '}' + $$91: . (91) + + . reduce 91 (src line 839) + + $$91 goto 158 + +state 53 + if_stmt: LIF.$$78 if_header $$79 loop_body $$80 elseif_list else + $$78: . (78) + + . reduce 78 (src line 750) + + $$78 goto 159 + +state 54 + labelname: new_name. (163) + + . reduce 163 (src line 1229) + + +state 55 + expr: uexpr. (93) + + . reduce 93 (src line 855) + + +state 56 + new_name: sym. (153) + name: sym. (162) + + ':' reduce 153 (src line 1153) + . reduce 162 (src line 1220) + + +state 57 + uexpr: pexpr. (114) + pseudocall: pexpr.'(' ')' + pseudocall: pexpr.'(' expr_or_type_list ocomma ')' + pseudocall: pexpr.'(' expr_or_type_list LDDD ocomma ')' + pexpr_no_paren: pexpr.'.' sym + pexpr_no_paren: pexpr.'.' '(' expr_or_type ')' + pexpr_no_paren: pexpr.'.' '(' LTYPE ')' + pexpr_no_paren: pexpr.'[' expr ']' + pexpr_no_paren: pexpr.'[' oexpr ':' oexpr ']' + pexpr_no_paren: pexpr.'[' oexpr ':' oexpr ':' oexpr ']' + + '(' shift 160 + '.' shift 161 + '[' shift 162 + . reduce 114 (src line 939) + + +state 58 + uexpr: '*'.uexpr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 163 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 59 + uexpr: '&'.uexpr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 164 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 60 + uexpr: '+'.uexpr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 165 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 61 + uexpr: '-'.uexpr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 166 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 62 + uexpr: '!'.uexpr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 167 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 63 + uexpr: '~'.uexpr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 168 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 64 + uexpr: '^'.uexpr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 169 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 65 + uexpr: LCOMM.uexpr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 170 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 66 + pexpr_no_paren: pexpr_no_paren.'{' start_complit braced_keyval_list '}' + pexpr: pexpr_no_paren. (146) + + '{' shift 171 + . reduce 146 (src line 1116) + + +state 67 + pexpr_no_paren: '('.expr_or_type ')' '{' start_complit braced_keyval_list '}' + pexpr: '('.expr_or_type ')' + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 179 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 178 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 173 + expr_or_type goto 172 + fnliteral goto 73 + name goto 69 + non_expr_type goto 174 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + recvchantype goto 175 + othertype goto 177 + fntype goto 176 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 68 + pexpr_no_paren: LLITERAL. (126) + + . reduce 126 (src line 1003) + + +state 69 + pexpr_no_paren: name. (127) + + . reduce 127 (src line 1008) + + +state 70 + pexpr_no_paren: pseudocall. (134) + + . reduce 134 (src line 1046) + + +state 71 + pexpr_no_paren: convtype.'(' expr ocomma ')' + + '(' shift 180 + . error + + +state 72 + pexpr_no_paren: comptype.lbrace start_complit braced_keyval_list '}' + + LBODY shift 182 + '{' shift 183 + . error + + lbrace goto 181 + +state 73 + pexpr_no_paren: fnliteral. (139) + + . reduce 139 (src line 1073) + + +state 74 + convtype: fntype. (181) + fnlitdcl: fntype. (215) + + '(' reduce 181 (src line 1282) + . reduce 215 (src line 1546) + + +state 75 + convtype: othertype. (182) + comptype: othertype. (183) + + '(' reduce 182 (src line 1284) + . reduce 183 (src line 1286) + + +state 76 + fnliteral: fnlitdcl.lbrace stmt_list '}' + fnliteral: fnlitdcl.error + + error shift 185 + LBODY shift 182 + '{' shift 183 + . error + + lbrace goto 184 + +state 77 + othertype: '['.oexpr ']' ntype + othertype: '['.LDDD ']' ntype + oexpr: . (290) + + LLITERAL shift 68 + LCHAN shift 78 + LDDD shift 187 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 290 (src line 1961) + + sym goto 123 + expr goto 188 + fnliteral goto 73 + name goto 69 + oexpr goto 186 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 78 + othertype: LCHAN.non_recvchantype + othertype: LCHAN.LCOMM ntype + + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 190 + '*' shift 196 + '(' shift 195 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + dotname goto 194 + name goto 197 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 193 + non_recvchantype goto 189 + othertype goto 192 + fntype goto 191 + hidden_importsym goto 11 + +state 79 + othertype: LMAP.'[' ntype ']' ntype + + '[' shift 198 + . error + + +state 80 + othertype: structtype. (196) + + . reduce 196 (src line 1334) + + +state 81 + othertype: interfacetype. (197) + + . reduce 197 (src line 1335) + + +state 82 + structtype: LSTRUCT.lbrace structdcl_list osemi '}' + structtype: LSTRUCT.lbrace '}' + + LBODY shift 182 + '{' shift 183 + . error + + lbrace goto 199 + +state 83 + interfacetype: LINTERFACE.lbrace interfacedcl_list osemi '}' + interfacetype: LINTERFACE.lbrace '}' + + LBODY shift 182 + '{' shift 183 + . error + + lbrace goto 200 + +state 84 + imports: imports import ';'. (7) + + . reduce 7 (src line 228) + + +state 85 + import: LIMPORT import_stmt. (8) + + . reduce 8 (src line 230) + + +state 86 + import: LIMPORT '('.import_stmt_list osemi ')' + import: LIMPORT '('.')' + + LLITERAL shift 88 + LNAME shift 10 + ')' shift 202 + '.' shift 90 + '?' shift 12 + '@' shift 13 + . error + + import_here goto 87 + sym goto 89 + hidden_importsym goto 11 + import_stmt goto 203 + import_stmt_list goto 201 + +state 87 + import_stmt: import_here.import_package import_there + import_stmt: import_here.import_there + $$21: . (21) + + LPACKAGE shift 7 + . reduce 21 (src line 334) + + import_package goto 204 + import_there goto 205 + $$21 goto 15 + +state 88 + import_here: LLITERAL. (15) + + . reduce 15 (src line 286) + + +state 89 + import_here: sym.LLITERAL + + LLITERAL shift 206 + . error + + +state 90 + import_here: '.'.LLITERAL + + LLITERAL shift 207 + . error + + +state 91 + hidden_importsym: '@' LLITERAL '.'.LNAME + hidden_importsym: '@' LLITERAL '.'.'?' + + LNAME shift 208 + '?' shift 209 + . error + + +state 92 + import_there: $$21 hidden_import_list '$'.'$' + + '$' shift 210 + . error + + +state 93 + hidden_import_list: hidden_import_list hidden_import. (345) + + . reduce 345 (src line 2272) + + +state 94 + hidden_import: LIMPORT.LNAME LLITERAL ';' + + LNAME shift 211 + . error + + +state 95 + hidden_import: LVAR.hidden_pkg_importsym hidden_type ';' + + '@' shift 13 + . error + + hidden_importsym goto 213 + hidden_pkg_importsym goto 212 + +state 96 + hidden_import: LCONST.hidden_pkg_importsym '=' hidden_constant ';' + hidden_import: LCONST.hidden_pkg_importsym hidden_type '=' hidden_constant ';' + + '@' shift 13 + . error + + hidden_importsym goto 213 + hidden_pkg_importsym goto 214 + +state 97 + hidden_import: LTYPE.hidden_pkgtype hidden_type ';' + + '@' shift 13 + . error + + hidden_importsym goto 213 + hidden_pkg_importsym goto 216 + hidden_pkgtype goto 215 + +state 98 + hidden_import: LFUNC.hidden_fndcl fnbody ';' + + '(' shift 219 + '@' shift 13 + . error + + hidden_fndcl goto 217 + hidden_importsym goto 213 + hidden_pkg_importsym goto 218 + +state 99 + import_package: LPACKAGE LNAME import_safety ';'. (18) + + . reduce 18 (src line 309) + + +state 100 + xdcl_list: xdcl_list xdcl ';'. (219) + + . reduce 219 (src line 1573) + + +state 101 + common_dcl: LVAR vardcl. (28) + + . reduce 28 (src line 367) + + +state 102 + common_dcl: LVAR '('.vardcl_list osemi ')' + common_dcl: LVAR '('.')' + + LNAME shift 10 + ')' shift 221 + '?' shift 12 + '@' shift 13 + . error + + sym goto 105 + dcl_name goto 104 + dcl_name_list goto 103 + vardcl goto 222 + vardcl_list goto 220 + hidden_importsym goto 11 + +state 103 + vardcl: dcl_name_list.ntype + vardcl: dcl_name_list.ntype '=' expr_list + vardcl: dcl_name_list.'=' expr_list + dcl_name_list: dcl_name_list.',' dcl_name + + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 232 + '*' shift 196 + '(' shift 231 + '=' shift 224 + '[' shift 77 + '?' shift 12 + '@' shift 13 + ',' shift 225 + . error + + sym goto 123 + ntype goto 223 + dotname goto 230 + name goto 197 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 229 + recvchantype goto 226 + othertype goto 228 + fntype goto 227 + hidden_importsym goto 11 + +state 104 + dcl_name_list: dcl_name. (274) + + . reduce 274 (src line 1892) + + +state 105 + dcl_name: sym. (154) + + . reduce 154 (src line 1163) + + +state 106 + common_dcl: lconst constdcl. (31) + + . reduce 31 (src line 380) + + +state 107 + common_dcl: lconst '('.constdcl osemi ')' + common_dcl: lconst '('.constdcl ';' constdcl_list osemi ')' + common_dcl: lconst '('.')' + + LNAME shift 10 + ')' shift 234 + '?' shift 12 + '@' shift 13 + . error + + sym goto 105 + dcl_name goto 104 + dcl_name_list goto 108 + constdcl goto 233 + hidden_importsym goto 11 + +state 108 + constdcl: dcl_name_list.ntype '=' expr_list + constdcl: dcl_name_list.'=' expr_list + dcl_name_list: dcl_name_list.',' dcl_name + + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 232 + '*' shift 196 + '(' shift 231 + '=' shift 236 + '[' shift 77 + '?' shift 12 + '@' shift 13 + ',' shift 225 + . error + + sym goto 123 + ntype goto 235 + dotname goto 230 + name goto 197 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 229 + recvchantype goto 226 + othertype goto 228 + fntype goto 227 + hidden_importsym goto 11 + +state 109 + common_dcl: LTYPE typedcl. (35) + + . reduce 35 (src line 403) + + +state 110 + common_dcl: LTYPE '('.typedcl_list osemi ')' + common_dcl: LTYPE '('.')' + + LNAME shift 10 + ')' shift 238 + '?' shift 12 + '@' shift 13 + . error + + sym goto 112 + typedclname goto 111 + typedcl goto 239 + typedcl_list goto 237 + hidden_importsym goto 11 + +state 111 + typedcl: typedclname.ntype + + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 232 + '*' shift 196 + '(' shift 231 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + ntype goto 240 + dotname goto 230 + name goto 197 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 229 + recvchantype goto 226 + othertype goto 228 + fntype goto 227 + hidden_importsym goto 11 + +state 112 + typedclname: sym. (47) + + . reduce 47 (src line 457) + + +state 113 + xfndcl: LFUNC fndcl.fnbody + fnbody: . (210) + + '{' shift 242 + . reduce 210 (src line 1519) + + fnbody goto 241 + +state 114 + fndcl: '('.oarg_type_list_ocomma ')' sym '(' oarg_type_list_ocomma ')' fnres + fntype: LFUNC '('.oarg_type_list_ocomma ')' fnres + oarg_type_list_ocomma: . (249) + + LCHAN shift 78 + LDDD shift 250 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 232 + '*' shift 196 + '(' shift 231 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 249 (src line 1769) + + sym goto 247 + ntype goto 249 + arg_type goto 245 + dotname goto 230 + name goto 197 + name_or_type goto 246 + oarg_type_list_ocomma goto 243 + arg_type_list goto 244 + dotdotdot goto 248 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 229 + recvchantype goto 226 + othertype goto 228 + fntype goto 227 + hidden_importsym goto 11 + +state 115 + fndcl: sym.'(' oarg_type_list_ocomma ')' fnres + + '(' shift 251 + . error + + +state 116 + non_dcl_stmt: labelname ':'.$$261 stmt + $$261: . (261) + + . reduce 261 (src line 1802) + + $$261 goto 252 + +state 117 + non_dcl_stmt: LBREAK onew_name. (264) + + . reduce 264 (src line 1824) + + +state 118 + onew_name: new_name. (156) + + . reduce 156 (src line 1173) + + +state 119 + new_name: sym. (153) + + . reduce 153 (src line 1153) + + +state 120 + non_dcl_stmt: LCONTINUE onew_name. (265) + + . reduce 265 (src line 1828) + + +state 121 + pexpr_no_paren: pseudocall. (134) + non_dcl_stmt: LGO pseudocall. (266) + + '(' 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 + pseudocall: pexpr.'(' ')' + pseudocall: pexpr.'(' expr_or_type_list ocomma ')' + pseudocall: pexpr.'(' expr_or_type_list LDDD ocomma ')' + pexpr_no_paren: pexpr.'.' sym + pexpr_no_paren: pexpr.'.' '(' expr_or_type ')' + pexpr_no_paren: pexpr.'.' '(' LTYPE ')' + pexpr_no_paren: pexpr.'[' expr ']' + pexpr_no_paren: pexpr.'[' oexpr ':' oexpr ']' + pexpr_no_paren: pexpr.'[' oexpr ':' oexpr ':' oexpr ']' + + '(' shift 160 + '.' shift 161 + '[' shift 162 + . error + + +state 123 + name: sym. (162) + + . reduce 162 (src line 1220) + + +state 124 + fntype: LFUNC.'(' oarg_type_list_ocomma ')' fnres + + '(' shift 253 + . error + + +state 125 + pexpr_no_paren: pseudocall. (134) + non_dcl_stmt: LDEFER pseudocall. (267) + + '(' 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 1840) + + +state 127 + non_dcl_stmt: LRETURN oexpr_list. (269) + + . reduce 269 (src line 1845) + + +state 128 + expr_list: expr_list.',' expr + oexpr_list: expr_list. (293) + + ',' shift 155 + . reduce 293 (src line 1971) + + +state 129 + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + expr_list: expr. (276) + + LANDAND shift 134 + LANDNOT shift 149 + LCOMM shift 152 + LEQ shift 135 + LGE shift 139 + LGT shift 140 + LLE shift 138 + LLSH shift 150 + LLT shift 137 + LNE shift 136 + LOROR shift 133 + LRSH shift 151 + '+' shift 141 + '-' shift 142 + '|' shift 143 + '^' shift 144 + '*' shift 145 + '/' shift 146 + '%' shift 147 + '&' shift 148 + . reduce 276 (src line 1902) + + +state 130 + simple_stmt: expr LASOP.expr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 254 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 131 + simple_stmt: expr LINC. (53) + + . reduce 53 (src line 522) + + +state 132 + simple_stmt: expr LDEC. (54) + + . reduce 54 (src line 528) + + +state 133 + expr: expr LOROR.expr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 255 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 134 + expr: expr LANDAND.expr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 256 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 135 + expr: expr LEQ.expr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 257 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 136 + expr: expr LNE.expr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 258 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 137 + expr: expr LLT.expr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 259 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 138 + expr: expr LLE.expr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 260 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 139 + expr: expr LGE.expr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 261 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 140 + expr: expr LGT.expr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 262 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 141 + expr: expr '+'.expr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 263 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 142 + expr: expr '-'.expr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 264 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 143 + expr: expr '|'.expr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 265 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 144 + expr: expr '^'.expr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 266 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 145 + expr: expr '*'.expr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 267 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 146 + expr: expr '/'.expr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 268 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 147 + expr: expr '%'.expr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 269 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 148 + expr: expr '&'.expr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 270 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 149 + expr: expr LANDNOT.expr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 271 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 150 + expr: expr LLSH.expr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 272 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 151 + expr: expr LRSH.expr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 273 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 152 + expr: expr LCOMM.expr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 274 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 153 + simple_stmt: expr_list '='.expr_list + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 129 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + expr_list goto 275 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 154 + simple_stmt: expr_list LCOLAS.expr_list + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 129 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + expr_list goto 276 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 155 + expr_list: expr_list ','.expr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 277 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 156 + for_stmt: LFOR $$74.for_body + osimple_stmt: . (294) + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LRANGE shift 284 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 294 (src line 1973) + + sym goto 123 + expr goto 48 + fnliteral goto 73 + for_body goto 278 + for_header goto 279 + name goto 69 + osimple_stmt goto 280 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + range_stmt goto 281 + simple_stmt goto 282 + uexpr goto 55 + expr_list goto 283 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 157 + switch_stmt: LSWITCH $$88.if_header $$89 LBODY caseblock_list '}' + osimple_stmt: . (294) + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 294 (src line 1973) + + sym goto 123 + expr goto 48 + fnliteral goto 73 + if_header goto 285 + name goto 69 + osimple_stmt goto 286 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + simple_stmt goto 282 + uexpr goto 55 + expr_list goto 49 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 158 + select_stmt: LSELECT $$91.LBODY caseblock_list '}' + + LBODY shift 287 + . error + + +state 159 + if_stmt: LIF $$78.if_header $$79 loop_body $$80 elseif_list else + osimple_stmt: . (294) + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 294 (src line 1973) + + sym goto 123 + expr goto 48 + fnliteral goto 73 + if_header goto 288 + name goto 69 + osimple_stmt goto 286 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + simple_stmt goto 282 + uexpr goto 55 + expr_list goto 49 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 160 + pseudocall: pexpr '('.')' + pseudocall: pexpr '('.expr_or_type_list ocomma ')' + pseudocall: pexpr '('.expr_or_type_list LDDD ocomma ')' + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 179 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 178 + '&' shift 59 + '(' shift 67 + ')' shift 289 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 173 + expr_or_type goto 291 + fnliteral goto 73 + name goto 69 + non_expr_type goto 174 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + expr_or_type_list goto 290 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + recvchantype goto 175 + othertype goto 177 + fntype goto 176 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 161 + pexpr_no_paren: pexpr '.'.sym + pexpr_no_paren: pexpr '.'.'(' expr_or_type ')' + pexpr_no_paren: pexpr '.'.'(' LTYPE ')' + + LNAME shift 10 + '(' shift 293 + '?' shift 12 + '@' shift 13 + . error + + sym goto 292 + hidden_importsym goto 11 + +state 162 + pexpr_no_paren: pexpr '['.expr ']' + pexpr_no_paren: pexpr '['.oexpr ':' oexpr ']' + pexpr_no_paren: pexpr '['.oexpr ':' oexpr ':' oexpr ']' + oexpr: . (290) + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 290 (src line 1961) + + sym goto 123 + expr goto 294 + fnliteral goto 73 + name goto 69 + oexpr goto 295 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 163 + uexpr: '*' uexpr. (115) + + . reduce 115 (src line 941) + + +state 164 + uexpr: '&' uexpr. (116) + + . reduce 116 (src line 945) + + +state 165 + uexpr: '+' uexpr. (117) + + . reduce 117 (src line 956) + + +state 166 + uexpr: '-' uexpr. (118) + + . reduce 118 (src line 960) + + +state 167 + uexpr: '!' uexpr. (119) + + . reduce 119 (src line 964) + + +state 168 + uexpr: '~' uexpr. (120) + + . reduce 120 (src line 968) + + +state 169 + uexpr: '^' uexpr. (121) + + . reduce 121 (src line 973) + + +state 170 + uexpr: LCOMM uexpr. (122) + + . 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 1075) + + start_complit goto 296 + +state 172 + pexpr_no_paren: '(' expr_or_type.')' '{' start_complit braced_keyval_list '}' + pexpr: '(' expr_or_type.')' + + ')' shift 297 + . error + + +state 173 + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + expr_or_type: expr. (148) + + LANDAND shift 134 + LANDNOT shift 149 + LCOMM shift 152 + LEQ shift 135 + LGE shift 139 + LGT shift 140 + LLE shift 138 + LLSH shift 150 + LLT shift 137 + LNE shift 136 + LOROR shift 133 + LRSH shift 151 + '+' shift 141 + '-' shift 142 + '|' shift 143 + '^' shift 144 + '*' shift 145 + '/' shift 146 + '%' shift 147 + '&' shift 148 + . reduce 148 (src line 1131) + + +state 174 + expr_or_type: non_expr_type. (149) + + . reduce 149 (src line 1133) + + +state 175 + non_expr_type: recvchantype. (172) + + . reduce 172 (src line 1263) + + +state 176 + non_expr_type: fntype. (173) + convtype: fntype. (181) + fnlitdcl: fntype. (215) + + 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 + non_expr_type: othertype. (174) + convtype: othertype. (182) + comptype: othertype. (183) + + LBODY reduce 183 (src line 1286) + '(' reduce 182 (src line 1284) + '{' reduce 183 (src line 1286) + . reduce 174 (src line 1266) + + +state 178 + uexpr: '*'.uexpr + non_expr_type: '*'.non_expr_type + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 179 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 178 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + fnliteral goto 73 + name goto 69 + non_expr_type goto 298 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 163 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + recvchantype goto 175 + othertype goto 177 + fntype goto 176 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 179 + uexpr: LCOMM.uexpr + recvchantype: LCOMM.LCHAN ntype + + LLITERAL shift 68 + LCHAN shift 299 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 170 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 180 + pexpr_no_paren: convtype '('.expr ocomma ')' + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 300 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 181 + pexpr_no_paren: comptype lbrace.start_complit braced_keyval_list '}' + start_complit: . (140) + + . reduce 140 (src line 1075) + + start_complit goto 301 + +state 182 + lbrace: LBODY. (151) + + . reduce 151 (src line 1138) + + +state 183 + lbrace: '{'. (152) + + . reduce 152 (src line 1143) + + +state 184 + fnliteral: fnlitdcl lbrace.stmt_list '}' + stmt: . (251) + + error shift 307 + LLITERAL shift 68 + LBREAK shift 41 + LCHAN shift 78 + LCONST shift 47 + LCONTINUE shift 42 + LDEFER shift 44 + LFALL shift 40 + LFOR shift 50 + LFUNC shift 124 + LGO shift 43 + LGOTO shift 45 + LIF shift 53 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LRETURN shift 46 + LSELECT shift 52 + LSTRUCT shift 82 + LSWITCH shift 51 + LTYPE shift 32 + LVAR shift 30 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + ';' reduce 251 (src line 1781) + '{' shift 308 + '}' reduce 251 (src line 1781) + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 56 + stmt goto 303 + compound_stmt goto 304 + expr goto 48 + fnliteral goto 73 + for_stmt goto 35 + if_stmt goto 38 + non_dcl_stmt goto 306 + labelname goto 39 + name goto 69 + new_name goto 54 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + select_stmt goto 37 + simple_stmt goto 34 + switch_stmt goto 36 + uexpr goto 55 + expr_list goto 49 + stmt_list goto 302 + common_dcl goto 305 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + lconst goto 31 + fnlitdcl goto 76 + +state 185 + fnliteral: fnlitdcl error. (217) + + . reduce 217 (src line 1558) + + +state 186 + othertype: '[' oexpr.']' ntype + + ']' shift 309 + . error + + +state 187 + othertype: '[' LDDD.']' ntype + + ']' shift 310 + . error + + +state 188 + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + oexpr: expr. (291) + + LANDAND shift 134 + LANDNOT shift 149 + LCOMM shift 152 + LEQ shift 135 + LGE shift 139 + LGT shift 140 + LLE shift 138 + LLSH shift 150 + LLT shift 137 + LNE shift 136 + LOROR shift 133 + LRSH shift 151 + '+' shift 141 + '-' shift 142 + '|' shift 143 + '^' shift 144 + '*' shift 145 + '/' shift 146 + '%' shift 147 + '&' shift 148 + . reduce 291 (src line 1965) + + +state 189 + othertype: LCHAN non_recvchantype. (193) + + . reduce 193 (src line 1320) + + +state 190 + othertype: LCHAN LCOMM.ntype + + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 232 + '*' shift 196 + '(' shift 231 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + ntype goto 311 + dotname goto 230 + name goto 197 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 229 + recvchantype goto 226 + othertype goto 228 + fntype goto 227 + hidden_importsym goto 11 + +state 191 + non_recvchantype: fntype. (176) + + . reduce 176 (src line 1272) + + +state 192 + non_recvchantype: othertype. (177) + + . reduce 177 (src line 1274) + + +state 193 + non_recvchantype: ptrtype. (178) + + . reduce 178 (src line 1275) + + +state 194 + non_recvchantype: dotname. (179) + + . reduce 179 (src line 1276) + + +state 195 + non_recvchantype: '('.ntype ')' + + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 232 + '*' shift 196 + '(' shift 231 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + ntype goto 312 + dotname goto 230 + name goto 197 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 229 + recvchantype goto 226 + othertype goto 228 + fntype goto 227 + hidden_importsym goto 11 + +state 196 + ptrtype: '*'.ntype + + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 232 + '*' shift 196 + '(' shift 231 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + ntype goto 313 + dotname goto 230 + name goto 197 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 229 + recvchantype goto 226 + othertype goto 228 + fntype goto 227 + hidden_importsym goto 11 + +state 197 + dotname: name. (189) + dotname: name.'.' sym + + '.' shift 314 + . reduce 189 (src line 1296) + + +state 198 + othertype: LMAP '['.ntype ']' ntype + + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 232 + '*' shift 196 + '(' shift 231 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + ntype goto 315 + dotname goto 230 + name goto 197 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 229 + recvchantype goto 226 + othertype goto 228 + fntype goto 227 + hidden_importsym goto 11 + +state 199 + structtype: LSTRUCT lbrace.structdcl_list osemi '}' + structtype: LSTRUCT lbrace.'}' + + LNAME shift 325 + '*' shift 322 + '(' shift 321 + '}' shift 317 + '?' shift 12 + '@' shift 13 + . error + + sym goto 119 + packname goto 324 + embed goto 320 + new_name goto 323 + new_name_list goto 319 + structdcl goto 318 + structdcl_list goto 316 + hidden_importsym goto 11 + +state 200 + interfacetype: LINTERFACE lbrace.interfacedcl_list osemi '}' + interfacetype: LINTERFACE lbrace.'}' + + LNAME shift 325 + '(' shift 331 + '}' shift 327 + '?' shift 12 + '@' shift 13 + . error + + sym goto 119 + packname goto 330 + interfacedcl goto 328 + new_name goto 329 + interfacedcl_list goto 326 + hidden_importsym goto 11 + +state 201 + import: LIMPORT '(' import_stmt_list.osemi ')' + import_stmt_list: import_stmt_list.';' import_stmt + osemi: . (286) + + ';' shift 333 + . reduce 286 (src line 1955) + + osemi goto 332 + +state 202 + import: LIMPORT '(' ')'. (10) + + . reduce 10 (src line 233) + + +state 203 + import_stmt_list: import_stmt. (13) + + . reduce 13 (src line 282) + + +state 204 + import_stmt: import_here import_package.import_there + $$21: . (21) + + . reduce 21 (src line 334) + + import_there goto 334 + $$21 goto 15 + +state 205 + import_stmt: import_here import_there. (12) + + . reduce 12 (src line 271) + + +state 206 + import_here: sym LLITERAL. (16) + + . reduce 16 (src line 294) + + +state 207 + import_here: '.' LLITERAL. (17) + + . reduce 17 (src line 301) + + +state 208 + hidden_importsym: '@' LLITERAL '.' LNAME. (160) + + . reduce 160 (src line 1190) + + +state 209 + hidden_importsym: '@' LLITERAL '.' '?'. (161) + + . reduce 161 (src line 1205) + + +state 210 + import_there: $$21 hidden_import_list '$' '$'. (22) + + . reduce 22 (src line 338) + + +state 211 + hidden_import: LIMPORT LNAME.LLITERAL ';' + + LLITERAL shift 335 + . error + + +state 212 + hidden_import: LVAR hidden_pkg_importsym.hidden_type ';' + + LCHAN shift 347 + LFUNC shift 349 + LINTERFACE shift 345 + LMAP shift 343 + LNAME shift 341 + LSTRUCT shift 344 + LCOMM shift 348 + '*' shift 346 + '[' shift 342 + '@' shift 13 + . error + + hidden_importsym goto 340 + hidden_type goto 336 + hidden_type_misc goto 337 + hidden_type_func goto 339 + hidden_type_recv_chan goto 338 + +state 213 + hidden_pkg_importsym: hidden_importsym. (310) + + . reduce 310 (src line 2047) + + +state 214 + hidden_import: LCONST hidden_pkg_importsym.'=' hidden_constant ';' + hidden_import: LCONST hidden_pkg_importsym.hidden_type '=' hidden_constant ';' + + LCHAN shift 347 + LFUNC shift 349 + LINTERFACE shift 345 + LMAP shift 343 + LNAME shift 341 + LSTRUCT shift 344 + LCOMM shift 348 + '*' shift 346 + '=' shift 350 + '[' shift 342 + '@' shift 13 + . error + + hidden_importsym goto 340 + hidden_type goto 351 + hidden_type_misc goto 337 + hidden_type_func goto 339 + hidden_type_recv_chan goto 338 + +state 215 + hidden_import: LTYPE hidden_pkgtype.hidden_type ';' + + LCHAN shift 347 + LFUNC shift 349 + LINTERFACE shift 345 + LMAP shift 343 + LNAME shift 341 + LSTRUCT shift 344 + LCOMM shift 348 + '*' shift 346 + '[' shift 342 + '@' shift 13 + . error + + hidden_importsym goto 340 + hidden_type goto 352 + hidden_type_misc goto 337 + hidden_type_func goto 339 + hidden_type_recv_chan goto 338 + +state 216 + hidden_pkgtype: hidden_pkg_importsym. (311) + + . reduce 311 (src line 2054) + + +state 217 + hidden_import: LFUNC hidden_fndcl.fnbody ';' + fnbody: . (210) + + '{' shift 242 + . reduce 210 (src line 1519) + + fnbody goto 353 + +state 218 + hidden_fndcl: hidden_pkg_importsym.'(' ohidden_funarg_list ')' ohidden_funres + + '(' shift 354 + . error + + +state 219 + hidden_fndcl: '('.hidden_funarg_list ')' sym '(' ohidden_funarg_list ')' ohidden_funres + + LNAME shift 10 + '?' shift 12 + '@' shift 13 + . error + + sym goto 357 + hidden_importsym goto 11 + hidden_funarg goto 356 + hidden_funarg_list goto 355 + +state 220 + common_dcl: LVAR '(' vardcl_list.osemi ')' + vardcl_list: vardcl_list.';' vardcl + osemi: . (286) + + ';' shift 359 + . reduce 286 (src line 1955) + + osemi goto 358 + +state 221 + common_dcl: LVAR '(' ')'. (30) + + . reduce 30 (src line 376) + + +state 222 + vardcl_list: vardcl. (220) + + . reduce 220 (src line 1585) + + +state 223 + vardcl: dcl_name_list ntype. (39) + vardcl: dcl_name_list ntype.'=' expr_list + + '=' shift 360 + . reduce 39 (src line 422) + + +state 224 + vardcl: dcl_name_list '='.expr_list + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 129 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + expr_list goto 361 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 225 + dcl_name_list: dcl_name_list ','.dcl_name + + LNAME shift 10 + '?' shift 12 + '@' shift 13 + . error + + sym goto 105 + dcl_name goto 362 + hidden_importsym goto 11 + +state 226 + ntype: recvchantype. (166) + + . reduce 166 (src line 1252) + + +state 227 + ntype: fntype. (167) + + . reduce 167 (src line 1254) + + +state 228 + ntype: othertype. (168) + + . reduce 168 (src line 1255) + + +state 229 + ntype: ptrtype. (169) + + . reduce 169 (src line 1256) + + +state 230 + ntype: dotname. (170) + + . reduce 170 (src line 1257) + + +state 231 + ntype: '('.ntype ')' + + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 232 + '*' shift 196 + '(' shift 231 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + ntype goto 363 + dotname goto 230 + name goto 197 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 229 + recvchantype goto 226 + othertype goto 228 + fntype goto 227 + hidden_importsym goto 11 + +state 232 + recvchantype: LCOMM.LCHAN ntype + + LCHAN shift 364 + . error + + +state 233 + common_dcl: lconst '(' constdcl.osemi ')' + common_dcl: lconst '(' constdcl.';' constdcl_list osemi ')' + osemi: . (286) + + ';' shift 366 + . reduce 286 (src line 1955) + + osemi goto 365 + +state 234 + common_dcl: lconst '(' ')'. (34) + + . reduce 34 (src line 398) + + +state 235 + constdcl: dcl_name_list ntype.'=' expr_list + + '=' shift 367 + . error + + +state 236 + constdcl: dcl_name_list '='.expr_list + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 129 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + expr_list goto 368 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 237 + common_dcl: LTYPE '(' typedcl_list.osemi ')' + typedcl_list: typedcl_list.';' typedcl + osemi: . (286) + + ';' shift 370 + . reduce 286 (src line 1955) + + osemi goto 369 + +state 238 + common_dcl: LTYPE '(' ')'. (37) + + . reduce 37 (src line 411) + + +state 239 + typedcl_list: typedcl. (224) + + . reduce 224 (src line 1599) + + +state 240 + typedcl: typedclname ntype. (48) + + . reduce 48 (src line 466) + + +state 241 + xfndcl: LFUNC fndcl fnbody. (204) + + . reduce 204 (src line 1380) + + +state 242 + fnbody: '{'.stmt_list '}' + stmt: . (251) + + error shift 307 + LLITERAL shift 68 + LBREAK shift 41 + LCHAN shift 78 + LCONST shift 47 + LCONTINUE shift 42 + LDEFER shift 44 + LFALL shift 40 + LFOR shift 50 + LFUNC shift 124 + LGO shift 43 + LGOTO shift 45 + LIF shift 53 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LRETURN shift 46 + LSELECT shift 52 + LSTRUCT shift 82 + LSWITCH shift 51 + LTYPE shift 32 + LVAR shift 30 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + ';' reduce 251 (src line 1781) + '{' shift 308 + '}' reduce 251 (src line 1781) + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 56 + stmt goto 303 + compound_stmt goto 304 + expr goto 48 + fnliteral goto 73 + for_stmt goto 35 + if_stmt goto 38 + non_dcl_stmt goto 306 + labelname goto 39 + name goto 69 + new_name goto 54 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + select_stmt goto 37 + simple_stmt goto 34 + switch_stmt goto 36 + uexpr goto 55 + expr_list goto 49 + stmt_list goto 371 + common_dcl goto 305 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + lconst goto 31 + fnlitdcl goto 76 + +state 243 + fndcl: '(' oarg_type_list_ocomma.')' sym '(' oarg_type_list_ocomma ')' fnres + fntype: LFUNC '(' oarg_type_list_ocomma.')' fnres + + ')' shift 372 + . error + + +state 244 + arg_type_list: arg_type_list.',' arg_type + oarg_type_list_ocomma: arg_type_list.ocomma + ocomma: . (288) + + ',' shift 373 + . reduce 288 (src line 1958) + + ocomma goto 374 + +state 245 + arg_type_list: arg_type. (247) + + . reduce 247 (src line 1759) + + +state 246 + arg_type: name_or_type. (243) + + . reduce 243 (src line 1743) + + +state 247 + name: sym. (162) + arg_type: sym.name_or_type + arg_type: sym.dotdotdot + + LCHAN shift 78 + LDDD shift 250 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 232 + '*' shift 196 + '(' shift 231 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 162 (src line 1220) + + sym goto 123 + ntype goto 249 + dotname goto 230 + name goto 197 + name_or_type goto 375 + dotdotdot goto 376 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 229 + recvchantype goto 226 + othertype goto 228 + fntype goto 227 + hidden_importsym goto 11 + +state 248 + arg_type: dotdotdot. (246) + + . reduce 246 (src line 1757) + + +state 249 + name_or_type: ntype. (150) + + . reduce 150 (src line 1135) + + +state 250 + dotdotdot: LDDD. (164) + dotdotdot: LDDD.ntype + + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 232 + '*' shift 196 + '(' shift 231 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 164 (src line 1241) + + sym goto 123 + ntype goto 377 + dotname goto 230 + name goto 197 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 229 + recvchantype goto 226 + othertype goto 228 + fntype goto 227 + hidden_importsym goto 11 + +state 251 + fndcl: sym '('.oarg_type_list_ocomma ')' fnres + oarg_type_list_ocomma: . (249) + + LCHAN shift 78 + LDDD shift 250 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 232 + '*' shift 196 + '(' shift 231 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 249 (src line 1769) + + sym goto 247 + ntype goto 249 + arg_type goto 245 + dotname goto 230 + name goto 197 + name_or_type goto 246 + oarg_type_list_ocomma goto 378 + arg_type_list goto 244 + dotdotdot goto 248 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 229 + recvchantype goto 226 + othertype goto 228 + fntype goto 227 + hidden_importsym goto 11 + +state 252 + non_dcl_stmt: labelname ':' $$261.stmt + stmt: . (251) + + error shift 307 + LLITERAL shift 68 + LBREAK shift 41 + LCASE reduce 251 (src line 1781) + LCHAN shift 78 + LCONST shift 47 + LCONTINUE shift 42 + LDEFAULT reduce 251 (src line 1781) + LDEFER shift 44 + LFALL shift 40 + LFOR shift 50 + LFUNC shift 124 + LGO shift 43 + LGOTO shift 45 + LIF shift 53 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LRETURN shift 46 + LSELECT shift 52 + LSTRUCT shift 82 + LSWITCH shift 51 + LTYPE shift 32 + LVAR shift 30 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + ';' reduce 251 (src line 1781) + '{' shift 308 + '}' reduce 251 (src line 1781) + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 56 + stmt goto 379 + compound_stmt goto 304 + expr goto 48 + fnliteral goto 73 + for_stmt goto 35 + if_stmt goto 38 + non_dcl_stmt goto 306 + labelname goto 39 + name goto 69 + new_name goto 54 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + select_stmt goto 37 + simple_stmt goto 34 + switch_stmt goto 36 + uexpr goto 55 + expr_list goto 49 + common_dcl goto 305 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + lconst goto 31 + fnlitdcl goto 76 + +state 253 + fntype: LFUNC '('.oarg_type_list_ocomma ')' fnres + oarg_type_list_ocomma: . (249) + + LCHAN shift 78 + LDDD shift 250 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 232 + '*' shift 196 + '(' shift 231 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 249 (src line 1769) + + sym goto 247 + ntype goto 249 + arg_type goto 245 + dotname goto 230 + name goto 197 + name_or_type goto 246 + oarg_type_list_ocomma goto 380 + arg_type_list goto 244 + dotdotdot goto 248 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 229 + recvchantype goto 226 + othertype goto 228 + fntype goto 227 + hidden_importsym goto 11 + +state 254 + simple_stmt: expr LASOP expr. (50) + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + + LANDAND shift 134 + LANDNOT shift 149 + LCOMM shift 152 + LEQ shift 135 + LGE shift 139 + LGT shift 140 + LLE shift 138 + LLSH shift 150 + LLT shift 137 + LNE shift 136 + LOROR shift 133 + LRSH shift 151 + '+' shift 141 + '-' shift 142 + '|' shift 143 + '^' shift 144 + '*' shift 145 + '/' shift 146 + '%' shift 147 + '&' shift 148 + . reduce 50 (src line 487) + + +state 255 + expr: expr.LOROR expr + expr: expr LOROR expr. (94) + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + + LANDAND shift 134 + LANDNOT shift 149 + LEQ shift 135 + LGE shift 139 + LGT shift 140 + LLE shift 138 + LLSH shift 150 + LLT shift 137 + LNE shift 136 + LRSH shift 151 + '+' shift 141 + '-' shift 142 + '|' shift 143 + '^' shift 144 + '*' shift 145 + '/' shift 146 + '%' shift 147 + '&' shift 148 + . reduce 94 (src line 857) + + +state 256 + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr LANDAND expr. (95) + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + + LANDNOT shift 149 + LEQ shift 135 + LGE shift 139 + LGT shift 140 + LLE shift 138 + LLSH shift 150 + LLT shift 137 + LNE shift 136 + LRSH shift 151 + '+' shift 141 + '-' shift 142 + '|' shift 143 + '^' shift 144 + '*' shift 145 + '/' shift 146 + '%' shift 147 + '&' shift 148 + . reduce 95 (src line 861) + + +state 257 + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr LEQ expr. (96) + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + + LANDNOT shift 149 + LLSH shift 150 + LRSH shift 151 + '+' shift 141 + '-' shift 142 + '|' shift 143 + '^' shift 144 + '*' shift 145 + '/' shift 146 + '%' shift 147 + '&' shift 148 + . reduce 96 (src line 865) + + +state 258 + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr LNE expr. (97) + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + + LANDNOT shift 149 + LLSH shift 150 + LRSH shift 151 + '+' shift 141 + '-' shift 142 + '|' shift 143 + '^' shift 144 + '*' shift 145 + '/' shift 146 + '%' shift 147 + '&' shift 148 + . reduce 97 (src line 869) + + +state 259 + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr LLT expr. (98) + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + + LANDNOT shift 149 + LLSH shift 150 + LRSH shift 151 + '+' shift 141 + '-' shift 142 + '|' shift 143 + '^' shift 144 + '*' shift 145 + '/' shift 146 + '%' shift 147 + '&' shift 148 + . reduce 98 (src line 873) + + +state 260 + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr LLE expr. (99) + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + + LANDNOT shift 149 + LLSH shift 150 + LRSH shift 151 + '+' shift 141 + '-' shift 142 + '|' shift 143 + '^' shift 144 + '*' shift 145 + '/' shift 146 + '%' shift 147 + '&' shift 148 + . reduce 99 (src line 877) + + +state 261 + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr LGE expr. (100) + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + + LANDNOT shift 149 + LLSH shift 150 + LRSH shift 151 + '+' shift 141 + '-' shift 142 + '|' shift 143 + '^' shift 144 + '*' shift 145 + '/' shift 146 + '%' shift 147 + '&' shift 148 + . reduce 100 (src line 881) + + +state 262 + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr LGT expr. (101) + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + + LANDNOT shift 149 + LLSH shift 150 + LRSH shift 151 + '+' shift 141 + '-' shift 142 + '|' shift 143 + '^' shift 144 + '*' shift 145 + '/' shift 146 + '%' shift 147 + '&' shift 148 + . reduce 101 (src line 885) + + +state 263 + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr '+' expr. (102) + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + + LANDNOT shift 149 + LLSH shift 150 + LRSH shift 151 + '*' shift 145 + '/' shift 146 + '%' shift 147 + '&' shift 148 + . reduce 102 (src line 889) + + +state 264 + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr '-' expr. (103) + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + + LANDNOT shift 149 + LLSH shift 150 + LRSH shift 151 + '*' shift 145 + '/' shift 146 + '%' shift 147 + '&' shift 148 + . reduce 103 (src line 893) + + +state 265 + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr '|' expr. (104) + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + + LANDNOT shift 149 + LLSH shift 150 + LRSH shift 151 + '*' shift 145 + '/' shift 146 + '%' shift 147 + '&' shift 148 + . reduce 104 (src line 897) + + +state 266 + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr '^' expr. (105) + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + + LANDNOT shift 149 + LLSH shift 150 + LRSH shift 151 + '*' shift 145 + '/' shift 146 + '%' shift 147 + '&' shift 148 + . reduce 105 (src line 901) + + +state 267 + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr '*' expr. (106) + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + + . reduce 106 (src line 905) + + +state 268 + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr '/' expr. (107) + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + + . reduce 107 (src line 909) + + +state 269 + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr '%' expr. (108) + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + + . reduce 108 (src line 913) + + +state 270 + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr '&' expr. (109) + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + + . reduce 109 (src line 917) + + +state 271 + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr LANDNOT expr. (110) + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + + . reduce 110 (src line 921) + + +state 272 + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr LLSH expr. (111) + expr: expr.LRSH expr + expr: expr.LCOMM expr + + . reduce 111 (src line 925) + + +state 273 + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr LRSH expr. (112) + expr: expr.LCOMM expr + + . reduce 112 (src line 929) + + +state 274 + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + expr: expr LCOMM expr. (113) + + LANDAND shift 134 + LANDNOT shift 149 + LEQ shift 135 + LGE shift 139 + LGT shift 140 + LLE shift 138 + LLSH shift 150 + LLT shift 137 + LNE shift 136 + LOROR shift 133 + LRSH shift 151 + '+' shift 141 + '-' shift 142 + '|' shift 143 + '^' shift 144 + '*' shift 145 + '/' shift 146 + '%' shift 147 + '&' shift 148 + . reduce 113 (src line 934) + + +state 275 + simple_stmt: expr_list '=' expr_list. (51) + expr_list: expr_list.',' expr + + ',' shift 155 + . reduce 51 (src line 492) + + +state 276 + simple_stmt: expr_list LCOLAS expr_list. (52) + expr_list: expr_list.',' expr + + ',' shift 155 + . reduce 52 (src line 504) + + +state 277 + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + expr_list: expr_list ',' expr. (277) + + LANDAND shift 134 + LANDNOT shift 149 + LCOMM shift 152 + LEQ shift 135 + LGE shift 139 + LGT shift 140 + LLE shift 138 + LLSH shift 150 + LLT shift 137 + LNE shift 136 + LOROR shift 133 + LRSH shift 151 + '+' shift 141 + '-' shift 142 + '|' shift 143 + '^' shift 144 + '*' shift 145 + '/' shift 146 + '%' shift 147 + '&' shift 148 + . reduce 277 (src line 1907) + + +state 278 + for_stmt: LFOR $$74 for_body. (75) + + . reduce 75 (src line 726) + + +state 279 + for_body: for_header.loop_body + + LBODY shift 382 + . error + + loop_body goto 381 + +state 280 + for_header: osimple_stmt.';' osimple_stmt ';' osimple_stmt + for_header: osimple_stmt. (71) + + ';' shift 383 + . reduce 71 (src line 706) + + +state 281 + for_header: range_stmt. (72) + + . reduce 72 (src line 712) + + +state 282 + osimple_stmt: simple_stmt. (295) + + . reduce 295 (src line 1977) + + +state 283 + simple_stmt: expr_list.'=' expr_list + simple_stmt: expr_list.LCOLAS expr_list + range_stmt: expr_list.'=' LRANGE expr + range_stmt: expr_list.LCOLAS LRANGE expr + expr_list: expr_list.',' expr + + LCOLAS shift 385 + '=' shift 384 + ',' shift 155 + . error + + +state 284 + range_stmt: LRANGE.expr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 386 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 285 + switch_stmt: LSWITCH $$88 if_header.$$89 LBODY caseblock_list '}' + $$89: . (89) + + . reduce 89 (src line 821) + + $$89 goto 387 + +state 286 + if_header: osimple_stmt. (76) + if_header: osimple_stmt.';' osimple_stmt + + ';' shift 388 + . reduce 76 (src line 732) + + +state 287 + select_stmt: LSELECT $$91 LBODY.caseblock_list '}' + caseblock_list: . (63) + + . reduce 63 (src line 652) + + caseblock_list goto 389 + +state 288 + if_stmt: LIF $$78 if_header.$$79 loop_body $$80 elseif_list else + $$79: . (79) + + . reduce 79 (src line 755) + + $$79 goto 390 + +state 289 + pseudocall: pexpr '(' ')'. (123) + + . reduce 123 (src line 986) + + +state 290 + pseudocall: pexpr '(' expr_or_type_list.ocomma ')' + pseudocall: pexpr '(' expr_or_type_list.LDDD ocomma ')' + expr_or_type_list: expr_or_type_list.',' expr_or_type + ocomma: . (288) + + LDDD shift 392 + ',' shift 393 + . reduce 288 (src line 1958) + + ocomma goto 391 + +state 291 + expr_or_type_list: expr_or_type. (278) + + . reduce 278 (src line 1912) + + +state 292 + pexpr_no_paren: pexpr '.' sym. (128) + + . reduce 128 (src line 1009) + + +state 293 + pexpr_no_paren: pexpr '.' '('.expr_or_type ')' + pexpr_no_paren: pexpr '.' '('.LTYPE ')' + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LTYPE shift 395 + LCOMM shift 179 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 178 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 173 + expr_or_type goto 394 + fnliteral goto 73 + name goto 69 + non_expr_type goto 174 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + recvchantype goto 175 + othertype goto 177 + fntype goto 176 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 294 + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + pexpr_no_paren: pexpr '[' expr.']' + oexpr: expr. (291) + + LANDAND shift 134 + LANDNOT shift 149 + LCOMM shift 152 + LEQ shift 135 + LGE shift 139 + LGT shift 140 + LLE shift 138 + LLSH shift 150 + LLT shift 137 + LNE shift 136 + LOROR shift 133 + LRSH shift 151 + '+' shift 141 + '-' shift 142 + '|' shift 143 + '^' shift 144 + '*' shift 145 + '/' shift 146 + '%' shift 147 + '&' shift 148 + ']' shift 396 + . reduce 291 (src line 1965) + + +state 295 + pexpr_no_paren: pexpr '[' oexpr.':' oexpr ']' + pexpr_no_paren: pexpr '[' oexpr.':' oexpr ':' oexpr ']' + + ':' shift 397 + . error + + +state 296 + pexpr_no_paren: pexpr_no_paren '{' start_complit.braced_keyval_list '}' + braced_keyval_list: . (284) + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '{' shift 403 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 284 (src line 1943) + + sym goto 123 + expr goto 402 + bare_complitexpr goto 401 + fnliteral goto 73 + keyval goto 400 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + keyval_list goto 399 + braced_keyval_list goto 398 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 297 + pexpr_no_paren: '(' expr_or_type ')'.'{' start_complit braced_keyval_list '}' + pexpr: '(' expr_or_type ')'. (147) + + '{' shift 404 + . reduce 147 (src line 1118) + + +state 298 + non_expr_type: '*' non_expr_type. (175) + + . reduce 175 (src line 1267) + + +state 299 + othertype: LCHAN.non_recvchantype + othertype: LCHAN.LCOMM ntype + recvchantype: LCOMM LCHAN.ntype + + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 405 + '*' shift 196 + '(' shift 411 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + ntype goto 406 + dotname goto 410 + name goto 197 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 409 + recvchantype goto 226 + non_recvchantype goto 189 + othertype goto 408 + fntype goto 407 + hidden_importsym goto 11 + +state 300 + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + pexpr_no_paren: convtype '(' expr.ocomma ')' + ocomma: . (288) + + LANDAND shift 134 + LANDNOT shift 149 + LCOMM shift 152 + LEQ shift 135 + LGE shift 139 + LGT shift 140 + LLE shift 138 + LLSH shift 150 + LLT shift 137 + LNE shift 136 + LOROR shift 133 + LRSH shift 151 + '+' shift 141 + '-' shift 142 + '|' shift 143 + '^' shift 144 + '*' shift 145 + '/' shift 146 + '%' shift 147 + '&' shift 148 + ',' shift 413 + . reduce 288 (src line 1958) + + ocomma goto 412 + +state 301 + pexpr_no_paren: comptype lbrace start_complit.braced_keyval_list '}' + braced_keyval_list: . (284) + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '{' shift 403 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 284 (src line 1943) + + sym goto 123 + expr goto 402 + bare_complitexpr goto 401 + fnliteral goto 73 + keyval goto 400 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + keyval_list goto 399 + braced_keyval_list goto 414 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 302 + fnliteral: fnlitdcl lbrace stmt_list.'}' + stmt_list: stmt_list.';' stmt + + ';' shift 416 + '}' shift 415 + . error + + +state 303 + stmt_list: stmt. (270) + + . reduce 270 (src line 1866) + + +state 304 + stmt: compound_stmt. (252) + + . reduce 252 (src line 1785) + + +state 305 + stmt: common_dcl. (253) + + . reduce 253 (src line 1786) + + +state 306 + stmt: non_dcl_stmt. (254) + + . reduce 254 (src line 1790) + + +state 307 + stmt: error. (255) + + . reduce 255 (src line 1791) + + +state 308 + compound_stmt: '{'.$$59 stmt_list '}' + $$59: . (59) + + . reduce 59 (src line 606) + + $$59 goto 417 + +state 309 + othertype: '[' oexpr ']'.ntype + + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 232 + '*' shift 196 + '(' shift 231 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + ntype goto 418 + dotname goto 230 + name goto 197 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 229 + recvchantype goto 226 + othertype goto 228 + fntype goto 227 + hidden_importsym goto 11 + +state 310 + othertype: '[' LDDD ']'.ntype + + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 232 + '*' shift 196 + '(' shift 231 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + ntype goto 419 + dotname goto 230 + name goto 197 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 229 + recvchantype goto 226 + othertype goto 228 + fntype goto 227 + hidden_importsym goto 11 + +state 311 + othertype: LCHAN LCOMM ntype. (194) + + . reduce 194 (src line 1325) + + +state 312 + non_recvchantype: '(' ntype.')' + + ')' shift 420 + . error + + +state 313 + ptrtype: '*' ntype. (198) + + . reduce 198 (src line 1337) + + +state 314 + dotname: name '.'.sym + + LNAME shift 10 + '?' shift 12 + '@' shift 13 + . error + + sym goto 421 + hidden_importsym goto 11 + +state 315 + othertype: LMAP '[' ntype.']' ntype + + ']' shift 422 + . error + + +state 316 + structtype: LSTRUCT lbrace structdcl_list.osemi '}' + structdcl_list: structdcl_list.';' structdcl + osemi: . (286) + + ';' shift 424 + . reduce 286 (src line 1955) + + osemi goto 423 + +state 317 + structtype: LSTRUCT lbrace '}'. (201) + + . reduce 201 (src line 1357) + + +state 318 + structdcl_list: structdcl. (226) + + . reduce 226 (src line 1609) + + +state 319 + structdcl: new_name_list.ntype oliteral + new_name_list: new_name_list.',' new_name + + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 232 + '*' shift 196 + '(' shift 231 + '[' shift 77 + '?' shift 12 + '@' shift 13 + ',' shift 426 + . error + + sym goto 123 + ntype goto 425 + dotname goto 230 + name goto 197 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 229 + recvchantype goto 226 + othertype goto 228 + fntype goto 227 + hidden_importsym goto 11 + +state 320 + structdcl: embed.oliteral + oliteral: . (302) + + LLITERAL shift 428 + . reduce 302 (src line 1997) + + oliteral goto 427 + +state 321 + structdcl: '('.embed ')' oliteral + structdcl: '('.'*' embed ')' oliteral + + LNAME shift 431 + '*' shift 430 + . error + + packname goto 324 + embed goto 429 + +state 322 + structdcl: '*'.embed oliteral + structdcl: '*'.'(' embed ')' oliteral + + LNAME shift 431 + '(' shift 433 + . error + + packname goto 324 + embed goto 432 + +state 323 + new_name_list: new_name. (272) + + . reduce 272 (src line 1882) + + +state 324 + embed: packname. (238) + + . reduce 238 (src line 1708) + + +state 325 + sym: LNAME. (157) + packname: LNAME. (236) + packname: LNAME.'.' sym + + LLITERAL reduce 236 (src line 1683) + ';' reduce 236 (src line 1683) + '.' shift 434 + '}' reduce 236 (src line 1683) + . reduce 157 (src line 1175) + + +state 326 + interfacetype: LINTERFACE lbrace interfacedcl_list.osemi '}' + interfacedcl_list: interfacedcl_list.';' interfacedcl + osemi: . (286) + + ';' shift 436 + . reduce 286 (src line 1955) + + osemi goto 435 + +state 327 + interfacetype: LINTERFACE lbrace '}'. (203) + + . reduce 203 (src line 1370) + + +state 328 + interfacedcl_list: interfacedcl. (228) + + . reduce 228 (src line 1616) + + +state 329 + interfacedcl: new_name.indcl + + '(' shift 438 + . error + + indcl goto 437 + +state 330 + interfacedcl: packname. (240) + + . reduce 240 (src line 1720) + + +state 331 + interfacedcl: '('.packname ')' + + LNAME shift 431 + . error + + packname goto 439 + +state 332 + import: LIMPORT '(' import_stmt_list osemi.')' + + ')' shift 440 + . error + + +state 333 + import_stmt_list: import_stmt_list ';'.import_stmt + osemi: ';'. (287) + + LLITERAL shift 88 + LNAME shift 10 + '.' shift 90 + '?' shift 12 + '@' shift 13 + . reduce 287 (src line 1956) + + import_here goto 87 + sym goto 89 + hidden_importsym goto 11 + import_stmt goto 441 + +state 334 + import_stmt: import_here import_package import_there. (11) + + . reduce 11 (src line 235) + + +state 335 + hidden_import: LIMPORT LNAME LLITERAL.';' + + ';' shift 442 + . error + + +state 336 + hidden_import: LVAR hidden_pkg_importsym hidden_type.';' + + ';' shift 443 + . error + + +state 337 + hidden_type: hidden_type_misc. (312) + + . reduce 312 (src line 2065) + + +state 338 + hidden_type: hidden_type_recv_chan. (313) + + . reduce 313 (src line 2067) + + +state 339 + hidden_type: hidden_type_func. (314) + + . reduce 314 (src line 2068) + + +state 340 + hidden_type_misc: hidden_importsym. (317) + + . reduce 317 (src line 2074) + + +state 341 + hidden_type_misc: LNAME. (318) + + . reduce 318 (src line 2079) + + +state 342 + hidden_type_misc: '['.']' hidden_type + hidden_type_misc: '['.LLITERAL ']' hidden_type + + LLITERAL shift 445 + ']' shift 444 + . error + + +state 343 + hidden_type_misc: LMAP.'[' hidden_type ']' hidden_type + + '[' shift 446 + . error + + +state 344 + hidden_type_misc: LSTRUCT.'{' ohidden_structdcl_list '}' + + '{' shift 447 + . error + + +state 345 + hidden_type_misc: LINTERFACE.'{' ohidden_interfacedcl_list '}' + + '{' shift 448 + . error + + +state 346 + hidden_type_misc: '*'.hidden_type + + LCHAN shift 347 + LFUNC shift 349 + LINTERFACE shift 345 + LMAP shift 343 + LNAME shift 341 + LSTRUCT shift 344 + LCOMM shift 348 + '*' shift 346 + '[' shift 342 + '@' shift 13 + . error + + hidden_importsym goto 340 + hidden_type goto 449 + hidden_type_misc goto 337 + hidden_type_func goto 339 + hidden_type_recv_chan goto 338 + +state 347 + hidden_type_misc: LCHAN.hidden_type_non_recv_chan + hidden_type_misc: LCHAN.'(' hidden_type_recv_chan ')' + hidden_type_misc: LCHAN.LCOMM hidden_type + + LCHAN shift 347 + LFUNC shift 349 + LINTERFACE shift 345 + LMAP shift 343 + LNAME shift 341 + LSTRUCT shift 344 + LCOMM shift 452 + '*' shift 346 + '(' shift 451 + '[' shift 342 + '@' shift 13 + . error + + hidden_importsym goto 340 + hidden_type_misc goto 453 + hidden_type_func goto 454 + hidden_type_non_recv_chan goto 450 + +state 348 + hidden_type_recv_chan: LCOMM.LCHAN hidden_type + + LCHAN shift 455 + . error + + +state 349 + hidden_type_func: LFUNC.'(' ohidden_funarg_list ')' ohidden_funres + + '(' shift 456 + . error + + +state 350 + hidden_import: LCONST hidden_pkg_importsym '='.hidden_constant ';' + + LLITERAL shift 460 + LNAME shift 10 + '-' shift 461 + '(' shift 459 + '?' shift 12 + '@' shift 13 + . error + + sym goto 462 + hidden_importsym goto 11 + hidden_constant goto 457 + hidden_literal goto 458 + +state 351 + hidden_import: LCONST hidden_pkg_importsym hidden_type.'=' hidden_constant ';' + + '=' shift 463 + . error + + +state 352 + hidden_import: LTYPE hidden_pkgtype hidden_type.';' + + ';' shift 464 + . error + + +state 353 + hidden_import: LFUNC hidden_fndcl fnbody.';' + + ';' shift 465 + . error + + +state 354 + hidden_fndcl: hidden_pkg_importsym '('.ohidden_funarg_list ')' ohidden_funres + ohidden_funarg_list: . (296) + + LNAME shift 10 + '?' shift 12 + '@' shift 13 + . reduce 296 (src line 1979) + + sym goto 357 + hidden_importsym goto 11 + hidden_funarg goto 356 + hidden_funarg_list goto 467 + ohidden_funarg_list goto 466 + +state 355 + hidden_fndcl: '(' hidden_funarg_list.')' sym '(' ohidden_funarg_list ')' ohidden_funres + hidden_funarg_list: hidden_funarg_list.',' hidden_funarg + + ')' shift 468 + ',' shift 469 + . error + + +state 356 + hidden_funarg_list: hidden_funarg. (346) + + . reduce 346 (src line 2274) + + +state 357 + hidden_funarg: sym.hidden_type oliteral + hidden_funarg: sym.LDDD hidden_type oliteral + + LCHAN shift 347 + LDDD shift 471 + LFUNC shift 349 + LINTERFACE shift 345 + LMAP shift 343 + LNAME shift 341 + LSTRUCT shift 344 + LCOMM shift 348 + '*' shift 346 + '[' shift 342 + '@' shift 13 + . error + + hidden_importsym goto 340 + hidden_type goto 470 + hidden_type_misc goto 337 + hidden_type_func goto 339 + hidden_type_recv_chan goto 338 + +state 358 + common_dcl: LVAR '(' vardcl_list osemi.')' + + ')' shift 472 + . error + + +state 359 + vardcl_list: vardcl_list ';'.vardcl + osemi: ';'. (287) + + LNAME shift 10 + '?' shift 12 + '@' shift 13 + . reduce 287 (src line 1956) + + sym goto 105 + dcl_name goto 104 + dcl_name_list goto 103 + vardcl goto 473 + hidden_importsym goto 11 + +state 360 + vardcl: dcl_name_list ntype '='.expr_list + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 129 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + expr_list goto 474 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 361 + vardcl: dcl_name_list '=' expr_list. (41) + expr_list: expr_list.',' expr + + ',' shift 155 + . reduce 41 (src line 431) + + +state 362 + dcl_name_list: dcl_name_list ',' dcl_name. (275) + + . reduce 275 (src line 1897) + + +state 363 + ntype: '(' ntype.')' + + ')' shift 475 + . error + + +state 364 + recvchantype: LCOMM LCHAN.ntype + + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 232 + '*' shift 196 + '(' shift 231 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + ntype goto 406 + dotname goto 230 + name goto 197 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 229 + recvchantype goto 226 + othertype goto 228 + fntype goto 227 + hidden_importsym goto 11 + +state 365 + common_dcl: lconst '(' constdcl osemi.')' + + ')' shift 476 + . error + + +state 366 + common_dcl: lconst '(' constdcl ';'.constdcl_list osemi ')' + osemi: ';'. (287) + + LNAME shift 10 + '?' shift 12 + '@' shift 13 + . reduce 287 (src line 1956) + + sym goto 105 + dcl_name goto 104 + dcl_name_list goto 480 + constdcl goto 479 + constdcl1 goto 478 + constdcl_list goto 477 + hidden_importsym goto 11 + +state 367 + constdcl: dcl_name_list ntype '='.expr_list + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 129 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + expr_list goto 481 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 368 + constdcl: dcl_name_list '=' expr_list. (43) + expr_list: expr_list.',' expr + + ',' shift 155 + . reduce 43 (src line 441) + + +state 369 + common_dcl: LTYPE '(' typedcl_list osemi.')' + + ')' shift 482 + . error + + +state 370 + typedcl_list: typedcl_list ';'.typedcl + osemi: ';'. (287) + + LNAME shift 10 + '?' shift 12 + '@' shift 13 + . reduce 287 (src line 1956) + + sym goto 112 + typedclname goto 111 + typedcl goto 483 + hidden_importsym goto 11 + +state 371 + fnbody: '{' stmt_list.'}' + stmt_list: stmt_list.';' stmt + + ';' shift 416 + '}' shift 484 + . error + + +state 372 + fndcl: '(' oarg_type_list_ocomma ')'.sym '(' oarg_type_list_ocomma ')' fnres + fntype: LFUNC '(' oarg_type_list_ocomma ')'.fnres + fnres: . (212) + + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 232 + '*' shift 196 + '(' shift 488 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 212 (src line 1531) + + sym goto 485 + dotname goto 493 + name goto 197 + fnres goto 486 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 492 + recvchantype goto 489 + othertype goto 491 + fnret_type goto 487 + fntype goto 490 + hidden_importsym goto 11 + +state 373 + arg_type_list: arg_type_list ','.arg_type + ocomma: ','. (289) + + LCHAN shift 78 + LDDD shift 250 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 232 + '*' shift 196 + '(' shift 231 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 289 (src line 1959) + + sym goto 247 + ntype goto 249 + arg_type goto 494 + dotname goto 230 + name goto 197 + name_or_type goto 246 + dotdotdot goto 248 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 229 + recvchantype goto 226 + othertype goto 228 + fntype goto 227 + hidden_importsym goto 11 + +state 374 + oarg_type_list_ocomma: arg_type_list ocomma. (250) + + . reduce 250 (src line 1773) + + +state 375 + arg_type: sym name_or_type. (244) + + . reduce 244 (src line 1745) + + +state 376 + arg_type: sym dotdotdot. (245) + + . reduce 245 (src line 1751) + + +state 377 + dotdotdot: LDDD ntype. (165) + + . reduce 165 (src line 1247) + + +state 378 + fndcl: sym '(' oarg_type_list_ocomma.')' fnres + + ')' shift 495 + . error + + +state 379 + non_dcl_stmt: labelname ':' $$261 stmt. (262) + + . reduce 262 (src line 1807) + + +state 380 + fntype: LFUNC '(' oarg_type_list_ocomma.')' fnres + + ')' shift 496 + . error + + +state 381 + for_body: for_header loop_body. (73) + + . reduce 73 (src line 714) + + +state 382 + loop_body: LBODY.$$65 stmt_list '}' + $$65: . (65) + + . reduce 65 (src line 661) + + $$65 goto 497 + +state 383 + for_header: osimple_stmt ';'.osimple_stmt ';' osimple_stmt + osimple_stmt: . (294) + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 294 (src line 1973) + + sym goto 123 + expr goto 48 + fnliteral goto 73 + name goto 69 + osimple_stmt goto 498 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + simple_stmt goto 282 + uexpr goto 55 + expr_list goto 49 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 384 + simple_stmt: expr_list '='.expr_list + range_stmt: expr_list '='.LRANGE expr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LRANGE shift 499 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 129 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + expr_list goto 275 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 385 + simple_stmt: expr_list LCOLAS.expr_list + range_stmt: expr_list LCOLAS.LRANGE expr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LRANGE shift 500 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 129 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + expr_list goto 276 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 386 + range_stmt: LRANGE expr. (69) + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + + LANDAND shift 134 + LANDNOT shift 149 + LCOMM shift 152 + LEQ shift 135 + LGE shift 139 + LGT shift 140 + LLE shift 138 + LLSH shift 150 + LLT shift 137 + LNE shift 136 + LOROR shift 133 + LRSH shift 151 + '+' shift 141 + '-' shift 142 + '|' shift 143 + '^' shift 144 + '*' shift 145 + '/' shift 146 + '%' shift 147 + '&' shift 148 + . reduce 69 (src line 686) + + +state 387 + switch_stmt: LSWITCH $$88 if_header $$89.LBODY caseblock_list '}' + + LBODY shift 501 + . error + + +state 388 + if_header: osimple_stmt ';'.osimple_stmt + osimple_stmt: . (294) + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 294 (src line 1973) + + sym goto 123 + expr goto 48 + fnliteral goto 73 + name goto 69 + osimple_stmt goto 502 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + simple_stmt goto 282 + uexpr goto 55 + expr_list goto 49 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 389 + caseblock_list: caseblock_list.caseblock + select_stmt: LSELECT $$91 LBODY caseblock_list.'}' + + LCASE shift 506 + LDEFAULT shift 507 + '}' shift 504 + . error + + case goto 505 + caseblock goto 503 + +state 390 + if_stmt: LIF $$78 if_header $$79.loop_body $$80 elseif_list else + + LBODY shift 382 + . error + + loop_body goto 508 + +state 391 + pseudocall: pexpr '(' expr_or_type_list ocomma.')' + + ')' shift 509 + . error + + +state 392 + pseudocall: pexpr '(' expr_or_type_list LDDD.ocomma ')' + ocomma: . (288) + + ',' shift 413 + . reduce 288 (src line 1958) + + ocomma goto 510 + +state 393 + expr_or_type_list: expr_or_type_list ','.expr_or_type + ocomma: ','. (289) + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 179 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 178 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 289 (src line 1959) + + sym goto 123 + expr goto 173 + expr_or_type goto 511 + fnliteral goto 73 + name goto 69 + non_expr_type goto 174 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + recvchantype goto 175 + othertype goto 177 + fntype goto 176 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 394 + pexpr_no_paren: pexpr '.' '(' expr_or_type.')' + + ')' shift 512 + . error + + +state 395 + pexpr_no_paren: pexpr '.' '(' LTYPE.')' + + ')' shift 513 + . error + + +state 396 + pexpr_no_paren: pexpr '[' expr ']'. (131) + + . reduce 131 (src line 1028) + + +state 397 + pexpr_no_paren: pexpr '[' oexpr ':'.oexpr ']' + pexpr_no_paren: pexpr '[' oexpr ':'.oexpr ':' oexpr ']' + oexpr: . (290) + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 290 (src line 1961) + + sym goto 123 + expr goto 188 + fnliteral goto 73 + name goto 69 + oexpr goto 514 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 398 + pexpr_no_paren: pexpr_no_paren '{' start_complit braced_keyval_list.'}' + + '}' shift 515 + . error + + +state 399 + keyval_list: keyval_list.',' keyval + keyval_list: keyval_list.',' bare_complitexpr + braced_keyval_list: keyval_list.ocomma + ocomma: . (288) + + ',' shift 516 + . reduce 288 (src line 1958) + + ocomma goto 517 + +state 400 + keyval_list: keyval. (280) + + . reduce 280 (src line 1925) + + +state 401 + keyval_list: bare_complitexpr. (281) + + . reduce 281 (src line 1930) + + +state 402 + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + keyval: expr.':' complitexpr + bare_complitexpr: expr. (142) + + LANDAND shift 134 + LANDNOT shift 149 + LCOMM shift 152 + LEQ shift 135 + LGE shift 139 + LGT shift 140 + LLE shift 138 + LLSH shift 150 + LLT shift 137 + LNE shift 136 + LOROR shift 133 + LRSH shift 151 + '+' shift 141 + '-' shift 142 + '|' shift 143 + '^' shift 144 + '*' shift 145 + '/' shift 146 + '%' shift 147 + '&' shift 148 + ':' shift 518 + . reduce 142 (src line 1088) + + +state 403 + bare_complitexpr: '{'.start_complit braced_keyval_list '}' + start_complit: . (140) + + . reduce 140 (src line 1075) + + start_complit goto 519 + +state 404 + pexpr_no_paren: '(' expr_or_type ')' '{'.start_complit braced_keyval_list '}' + start_complit: . (140) + + . reduce 140 (src line 1075) + + start_complit goto 520 + +state 405 + othertype: LCHAN LCOMM.ntype + recvchantype: LCOMM.LCHAN ntype + + LCHAN shift 299 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 232 + '*' shift 196 + '(' shift 231 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + ntype goto 311 + dotname goto 230 + name goto 197 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 229 + recvchantype goto 226 + othertype goto 228 + fntype goto 227 + hidden_importsym goto 11 + +state 406 + recvchantype: LCOMM LCHAN ntype. (199) + + . reduce 199 (src line 1343) + + +state 407 + ntype: fntype. (167) + non_recvchantype: fntype. (176) + + 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 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 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 1276) + '(' reduce 179 (src line 1276) + '{' reduce 179 (src line 1276) + . reduce 170 (src line 1257) + + +state 411 + ntype: '('.ntype ')' + non_recvchantype: '('.ntype ')' + + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 232 + '*' shift 196 + '(' shift 231 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + ntype goto 521 + dotname goto 230 + name goto 197 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 229 + recvchantype goto 226 + othertype goto 228 + fntype goto 227 + hidden_importsym goto 11 + +state 412 + pexpr_no_paren: convtype '(' expr ocomma.')' + + ')' shift 522 + . error + + +state 413 + ocomma: ','. (289) + + . reduce 289 (src line 1959) + + +state 414 + pexpr_no_paren: comptype lbrace start_complit braced_keyval_list.'}' + + '}' shift 523 + . error + + +state 415 + fnliteral: fnlitdcl lbrace stmt_list '}'. (216) + + . reduce 216 (src line 1552) + + +state 416 + stmt_list: stmt_list ';'.stmt + stmt: . (251) + + error shift 307 + LLITERAL shift 68 + LBREAK shift 41 + LCASE reduce 251 (src line 1781) + LCHAN shift 78 + LCONST shift 47 + LCONTINUE shift 42 + LDEFAULT reduce 251 (src line 1781) + LDEFER shift 44 + LFALL shift 40 + LFOR shift 50 + LFUNC shift 124 + LGO shift 43 + LGOTO shift 45 + LIF shift 53 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LRETURN shift 46 + LSELECT shift 52 + LSTRUCT shift 82 + LSWITCH shift 51 + LTYPE shift 32 + LVAR shift 30 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + ';' reduce 251 (src line 1781) + '{' shift 308 + '}' reduce 251 (src line 1781) + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 56 + stmt goto 524 + compound_stmt goto 304 + expr goto 48 + fnliteral goto 73 + for_stmt goto 35 + if_stmt goto 38 + non_dcl_stmt goto 306 + labelname goto 39 + name goto 69 + new_name goto 54 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + select_stmt goto 37 + simple_stmt goto 34 + switch_stmt goto 36 + uexpr goto 55 + expr_list goto 49 + common_dcl goto 305 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + lconst goto 31 + fnlitdcl goto 76 + +state 417 + compound_stmt: '{' $$59.stmt_list '}' + stmt: . (251) + + error shift 307 + LLITERAL shift 68 + LBREAK shift 41 + LCHAN shift 78 + LCONST shift 47 + LCONTINUE shift 42 + LDEFER shift 44 + LFALL shift 40 + LFOR shift 50 + LFUNC shift 124 + LGO shift 43 + LGOTO shift 45 + LIF shift 53 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LRETURN shift 46 + LSELECT shift 52 + LSTRUCT shift 82 + LSWITCH shift 51 + LTYPE shift 32 + LVAR shift 30 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + ';' reduce 251 (src line 1781) + '{' shift 308 + '}' reduce 251 (src line 1781) + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 56 + stmt goto 303 + compound_stmt goto 304 + expr goto 48 + fnliteral goto 73 + for_stmt goto 35 + if_stmt goto 38 + non_dcl_stmt goto 306 + labelname goto 39 + name goto 69 + new_name goto 54 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + select_stmt goto 37 + simple_stmt goto 34 + switch_stmt goto 36 + uexpr goto 55 + expr_list goto 49 + stmt_list goto 525 + common_dcl goto 305 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + lconst goto 31 + fnlitdcl goto 76 + +state 418 + othertype: '[' oexpr ']' ntype. (191) + + . reduce 191 (src line 1310) + + +state 419 + othertype: '[' LDDD ']' ntype. (192) + + . reduce 192 (src line 1315) + + +state 420 + non_recvchantype: '(' ntype ')'. (180) + + . reduce 180 (src line 1277) + + +state 421 + dotname: name '.' sym. (190) + + . reduce 190 (src line 1298) + + +state 422 + othertype: LMAP '[' ntype ']'.ntype + + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 232 + '*' shift 196 + '(' shift 231 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + ntype goto 526 + dotname goto 230 + name goto 197 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 229 + recvchantype goto 226 + othertype goto 228 + fntype goto 227 + hidden_importsym goto 11 + +state 423 + structtype: LSTRUCT lbrace structdcl_list osemi.'}' + + '}' shift 527 + . error + + +state 424 + structdcl_list: structdcl_list ';'.structdcl + osemi: ';'. (287) + + LNAME shift 325 + '*' shift 322 + '(' shift 321 + '?' shift 12 + '@' shift 13 + . reduce 287 (src line 1956) + + sym goto 119 + packname goto 324 + embed goto 320 + new_name goto 323 + new_name_list goto 319 + structdcl goto 528 + hidden_importsym goto 11 + +state 425 + structdcl: new_name_list ntype.oliteral + oliteral: . (302) + + LLITERAL shift 428 + . reduce 302 (src line 1997) + + oliteral goto 529 + +state 426 + new_name_list: new_name_list ','.new_name + + LNAME shift 10 + '?' shift 12 + '@' shift 13 + . error + + sym goto 119 + new_name goto 530 + hidden_importsym goto 11 + +state 427 + structdcl: embed oliteral. (231) + + . reduce 231 (src line 1651) + + +state 428 + oliteral: LLITERAL. (303) + + . reduce 303 (src line 2001) + + +state 429 + structdcl: '(' embed.')' oliteral + + ')' shift 531 + . error + + +state 430 + structdcl: '(' '*'.embed ')' oliteral + + LNAME shift 431 + . error + + packname goto 324 + embed goto 532 + +state 431 + packname: LNAME. (236) + packname: LNAME.'.' sym + + '.' shift 434 + . reduce 236 (src line 1683) + + +state 432 + structdcl: '*' embed.oliteral + oliteral: . (302) + + LLITERAL shift 428 + . reduce 302 (src line 1997) + + oliteral goto 533 + +state 433 + structdcl: '*' '('.embed ')' oliteral + + LNAME shift 431 + . error + + packname goto 324 + embed goto 534 + +state 434 + packname: LNAME '.'.sym + + LNAME shift 10 + '?' shift 12 + '@' shift 13 + . error + + sym goto 535 + hidden_importsym goto 11 + +state 435 + interfacetype: LINTERFACE lbrace interfacedcl_list osemi.'}' + + '}' shift 536 + . error + + +state 436 + interfacedcl_list: interfacedcl_list ';'.interfacedcl + osemi: ';'. (287) + + LNAME shift 325 + '(' shift 331 + '?' shift 12 + '@' shift 13 + . reduce 287 (src line 1956) + + sym goto 119 + packname goto 330 + interfacedcl goto 537 + new_name goto 329 + hidden_importsym goto 11 + +state 437 + interfacedcl: new_name indcl. (239) + + . reduce 239 (src line 1714) + + +state 438 + indcl: '('.oarg_type_list_ocomma ')' fnres + oarg_type_list_ocomma: . (249) + + LCHAN shift 78 + LDDD shift 250 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 232 + '*' shift 196 + '(' shift 231 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 249 (src line 1769) + + sym goto 247 + ntype goto 249 + arg_type goto 245 + dotname goto 230 + name goto 197 + name_or_type goto 246 + oarg_type_list_ocomma goto 538 + arg_type_list goto 244 + dotdotdot goto 248 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 229 + recvchantype goto 226 + othertype goto 228 + fntype goto 227 + hidden_importsym goto 11 + +state 439 + interfacedcl: '(' packname.')' + + ')' shift 539 + . error + + +state 440 + import: LIMPORT '(' import_stmt_list osemi ')'. (9) + + . reduce 9 (src line 232) + + +state 441 + import_stmt_list: import_stmt_list ';' import_stmt. (14) + + . reduce 14 (src line 284) + + +state 442 + hidden_import: LIMPORT LNAME LLITERAL ';'. (304) + + . reduce 304 (src line 2006) + + +state 443 + hidden_import: LVAR hidden_pkg_importsym hidden_type ';'. (305) + + . reduce 305 (src line 2011) + + +state 444 + hidden_type_misc: '[' ']'.hidden_type + + LCHAN shift 347 + LFUNC shift 349 + LINTERFACE shift 345 + LMAP shift 343 + LNAME shift 341 + LSTRUCT shift 344 + LCOMM shift 348 + '*' shift 346 + '[' shift 342 + '@' shift 13 + . error + + hidden_importsym goto 340 + hidden_type goto 540 + hidden_type_misc goto 337 + hidden_type_func goto 339 + hidden_type_recv_chan goto 338 + +state 445 + hidden_type_misc: '[' LLITERAL.']' hidden_type + + ']' shift 541 + . error + + +state 446 + hidden_type_misc: LMAP '['.hidden_type ']' hidden_type + + LCHAN shift 347 + LFUNC shift 349 + LINTERFACE shift 345 + LMAP shift 343 + LNAME shift 341 + LSTRUCT shift 344 + LCOMM shift 348 + '*' shift 346 + '[' shift 342 + '@' shift 13 + . error + + hidden_importsym goto 340 + hidden_type goto 542 + hidden_type_misc goto 337 + hidden_type_func goto 339 + hidden_type_recv_chan goto 338 + +state 447 + hidden_type_misc: LSTRUCT '{'.ohidden_structdcl_list '}' + ohidden_structdcl_list: . (298) + + LNAME shift 10 + '?' shift 12 + '@' shift 13 + . reduce 298 (src line 1985) + + sym goto 546 + hidden_importsym goto 11 + hidden_structdcl goto 545 + hidden_structdcl_list goto 544 + ohidden_structdcl_list goto 543 + +state 448 + hidden_type_misc: LINTERFACE '{'.ohidden_interfacedcl_list '}' + ohidden_interfacedcl_list: . (300) + + LCHAN shift 347 + LFUNC shift 349 + LINTERFACE shift 345 + LMAP shift 343 + LNAME shift 552 + LSTRUCT shift 344 + LCOMM shift 348 + '*' shift 346 + '[' shift 342 + '?' shift 12 + '@' shift 13 + . reduce 300 (src line 1991) + + sym goto 550 + hidden_importsym goto 553 + hidden_interfacedcl goto 549 + hidden_interfacedcl_list goto 548 + ohidden_interfacedcl_list goto 547 + hidden_type goto 551 + hidden_type_misc goto 337 + hidden_type_func goto 339 + hidden_type_recv_chan goto 338 + +state 449 + hidden_type_misc: '*' hidden_type. (324) + + . reduce 324 (src line 2110) + + +state 450 + hidden_type_misc: LCHAN hidden_type_non_recv_chan. (325) + + . reduce 325 (src line 2114) + + +state 451 + hidden_type_misc: LCHAN '('.hidden_type_recv_chan ')' + + LCOMM shift 348 + . error + + hidden_type_recv_chan goto 554 + +state 452 + hidden_type_misc: LCHAN LCOMM.hidden_type + + LCHAN shift 347 + LFUNC shift 349 + LINTERFACE shift 345 + LMAP shift 343 + LNAME shift 341 + LSTRUCT shift 344 + LCOMM shift 348 + '*' shift 346 + '[' shift 342 + '@' shift 13 + . error + + hidden_importsym goto 340 + hidden_type goto 555 + hidden_type_misc goto 337 + hidden_type_func goto 339 + hidden_type_recv_chan goto 338 + +state 453 + hidden_type_non_recv_chan: hidden_type_misc. (315) + + . reduce 315 (src line 2070) + + +state 454 + hidden_type_non_recv_chan: hidden_type_func. (316) + + . reduce 316 (src line 2072) + + +state 455 + hidden_type_recv_chan: LCOMM LCHAN.hidden_type + + LCHAN shift 347 + LFUNC shift 349 + LINTERFACE shift 345 + LMAP shift 343 + LNAME shift 341 + LSTRUCT shift 344 + LCOMM shift 348 + '*' shift 346 + '[' shift 342 + '@' shift 13 + . error + + hidden_importsym goto 340 + hidden_type goto 556 + hidden_type_misc goto 337 + hidden_type_func goto 339 + hidden_type_recv_chan goto 338 + +state 456 + hidden_type_func: LFUNC '('.ohidden_funarg_list ')' ohidden_funres + ohidden_funarg_list: . (296) + + LNAME shift 10 + '?' shift 12 + '@' shift 13 + . reduce 296 (src line 1979) + + sym goto 357 + hidden_importsym goto 11 + hidden_funarg goto 356 + hidden_funarg_list goto 467 + ohidden_funarg_list goto 557 + +state 457 + hidden_import: LCONST hidden_pkg_importsym '=' hidden_constant.';' + + ';' shift 558 + . error + + +state 458 + hidden_constant: hidden_literal. (342) + + . reduce 342 (src line 2257) + + +state 459 + hidden_constant: '('.hidden_literal '+' hidden_literal ')' + + LLITERAL shift 460 + LNAME shift 10 + '-' shift 461 + '?' shift 12 + '@' shift 13 + . error + + sym goto 462 + hidden_importsym goto 11 + hidden_literal goto 559 + +state 460 + hidden_literal: LLITERAL. (339) + + . reduce 339 (src line 2226) + + +state 461 + hidden_literal: '-'.LLITERAL + + LLITERAL shift 560 + . error + + +state 462 + hidden_literal: sym. (341) + + . reduce 341 (src line 2249) + + +state 463 + hidden_import: LCONST hidden_pkg_importsym hidden_type '='.hidden_constant ';' + + LLITERAL shift 460 + LNAME shift 10 + '-' shift 461 + '(' shift 459 + '?' shift 12 + '@' shift 13 + . error + + sym goto 462 + hidden_importsym goto 11 + hidden_constant goto 561 + hidden_literal goto 458 + +state 464 + hidden_import: LTYPE hidden_pkgtype hidden_type ';'. (308) + + . reduce 308 (src line 2023) + + +state 465 + hidden_import: LFUNC hidden_fndcl fnbody ';'. (309) + + . reduce 309 (src line 2027) + + +state 466 + hidden_fndcl: hidden_pkg_importsym '(' ohidden_funarg_list.')' ohidden_funres + + ')' shift 562 + . error + + +state 467 + ohidden_funarg_list: hidden_funarg_list. (297) + hidden_funarg_list: hidden_funarg_list.',' hidden_funarg + + ',' shift 469 + . reduce 297 (src line 1983) + + +state 468 + hidden_fndcl: '(' hidden_funarg_list ')'.sym '(' ohidden_funarg_list ')' ohidden_funres + + LNAME shift 10 + '?' shift 12 + '@' shift 13 + . error + + sym goto 563 + hidden_importsym goto 11 + +state 469 + hidden_funarg_list: hidden_funarg_list ','.hidden_funarg + + LNAME shift 10 + '?' shift 12 + '@' shift 13 + . error + + sym goto 357 + hidden_importsym goto 11 + hidden_funarg goto 564 + +state 470 + hidden_funarg: sym hidden_type.oliteral + oliteral: . (302) + + LLITERAL shift 428 + . reduce 302 (src line 1997) + + oliteral goto 565 + +state 471 + hidden_funarg: sym LDDD.hidden_type oliteral + + LCHAN shift 347 + LFUNC shift 349 + LINTERFACE shift 345 + LMAP shift 343 + LNAME shift 341 + LSTRUCT shift 344 + LCOMM shift 348 + '*' shift 346 + '[' shift 342 + '@' shift 13 + . error + + hidden_importsym goto 340 + hidden_type goto 566 + hidden_type_misc goto 337 + hidden_type_func goto 339 + hidden_type_recv_chan goto 338 + +state 472 + common_dcl: LVAR '(' vardcl_list osemi ')'. (29) + + . reduce 29 (src line 372) + + +state 473 + vardcl_list: vardcl_list ';' vardcl. (221) + + . reduce 221 (src line 1587) + + +state 474 + vardcl: dcl_name_list ntype '=' expr_list. (40) + expr_list: expr_list.',' expr + + ',' shift 155 + . reduce 40 (src line 427) + + +state 475 + ntype: '(' ntype ')'. (171) + + . reduce 171 (src line 1258) + + +state 476 + common_dcl: lconst '(' constdcl osemi ')'. (32) + + . reduce 32 (src line 386) + + +state 477 + common_dcl: lconst '(' constdcl ';' constdcl_list.osemi ')' + constdcl_list: constdcl_list.';' constdcl1 + osemi: . (286) + + ';' shift 568 + . reduce 286 (src line 1955) + + osemi goto 567 + +state 478 + constdcl_list: constdcl1. (222) + + . reduce 222 (src line 1592) + + +state 479 + constdcl1: constdcl. (44) + + . reduce 44 (src line 446) + + +state 480 + constdcl: dcl_name_list.ntype '=' expr_list + constdcl: dcl_name_list.'=' expr_list + constdcl1: dcl_name_list.ntype + constdcl1: dcl_name_list. (46) + dcl_name_list: dcl_name_list.',' dcl_name + + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 232 + '*' shift 196 + '(' shift 231 + '=' shift 236 + '[' shift 77 + '?' shift 12 + '@' shift 13 + ',' shift 225 + . reduce 46 (src line 452) + + sym goto 123 + ntype goto 569 + dotname goto 230 + name goto 197 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 229 + recvchantype goto 226 + othertype goto 228 + fntype goto 227 + hidden_importsym goto 11 + +state 481 + constdcl: dcl_name_list ntype '=' expr_list. (42) + expr_list: expr_list.',' expr + + ',' shift 155 + . reduce 42 (src line 436) + + +state 482 + common_dcl: LTYPE '(' typedcl_list osemi ')'. (36) + + . reduce 36 (src line 407) + + +state 483 + typedcl_list: typedcl_list ';' typedcl. (225) + + . reduce 225 (src line 1604) + + +state 484 + fnbody: '{' stmt_list '}'. (211) + + . reduce 211 (src line 1523) + + +state 485 + name: sym. (162) + fndcl: '(' oarg_type_list_ocomma ')' sym.'(' oarg_type_list_ocomma ')' fnres + + '(' shift 570 + . reduce 162 (src line 1220) + + +state 486 + fntype: LFUNC '(' oarg_type_list_ocomma ')' fnres. (209) + + . reduce 209 (src line 1510) + + +state 487 + fnres: fnret_type. (213) + + . reduce 213 (src line 1536) + + +state 488 + fnres: '('.oarg_type_list_ocomma ')' + oarg_type_list_ocomma: . (249) + + LCHAN shift 78 + LDDD shift 250 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 232 + '*' shift 196 + '(' shift 231 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 249 (src line 1769) + + sym goto 247 + ntype goto 249 + arg_type goto 245 + dotname goto 230 + name goto 197 + name_or_type goto 246 + oarg_type_list_ocomma goto 571 + arg_type_list goto 244 + dotdotdot goto 248 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 229 + recvchantype goto 226 + othertype goto 228 + fntype goto 227 + hidden_importsym goto 11 + +state 489 + fnret_type: recvchantype. (184) + + . reduce 184 (src line 1289) + + +state 490 + fnret_type: fntype. (185) + + . reduce 185 (src line 1291) + + +state 491 + fnret_type: othertype. (186) + + . reduce 186 (src line 1292) + + +state 492 + fnret_type: ptrtype. (187) + + . reduce 187 (src line 1293) + + +state 493 + fnret_type: dotname. (188) + + . reduce 188 (src line 1294) + + +state 494 + arg_type_list: arg_type_list ',' arg_type. (248) + + . reduce 248 (src line 1764) + + +state 495 + fndcl: sym '(' oarg_type_list_ocomma ')'.fnres + fnres: . (212) + + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 232 + '*' shift 196 + '(' shift 488 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 212 (src line 1531) + + sym goto 123 + dotname goto 493 + name goto 197 + fnres goto 572 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 492 + recvchantype goto 489 + othertype goto 491 + fnret_type goto 487 + fntype goto 490 + hidden_importsym goto 11 + +state 496 + fntype: LFUNC '(' oarg_type_list_ocomma ')'.fnres + fnres: . (212) + + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 232 + '*' shift 196 + '(' shift 488 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 212 (src line 1531) + + sym goto 123 + dotname goto 493 + name goto 197 + fnres goto 486 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 492 + recvchantype goto 489 + othertype goto 491 + fnret_type goto 487 + fntype goto 490 + hidden_importsym goto 11 + +state 497 + loop_body: LBODY $$65.stmt_list '}' + stmt: . (251) + + error shift 307 + LLITERAL shift 68 + LBREAK shift 41 + LCHAN shift 78 + LCONST shift 47 + LCONTINUE shift 42 + LDEFER shift 44 + LFALL shift 40 + LFOR shift 50 + LFUNC shift 124 + LGO shift 43 + LGOTO shift 45 + LIF shift 53 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LRETURN shift 46 + LSELECT shift 52 + LSTRUCT shift 82 + LSWITCH shift 51 + LTYPE shift 32 + LVAR shift 30 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + ';' reduce 251 (src line 1781) + '{' shift 308 + '}' reduce 251 (src line 1781) + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 56 + stmt goto 303 + compound_stmt goto 304 + expr goto 48 + fnliteral goto 73 + for_stmt goto 35 + if_stmt goto 38 + non_dcl_stmt goto 306 + labelname goto 39 + name goto 69 + new_name goto 54 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + select_stmt goto 37 + simple_stmt goto 34 + switch_stmt goto 36 + uexpr goto 55 + expr_list goto 49 + stmt_list goto 573 + common_dcl goto 305 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + lconst goto 31 + fnlitdcl goto 76 + +state 498 + for_header: osimple_stmt ';' osimple_stmt.';' osimple_stmt + + ';' shift 574 + . error + + +state 499 + range_stmt: expr_list '=' LRANGE.expr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 575 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 500 + range_stmt: expr_list LCOLAS LRANGE.expr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 576 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 501 + switch_stmt: LSWITCH $$88 if_header $$89 LBODY.caseblock_list '}' + caseblock_list: . (63) + + . reduce 63 (src line 652) + + caseblock_list goto 577 + +state 502 + if_header: osimple_stmt ';' osimple_stmt. (77) + + . reduce 77 (src line 739) + + +state 503 + caseblock_list: caseblock_list caseblock. (64) + + . reduce 64 (src line 656) + + +state 504 + select_stmt: LSELECT $$91 LBODY caseblock_list '}'. (92) + + . reduce 92 (src line 844) + + +state 505 + caseblock: case.$$61 stmt_list + $$61: . (61) + + . reduce 61 (src line 621) + + $$61 goto 578 + +state 506 + case: LCASE.expr_or_type_list ':' + case: LCASE.expr_or_type_list '=' expr ':' + case: LCASE.expr_or_type_list LCOLAS expr ':' + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 179 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 178 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 173 + expr_or_type goto 291 + fnliteral goto 73 + name goto 69 + non_expr_type goto 174 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + expr_or_type_list goto 579 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + recvchantype goto 175 + othertype goto 177 + fntype goto 176 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 507 + case: LDEFAULT.':' + + ':' shift 580 + . error + + +state 508 + if_stmt: LIF $$78 if_header $$79 loop_body.$$80 elseif_list else + $$80: . (80) + + . reduce 80 (src line 761) + + $$80 goto 581 + +state 509 + pseudocall: pexpr '(' expr_or_type_list ocomma ')'. (124) + + . reduce 124 (src line 991) + + +state 510 + pseudocall: pexpr '(' expr_or_type_list LDDD ocomma.')' + + ')' shift 582 + . error + + +state 511 + expr_or_type_list: expr_or_type_list ',' expr_or_type. (279) + + . reduce 279 (src line 1917) + + +state 512 + pexpr_no_paren: pexpr '.' '(' expr_or_type ')'. (129) + + . reduce 129 (src line 1020) + + +state 513 + pexpr_no_paren: pexpr '.' '(' LTYPE ')'. (130) + + . reduce 130 (src line 1024) + + +state 514 + pexpr_no_paren: pexpr '[' oexpr ':' oexpr.']' + pexpr_no_paren: pexpr '[' oexpr ':' oexpr.':' oexpr ']' + + ':' shift 584 + ']' shift 583 + . error + + +state 515 + pexpr_no_paren: pexpr_no_paren '{' start_complit braced_keyval_list '}'. (137) + + . reduce 137 (src line 1060) + + +state 516 + keyval_list: keyval_list ','.keyval + keyval_list: keyval_list ','.bare_complitexpr + ocomma: ','. (289) + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '{' shift 403 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 289 (src line 1959) + + sym goto 123 + expr goto 402 + bare_complitexpr goto 586 + fnliteral goto 73 + keyval goto 585 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 517 + braced_keyval_list: keyval_list ocomma. (285) + + . reduce 285 (src line 1947) + + +state 518 + keyval: expr ':'.complitexpr + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '{' shift 589 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 588 + complitexpr goto 587 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 519 + bare_complitexpr: '{' start_complit.braced_keyval_list '}' + braced_keyval_list: . (284) + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '{' shift 403 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 284 (src line 1943) + + sym goto 123 + expr goto 402 + bare_complitexpr goto 401 + fnliteral goto 73 + keyval goto 400 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + keyval_list goto 399 + braced_keyval_list goto 590 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 520 + pexpr_no_paren: '(' expr_or_type ')' '{' start_complit.braced_keyval_list '}' + braced_keyval_list: . (284) + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '{' shift 403 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 284 (src line 1943) + + sym goto 123 + expr goto 402 + bare_complitexpr goto 401 + fnliteral goto 73 + keyval goto 400 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + keyval_list goto 399 + braced_keyval_list goto 591 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 521 + ntype: '(' ntype.')' + non_recvchantype: '(' ntype.')' + + ')' shift 592 + . error + + +state 522 + pexpr_no_paren: convtype '(' expr ocomma ')'. (135) + + . reduce 135 (src line 1047) + + +state 523 + pexpr_no_paren: comptype lbrace start_complit braced_keyval_list '}'. (136) + + . reduce 136 (src line 1053) + + +state 524 + stmt_list: stmt_list ';' stmt. (271) + + . reduce 271 (src line 1874) + + +state 525 + compound_stmt: '{' $$59 stmt_list.'}' + stmt_list: stmt_list.';' stmt + + ';' shift 416 + '}' shift 593 + . error + + +state 526 + othertype: LMAP '[' ntype ']' ntype. (195) + + . reduce 195 (src line 1330) + + +state 527 + structtype: LSTRUCT lbrace structdcl_list osemi '}'. (200) + + . reduce 200 (src line 1350) + + +state 528 + structdcl_list: structdcl_list ';' structdcl. (227) + + . reduce 227 (src line 1611) + + +state 529 + structdcl: new_name_list ntype oliteral. (230) + + . reduce 230 (src line 1626) + + +state 530 + new_name_list: new_name_list ',' new_name. (273) + + . reduce 273 (src line 1887) + + +state 531 + structdcl: '(' embed ')'.oliteral + oliteral: . (302) + + LLITERAL shift 428 + . reduce 302 (src line 1997) + + oliteral goto 594 + +state 532 + structdcl: '(' '*' embed.')' oliteral + + ')' shift 595 + . error + + +state 533 + structdcl: '*' embed oliteral. (233) + + . reduce 233 (src line 1662) + + +state 534 + structdcl: '*' '(' embed.')' oliteral + + ')' shift 596 + . error + + +state 535 + packname: LNAME '.' sym. (237) + + . reduce 237 (src line 1694) + + +state 536 + interfacetype: LINTERFACE lbrace interfacedcl_list osemi '}'. (202) + + . reduce 202 (src line 1363) + + +state 537 + interfacedcl_list: interfacedcl_list ';' interfacedcl. (229) + + . reduce 229 (src line 1621) + + +state 538 + indcl: '(' oarg_type_list_ocomma.')' fnres + + ')' shift 597 + . error + + +state 539 + interfacedcl: '(' packname ')'. (241) + + . reduce 241 (src line 1724) + + +state 540 + hidden_type_misc: '[' ']' hidden_type. (319) + + . reduce 319 (src line 2090) + + +state 541 + hidden_type_misc: '[' LLITERAL ']'.hidden_type + + LCHAN shift 347 + LFUNC shift 349 + LINTERFACE shift 345 + LMAP shift 343 + LNAME shift 341 + LSTRUCT shift 344 + LCOMM shift 348 + '*' shift 346 + '[' shift 342 + '@' shift 13 + . error + + hidden_importsym goto 340 + hidden_type goto 598 + hidden_type_misc goto 337 + hidden_type_func goto 339 + hidden_type_recv_chan goto 338 + +state 542 + hidden_type_misc: LMAP '[' hidden_type.']' hidden_type + + ']' shift 599 + . error + + +state 543 + hidden_type_misc: LSTRUCT '{' ohidden_structdcl_list.'}' + + '}' shift 600 + . error + + +state 544 + ohidden_structdcl_list: hidden_structdcl_list. (299) + hidden_structdcl_list: hidden_structdcl_list.';' hidden_structdcl + + ';' shift 601 + . reduce 299 (src line 1989) + + +state 545 + hidden_structdcl_list: hidden_structdcl. (348) + + . reduce 348 (src line 2284) + + +state 546 + hidden_structdcl: sym.hidden_type oliteral + + LCHAN shift 347 + LFUNC shift 349 + LINTERFACE shift 345 + LMAP shift 343 + LNAME shift 341 + LSTRUCT shift 344 + LCOMM shift 348 + '*' shift 346 + '[' shift 342 + '@' shift 13 + . error + + hidden_importsym goto 340 + hidden_type goto 602 + hidden_type_misc goto 337 + hidden_type_func goto 339 + hidden_type_recv_chan goto 338 + +state 547 + hidden_type_misc: LINTERFACE '{' ohidden_interfacedcl_list.'}' + + '}' shift 603 + . error + + +state 548 + ohidden_interfacedcl_list: hidden_interfacedcl_list. (301) + hidden_interfacedcl_list: hidden_interfacedcl_list.';' hidden_interfacedcl + + ';' shift 604 + . reduce 301 (src line 1995) + + +state 549 + hidden_interfacedcl_list: hidden_interfacedcl. (350) + + . reduce 350 (src line 2294) + + +state 550 + hidden_interfacedcl: sym.'(' ohidden_funarg_list ')' ohidden_funres + + '(' shift 605 + . error + + +state 551 + hidden_interfacedcl: hidden_type. (334) + + . reduce 334 (src line 2201) + + +state 552 + sym: LNAME. (157) + hidden_type_misc: LNAME. (318) + + '(' 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 1184) + . reduce 317 (src line 2074) + + +state 554 + hidden_type_misc: LCHAN '(' hidden_type_recv_chan.')' + + ')' shift 606 + . error + + +state 555 + hidden_type_misc: LCHAN LCOMM hidden_type. (327) + + . reduce 327 (src line 2126) + + +state 556 + hidden_type_recv_chan: LCOMM LCHAN hidden_type. (328) + + . reduce 328 (src line 2133) + + +state 557 + hidden_type_func: LFUNC '(' ohidden_funarg_list.')' ohidden_funres + + ')' shift 607 + . error + + +state 558 + hidden_import: LCONST hidden_pkg_importsym '=' hidden_constant ';'. (306) + + . reduce 306 (src line 2015) + + +state 559 + hidden_constant: '(' hidden_literal.'+' hidden_literal ')' + + '+' shift 608 + . error + + +state 560 + hidden_literal: '-' LLITERAL. (340) + + . reduce 340 (src line 2231) + + +state 561 + hidden_import: LCONST hidden_pkg_importsym hidden_type '=' hidden_constant.';' + + ';' shift 609 + . error + + +state 562 + hidden_fndcl: hidden_pkg_importsym '(' ohidden_funarg_list ')'.ohidden_funres + ohidden_funres: . (335) + + LCHAN shift 347 + LFUNC shift 349 + LINTERFACE shift 345 + LMAP shift 343 + LNAME shift 341 + LSTRUCT shift 344 + LCOMM shift 348 + '*' shift 346 + '(' shift 612 + '[' shift 342 + '@' shift 13 + . reduce 335 (src line 2206) + + hidden_importsym goto 340 + hidden_funres goto 611 + ohidden_funres goto 610 + hidden_type goto 613 + hidden_type_misc goto 337 + hidden_type_func goto 339 + hidden_type_recv_chan goto 338 + +state 563 + hidden_fndcl: '(' hidden_funarg_list ')' sym.'(' ohidden_funarg_list ')' ohidden_funres + + '(' shift 614 + . error + + +state 564 + hidden_funarg_list: hidden_funarg_list ',' hidden_funarg. (347) + + . reduce 347 (src line 2279) + + +state 565 + hidden_funarg: sym hidden_type oliteral. (330) + + . reduce 330 (src line 2147) + + +state 566 + hidden_funarg: sym LDDD hidden_type.oliteral + oliteral: . (302) + + LLITERAL shift 428 + . reduce 302 (src line 1997) + + oliteral goto 615 + +state 567 + common_dcl: lconst '(' constdcl ';' constdcl_list osemi.')' + + ')' shift 616 + . error + + +state 568 + constdcl_list: constdcl_list ';'.constdcl1 + osemi: ';'. (287) + + LNAME shift 10 + '?' shift 12 + '@' shift 13 + . reduce 287 (src line 1956) + + sym goto 105 + dcl_name goto 104 + dcl_name_list goto 480 + constdcl goto 479 + constdcl1 goto 617 + hidden_importsym goto 11 + +state 569 + constdcl: dcl_name_list ntype.'=' expr_list + constdcl1: dcl_name_list ntype. (45) + + '=' shift 367 + . reduce 45 (src line 448) + + +state 570 + fndcl: '(' oarg_type_list_ocomma ')' sym '('.oarg_type_list_ocomma ')' fnres + oarg_type_list_ocomma: . (249) + + LCHAN shift 78 + LDDD shift 250 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 232 + '*' shift 196 + '(' shift 231 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 249 (src line 1769) + + sym goto 247 + ntype goto 249 + arg_type goto 245 + dotname goto 230 + name goto 197 + name_or_type goto 246 + oarg_type_list_ocomma goto 618 + arg_type_list goto 244 + dotdotdot goto 248 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 229 + recvchantype goto 226 + othertype goto 228 + fntype goto 227 + hidden_importsym goto 11 + +state 571 + fnres: '(' oarg_type_list_ocomma.')' + + ')' shift 619 + . error + + +state 572 + fndcl: sym '(' oarg_type_list_ocomma ')' fnres. (205) + + . reduce 205 (src line 1398) + + +state 573 + loop_body: LBODY $$65 stmt_list.'}' + stmt_list: stmt_list.';' stmt + + ';' shift 416 + '}' shift 620 + . error + + +state 574 + for_header: osimple_stmt ';' osimple_stmt ';'.osimple_stmt + osimple_stmt: . (294) + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 294 (src line 1973) + + sym goto 123 + expr goto 48 + fnliteral goto 73 + name goto 69 + osimple_stmt goto 621 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + simple_stmt goto 282 + uexpr goto 55 + expr_list goto 49 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 575 + range_stmt: expr_list '=' LRANGE expr. (67) + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + + LANDAND shift 134 + LANDNOT shift 149 + LCOMM shift 152 + LEQ shift 135 + LGE shift 139 + LGT shift 140 + LLE shift 138 + LLSH shift 150 + LLT shift 137 + LNE shift 136 + LOROR shift 133 + LRSH shift 151 + '+' shift 141 + '-' shift 142 + '|' shift 143 + '^' shift 144 + '*' shift 145 + '/' shift 146 + '%' shift 147 + '&' shift 148 + . reduce 67 (src line 672) + + +state 576 + range_stmt: expr_list LCOLAS LRANGE expr. (68) + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + + LANDAND shift 134 + LANDNOT shift 149 + LCOMM shift 152 + LEQ shift 135 + LGE shift 139 + LGT shift 140 + LLE shift 138 + LLSH shift 150 + LLT shift 137 + LNE shift 136 + LOROR shift 133 + LRSH shift 151 + '+' shift 141 + '-' shift 142 + '|' shift 143 + '^' shift 144 + '*' shift 145 + '/' shift 146 + '%' shift 147 + '&' shift 148 + . reduce 68 (src line 679) + + +state 577 + caseblock_list: caseblock_list.caseblock + switch_stmt: LSWITCH $$88 if_header $$89 LBODY caseblock_list.'}' + + LCASE shift 506 + LDEFAULT shift 507 + '}' shift 622 + . error + + case goto 505 + caseblock goto 503 + +state 578 + caseblock: case $$61.stmt_list + stmt: . (251) + + error shift 307 + LLITERAL shift 68 + LBREAK shift 41 + LCASE reduce 251 (src line 1781) + LCHAN shift 78 + LCONST shift 47 + LCONTINUE shift 42 + LDEFAULT reduce 251 (src line 1781) + LDEFER shift 44 + LFALL shift 40 + LFOR shift 50 + LFUNC shift 124 + LGO shift 43 + LGOTO shift 45 + LIF shift 53 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LRETURN shift 46 + LSELECT shift 52 + LSTRUCT shift 82 + LSWITCH shift 51 + LTYPE shift 32 + LVAR shift 30 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + ';' reduce 251 (src line 1781) + '{' shift 308 + '}' reduce 251 (src line 1781) + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 56 + stmt goto 303 + compound_stmt goto 304 + expr goto 48 + fnliteral goto 73 + for_stmt goto 35 + if_stmt goto 38 + non_dcl_stmt goto 306 + labelname goto 39 + name goto 69 + new_name goto 54 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + select_stmt goto 37 + simple_stmt goto 34 + switch_stmt goto 36 + uexpr goto 55 + expr_list goto 49 + stmt_list goto 623 + common_dcl goto 305 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + lconst goto 31 + fnlitdcl goto 76 + +state 579 + case: LCASE expr_or_type_list.':' + case: LCASE expr_or_type_list.'=' expr ':' + case: LCASE expr_or_type_list.LCOLAS expr ':' + expr_or_type_list: expr_or_type_list.',' expr_or_type + + LCOLAS shift 626 + '=' shift 625 + ':' shift 624 + ',' shift 627 + . error + + +state 580 + case: LDEFAULT ':'. (58) + + . 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 796) + + elseif_list goto 628 + +state 582 + pseudocall: pexpr '(' expr_or_type_list LDDD ocomma ')'. (125) + + . reduce 125 (src line 996) + + +state 583 + pexpr_no_paren: pexpr '[' oexpr ':' oexpr ']'. (132) + + . reduce 132 (src line 1032) + + +state 584 + pexpr_no_paren: pexpr '[' oexpr ':' oexpr ':'.oexpr ']' + oexpr: . (290) + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 290 (src line 1961) + + sym goto 123 + expr goto 188 + fnliteral goto 73 + name goto 69 + oexpr goto 629 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 585 + keyval_list: keyval_list ',' keyval. (282) + + . reduce 282 (src line 1934) + + +state 586 + keyval_list: keyval_list ',' bare_complitexpr. (283) + + . reduce 283 (src line 1938) + + +state 587 + keyval: expr ':' complitexpr. (141) + + . reduce 141 (src line 1082) + + +state 588 + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + complitexpr: expr. (144) + + LANDAND shift 134 + LANDNOT shift 149 + LCOMM shift 152 + LEQ shift 135 + LGE shift 139 + LGT shift 140 + LLE shift 138 + LLSH shift 150 + LLT shift 137 + LNE shift 136 + LOROR shift 133 + LRSH shift 151 + '+' shift 141 + '-' shift 142 + '|' shift 143 + '^' shift 144 + '*' shift 145 + '/' shift 146 + '%' shift 147 + '&' shift 148 + . reduce 144 (src line 1108) + + +state 589 + complitexpr: '{'.start_complit braced_keyval_list '}' + start_complit: . (140) + + . reduce 140 (src line 1075) + + start_complit goto 630 + +state 590 + bare_complitexpr: '{' start_complit braced_keyval_list.'}' + + '}' shift 631 + . error + + +state 591 + pexpr_no_paren: '(' expr_or_type ')' '{' start_complit braced_keyval_list.'}' + + '}' shift 632 + . error + + +state 592 + ntype: '(' ntype ')'. (171) + non_recvchantype: '(' ntype ')'. (180) + + 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 611) + + +state 594 + structdcl: '(' embed ')' oliteral. (232) + + . reduce 232 (src line 1656) + + +state 595 + structdcl: '(' '*' embed ')'.oliteral + oliteral: . (302) + + LLITERAL shift 428 + . reduce 302 (src line 1997) + + oliteral goto 633 + +state 596 + structdcl: '*' '(' embed ')'.oliteral + oliteral: . (302) + + LLITERAL shift 428 + . reduce 302 (src line 1997) + + oliteral goto 634 + +state 597 + indcl: '(' oarg_type_list_ocomma ')'.fnres + fnres: . (212) + + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 232 + '*' shift 196 + '(' shift 488 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 212 (src line 1531) + + sym goto 123 + dotname goto 493 + name goto 197 + fnres goto 635 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 492 + recvchantype goto 489 + othertype goto 491 + fnret_type goto 487 + fntype goto 490 + hidden_importsym goto 11 + +state 598 + hidden_type_misc: '[' LLITERAL ']' hidden_type. (320) + + . reduce 320 (src line 2094) + + +state 599 + hidden_type_misc: LMAP '[' hidden_type ']'.hidden_type + + LCHAN shift 347 + LFUNC shift 349 + LINTERFACE shift 345 + LMAP shift 343 + LNAME shift 341 + LSTRUCT shift 344 + LCOMM shift 348 + '*' shift 346 + '[' shift 342 + '@' shift 13 + . error + + hidden_importsym goto 340 + hidden_type goto 636 + hidden_type_misc goto 337 + hidden_type_func goto 339 + hidden_type_recv_chan goto 338 + +state 600 + hidden_type_misc: LSTRUCT '{' ohidden_structdcl_list '}'. (322) + + . reduce 322 (src line 2102) + + +state 601 + hidden_structdcl_list: hidden_structdcl_list ';'.hidden_structdcl + + LNAME shift 10 + '?' shift 12 + '@' shift 13 + . error + + sym goto 546 + hidden_importsym goto 11 + hidden_structdcl goto 637 + +state 602 + hidden_structdcl: sym hidden_type.oliteral + oliteral: . (302) + + LLITERAL shift 428 + . reduce 302 (src line 1997) + + oliteral goto 638 + +state 603 + hidden_type_misc: LINTERFACE '{' ohidden_interfacedcl_list '}'. (323) + + . reduce 323 (src line 2106) + + +state 604 + hidden_interfacedcl_list: hidden_interfacedcl_list ';'.hidden_interfacedcl + + LCHAN shift 347 + LFUNC shift 349 + LINTERFACE shift 345 + LMAP shift 343 + LNAME shift 552 + LSTRUCT shift 344 + LCOMM shift 348 + '*' shift 346 + '[' shift 342 + '?' shift 12 + '@' shift 13 + . error + + sym goto 550 + hidden_importsym goto 553 + hidden_interfacedcl goto 639 + hidden_type goto 551 + hidden_type_misc goto 337 + hidden_type_func goto 339 + hidden_type_recv_chan goto 338 + +state 605 + hidden_interfacedcl: sym '('.ohidden_funarg_list ')' ohidden_funres + ohidden_funarg_list: . (296) + + LNAME shift 10 + '?' shift 12 + '@' shift 13 + . reduce 296 (src line 1979) + + sym goto 357 + hidden_importsym goto 11 + hidden_funarg goto 356 + hidden_funarg_list goto 467 + ohidden_funarg_list goto 640 + +state 606 + hidden_type_misc: LCHAN '(' hidden_type_recv_chan ')'. (326) + + . reduce 326 (src line 2120) + + +state 607 + hidden_type_func: LFUNC '(' ohidden_funarg_list ')'.ohidden_funres + ohidden_funres: . (335) + + LCHAN shift 347 + LFUNC shift 349 + LINTERFACE shift 345 + LMAP shift 343 + LNAME shift 341 + LSTRUCT shift 344 + LCOMM shift 348 + '*' shift 346 + '(' shift 612 + '[' shift 342 + '@' shift 13 + . reduce 335 (src line 2206) + + hidden_importsym goto 340 + hidden_funres goto 611 + ohidden_funres goto 641 + hidden_type goto 613 + hidden_type_misc goto 337 + hidden_type_func goto 339 + hidden_type_recv_chan goto 338 + +state 608 + hidden_constant: '(' hidden_literal '+'.hidden_literal ')' + + LLITERAL shift 460 + LNAME shift 10 + '-' shift 461 + '?' shift 12 + '@' shift 13 + . error + + sym goto 462 + hidden_importsym goto 11 + hidden_literal goto 642 + +state 609 + hidden_import: LCONST hidden_pkg_importsym hidden_type '=' hidden_constant ';'. (307) + + . reduce 307 (src line 2019) + + +state 610 + hidden_fndcl: hidden_pkg_importsym '(' ohidden_funarg_list ')' ohidden_funres. (207) + + . reduce 207 (src line 1467) + + +state 611 + ohidden_funres: hidden_funres. (336) + + . reduce 336 (src line 2210) + + +state 612 + hidden_funres: '('.ohidden_funarg_list ')' + ohidden_funarg_list: . (296) + + LNAME shift 10 + '?' shift 12 + '@' shift 13 + . reduce 296 (src line 1979) + + sym goto 357 + hidden_importsym goto 11 + hidden_funarg goto 356 + hidden_funarg_list goto 467 + ohidden_funarg_list goto 643 + +state 613 + hidden_funres: hidden_type. (338) + + . reduce 338 (src line 2217) + + +state 614 + hidden_fndcl: '(' hidden_funarg_list ')' sym '('.ohidden_funarg_list ')' ohidden_funres + ohidden_funarg_list: . (296) + + LNAME shift 10 + '?' shift 12 + '@' shift 13 + . reduce 296 (src line 1979) + + sym goto 357 + hidden_importsym goto 11 + hidden_funarg goto 356 + hidden_funarg_list goto 467 + ohidden_funarg_list goto 644 + +state 615 + hidden_funarg: sym LDDD hidden_type oliteral. (331) + + . reduce 331 (src line 2156) + + +state 616 + common_dcl: lconst '(' constdcl ';' constdcl_list osemi ')'. (33) + + . reduce 33 (src line 392) + + +state 617 + constdcl_list: constdcl_list ';' constdcl1. (223) + + . reduce 223 (src line 1594) + + +state 618 + fndcl: '(' oarg_type_list_ocomma ')' sym '(' oarg_type_list_ocomma.')' fnres + + ')' shift 645 + . error + + +state 619 + fnres: '(' oarg_type_list_ocomma ')'. (214) + + . reduce 214 (src line 1540) + + +state 620 + loop_body: LBODY $$65 stmt_list '}'. (66) + + . reduce 66 (src line 666) + + +state 621 + for_header: osimple_stmt ';' osimple_stmt ';' osimple_stmt. (70) + + . reduce 70 (src line 692) + + +state 622 + switch_stmt: LSWITCH $$88 if_header $$89 LBODY caseblock_list '}'. (90) + + . reduce 90 (src line 830) + + +state 623 + caseblock: case $$61 stmt_list. (62) + stmt_list: stmt_list.';' stmt + + ';' shift 416 + . reduce 62 (src line 633) + + +state 624 + case: LCASE expr_or_type_list ':'. (55) + + . reduce 55 (src line 535) + + +state 625 + case: LCASE expr_or_type_list '='.expr ':' + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 646 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 626 + case: LCASE expr_or_type_list LCOLAS.expr ':' + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 647 + fnliteral goto 73 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 627 + expr_or_type_list: expr_or_type_list ','.expr_or_type + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 179 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 178 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . error + + sym goto 123 + expr goto 173 + expr_or_type goto 511 + fnliteral goto 73 + name goto 69 + non_expr_type goto 174 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + recvchantype goto 175 + othertype goto 177 + fntype goto 176 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 628 + if_stmt: LIF $$78 if_header $$79 loop_body $$80 elseif_list.else + elseif_list: elseif_list.elseif + else: . (86) + + LELSE shift 650 + . reduce 86 (src line 805) + + elseif goto 649 + else goto 648 + +state 629 + pexpr_no_paren: pexpr '[' oexpr ':' oexpr ':' oexpr.']' + + ']' shift 651 + . error + + +state 630 + complitexpr: '{' start_complit.braced_keyval_list '}' + braced_keyval_list: . (284) + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '{' shift 403 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 284 (src line 1943) + + sym goto 123 + expr goto 402 + bare_complitexpr goto 401 + fnliteral goto 73 + keyval goto 400 + name goto 69 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + uexpr goto 55 + keyval_list goto 399 + braced_keyval_list goto 652 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 631 + bare_complitexpr: '{' start_complit braced_keyval_list '}'. (143) + + . reduce 143 (src line 1102) + + +state 632 + pexpr_no_paren: '(' expr_or_type ')' '{' start_complit braced_keyval_list '}'. (138) + + . reduce 138 (src line 1066) + + +state 633 + structdcl: '(' '*' embed ')' oliteral. (234) + + . reduce 234 (src line 1668) + + +state 634 + structdcl: '*' '(' embed ')' oliteral. (235) + + . reduce 235 (src line 1675) + + +state 635 + indcl: '(' oarg_type_list_ocomma ')' fnres. (242) + + . reduce 242 (src line 1730) + + +state 636 + hidden_type_misc: LMAP '[' hidden_type ']' hidden_type. (321) + + . reduce 321 (src line 2098) + + +state 637 + hidden_structdcl_list: hidden_structdcl_list ';' hidden_structdcl. (349) + + . reduce 349 (src line 2289) + + +state 638 + hidden_structdcl: sym hidden_type oliteral. (332) + + . reduce 332 (src line 2172) + + +state 639 + hidden_interfacedcl_list: hidden_interfacedcl_list ';' hidden_interfacedcl. (351) + + . reduce 351 (src line 2299) + + +state 640 + hidden_interfacedcl: sym '(' ohidden_funarg_list.')' ohidden_funres + + ')' shift 653 + . error + + +state 641 + hidden_type_func: LFUNC '(' ohidden_funarg_list ')' ohidden_funres. (329) + + . reduce 329 (src line 2141) + + +state 642 + hidden_constant: '(' hidden_literal '+' hidden_literal.')' + + ')' shift 654 + . error + + +state 643 + hidden_funres: '(' ohidden_funarg_list.')' + + ')' shift 655 + . error + + +state 644 + hidden_fndcl: '(' hidden_funarg_list ')' sym '(' ohidden_funarg_list.')' ohidden_funres + + ')' shift 656 + . error + + +state 645 + fndcl: '(' oarg_type_list_ocomma ')' sym '(' oarg_type_list_ocomma ')'.fnres + fnres: . (212) + + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 232 + '*' shift 196 + '(' shift 488 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 212 (src line 1531) + + sym goto 123 + dotname goto 493 + name goto 197 + fnres goto 657 + interfacetype goto 81 + structtype goto 80 + ptrtype goto 492 + recvchantype goto 489 + othertype goto 491 + fnret_type goto 487 + fntype goto 490 + hidden_importsym goto 11 + +state 646 + case: LCASE expr_or_type_list '=' expr.':' + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + + LANDAND shift 134 + LANDNOT shift 149 + LCOMM shift 152 + LEQ shift 135 + LGE shift 139 + LGT shift 140 + LLE shift 138 + LLSH shift 150 + LLT shift 137 + LNE shift 136 + LOROR shift 133 + LRSH shift 151 + '+' shift 141 + '-' shift 142 + '|' shift 143 + '^' shift 144 + '*' shift 145 + '/' shift 146 + '%' shift 147 + '&' shift 148 + ':' shift 658 + . error + + +state 647 + case: LCASE expr_or_type_list LCOLAS expr.':' + expr: expr.LOROR expr + expr: expr.LANDAND expr + expr: expr.LEQ expr + expr: expr.LNE expr + expr: expr.LLT expr + expr: expr.LLE expr + expr: expr.LGE expr + expr: expr.LGT expr + expr: expr.'+' expr + expr: expr.'-' expr + expr: expr.'|' expr + expr: expr.'^' expr + expr: expr.'*' expr + expr: expr.'/' expr + expr: expr.'%' expr + expr: expr.'&' expr + expr: expr.LANDNOT expr + expr: expr.LLSH expr + expr: expr.LRSH expr + expr: expr.LCOMM expr + + LANDAND shift 134 + LANDNOT shift 149 + LCOMM shift 152 + LEQ shift 135 + LGE shift 139 + LGT shift 140 + LLE shift 138 + LLSH shift 150 + LLT shift 137 + LNE shift 136 + LOROR shift 133 + LRSH shift 151 + '+' shift 141 + '-' shift 142 + '|' shift 143 + '^' shift 144 + '*' shift 145 + '/' shift 146 + '%' shift 147 + '&' shift 148 + ':' shift 659 + . error + + +state 648 + if_stmt: LIF $$78 if_header $$79 loop_body $$80 elseif_list else. (81) + + . reduce 81 (src line 765) + + +state 649 + elseif_list: elseif_list elseif. (85) + + . reduce 85 (src line 800) + + +state 650 + elseif: LELSE.LIF $$82 if_header loop_body + else: LELSE.compound_stmt + + LIF shift 660 + '{' shift 308 + . error + + compound_stmt goto 661 + +state 651 + pexpr_no_paren: pexpr '[' oexpr ':' oexpr ':' oexpr ']'. (133) + + . reduce 133 (src line 1036) + + +state 652 + complitexpr: '{' start_complit braced_keyval_list.'}' + + '}' shift 662 + . error + + +state 653 + hidden_interfacedcl: sym '(' ohidden_funarg_list ')'.ohidden_funres + ohidden_funres: . (335) + + LCHAN shift 347 + LFUNC shift 349 + LINTERFACE shift 345 + LMAP shift 343 + LNAME shift 341 + LSTRUCT shift 344 + LCOMM shift 348 + '*' shift 346 + '(' shift 612 + '[' shift 342 + '@' shift 13 + . reduce 335 (src line 2206) + + hidden_importsym goto 340 + hidden_funres goto 611 + ohidden_funres goto 663 + hidden_type goto 613 + hidden_type_misc goto 337 + hidden_type_func goto 339 + hidden_type_recv_chan goto 338 + +state 654 + hidden_constant: '(' hidden_literal '+' hidden_literal ')'. (343) + + . reduce 343 (src line 2259) + + +state 655 + hidden_funres: '(' ohidden_funarg_list ')'. (337) + + . reduce 337 (src line 2212) + + +state 656 + hidden_fndcl: '(' hidden_funarg_list ')' sym '(' ohidden_funarg_list ')'.ohidden_funres + ohidden_funres: . (335) + + LCHAN shift 347 + LFUNC shift 349 + LINTERFACE shift 345 + LMAP shift 343 + LNAME shift 341 + LSTRUCT shift 344 + LCOMM shift 348 + '*' shift 346 + '(' shift 612 + '[' shift 342 + '@' shift 13 + . reduce 335 (src line 2206) + + hidden_importsym goto 340 + hidden_funres goto 611 + ohidden_funres goto 664 + hidden_type goto 613 + hidden_type_misc goto 337 + hidden_type_func goto 339 + hidden_type_recv_chan goto 338 + +state 657 + fndcl: '(' oarg_type_list_ocomma ')' sym '(' oarg_type_list_ocomma ')' fnres. (206) + + . reduce 206 (src line 1430) + + +state 658 + case: LCASE expr_or_type_list '=' expr ':'. (56) + + . reduce 56 (src line 559) + + +state 659 + case: LCASE expr_or_type_list LCOLAS expr ':'. (57) + + . reduce 57 (src line 577) + + +state 660 + elseif: LELSE LIF.$$82 if_header loop_body + $$82: . (82) + + . reduce 82 (src line 782) + + $$82 goto 665 + +state 661 + else: LELSE compound_stmt. (87) + + . reduce 87 (src line 809) + + +state 662 + complitexpr: '{' start_complit braced_keyval_list '}'. (145) + + . reduce 145 (src line 1110) + + +state 663 + hidden_interfacedcl: sym '(' ohidden_funarg_list ')' ohidden_funres. (333) + + . reduce 333 (src line 2196) + + +state 664 + hidden_fndcl: '(' hidden_funarg_list ')' sym '(' ohidden_funarg_list ')' ohidden_funres. (208) + + . reduce 208 (src line 1493) + + +state 665 + elseif: LELSE LIF $$82.if_header loop_body + osimple_stmt: . (294) + + LLITERAL shift 68 + LCHAN shift 78 + LFUNC shift 124 + LINTERFACE shift 83 + LMAP shift 79 + LNAME shift 10 + LSTRUCT shift 82 + LCOMM shift 65 + '+' shift 60 + '-' shift 61 + '^' shift 64 + '*' shift 58 + '&' shift 59 + '(' shift 67 + '!' shift 62 + '~' shift 63 + '[' shift 77 + '?' shift 12 + '@' shift 13 + . reduce 294 (src line 1973) + + sym goto 123 + expr goto 48 + fnliteral goto 73 + if_header goto 666 + name goto 69 + osimple_stmt goto 286 + pexpr goto 57 + pexpr_no_paren goto 66 + pseudocall goto 70 + simple_stmt goto 282 + uexpr goto 55 + expr_list goto 49 + convtype goto 71 + comptype goto 72 + interfacetype goto 81 + structtype goto 80 + othertype goto 75 + fntype goto 74 + hidden_importsym goto 11 + fnlitdcl goto 76 + +state 666 + elseif: LELSE LIF $$82 if_header.loop_body + + LBODY shift 382 + . error + + loop_body goto 667 + +state 667 + elseif: LELSE LIF $$82 if_header loop_body. (83) + + . reduce 83 (src line 787) + + +76 terminals, 142 nonterminals +352 grammar rules, 668/2000 states +0 shift/reduce, 0 reduce/reduce conflicts reported +191 working sets used +memory: parser 3749/30000 +446 extra closures +3093 shift entries, 64 exceptions +603 goto entries +1650 entries saved by goto default +Optimizer space used: output 2282/30000 +2282 table entries, 722 zero +maximum spread: 76, maximum offset: 666 diff --git a/src/cmd/compile/internal/ppc64/cgen.go b/src/cmd/compile/internal/ppc64/cgen.go new file mode 100644 index 0000000000..37dd6cefb2 --- /dev/null +++ b/src/cmd/compile/internal/ppc64/cgen.go @@ -0,0 +1,149 @@ +// Copyright 2009 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 ppc64 + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/ppc64" +) + +func blockcopy(n, res *gc.Node, osrc, odst, w int64) { + // determine alignment. + // want to avoid unaligned access, so have to use + // smaller operations for less aligned types. + // for example moving [4]byte must use 4 MOVB not 1 MOVW. + align := int(n.Type.Align) + + var op int + switch align { + default: + gc.Fatal("sgen: invalid alignment %d for %v", align, n.Type) + + case 1: + op = ppc64.AMOVBU + + case 2: + op = ppc64.AMOVHU + + case 4: + op = ppc64.AMOVWZU // there is no lwau, only lwaux + + case 8: + op = ppc64.AMOVDU + } + + if w%int64(align) != 0 { + gc.Fatal("sgen: unaligned size %d (align=%d) for %v", w, align, n.Type) + } + c := int32(w / int64(align)) + + // if we are copying forward on the stack and + // the src and dst overlap, then reverse direction + dir := align + + if osrc < odst && int64(odst) < int64(osrc)+w { + dir = -dir + } + + var dst gc.Node + var src gc.Node + if n.Ullman >= res.Ullman { + gc.Agenr(n, &dst, res) // temporarily use dst + gc.Regalloc(&src, gc.Types[gc.Tptr], nil) + gins(ppc64.AMOVD, &dst, &src) + if res.Op == gc.ONAME { + gc.Gvardef(res) + } + gc.Agen(res, &dst) + } else { + if res.Op == gc.ONAME { + gc.Gvardef(res) + } + gc.Agenr(res, &dst, res) + gc.Agenr(n, &src, nil) + } + + var tmp gc.Node + gc.Regalloc(&tmp, gc.Types[gc.Tptr], nil) + + // set up end marker + var nend gc.Node + + // move src and dest to the end of block if necessary + if dir < 0 { + if c >= 4 { + gc.Regalloc(&nend, gc.Types[gc.Tptr], nil) + gins(ppc64.AMOVD, &src, &nend) + } + + p := gins(ppc64.AADD, nil, &src) + p.From.Type = obj.TYPE_CONST + p.From.Offset = w + + p = gins(ppc64.AADD, nil, &dst) + p.From.Type = obj.TYPE_CONST + p.From.Offset = w + } else { + p := gins(ppc64.AADD, nil, &src) + p.From.Type = obj.TYPE_CONST + p.From.Offset = int64(-dir) + + p = gins(ppc64.AADD, nil, &dst) + p.From.Type = obj.TYPE_CONST + p.From.Offset = int64(-dir) + + if c >= 4 { + gc.Regalloc(&nend, gc.Types[gc.Tptr], nil) + p := gins(ppc64.AMOVD, &src, &nend) + p.From.Type = obj.TYPE_ADDR + p.From.Offset = w + } + } + + // move + // TODO: enable duffcopy for larger copies. + if c >= 4 { + p := gins(op, &src, &tmp) + p.From.Type = obj.TYPE_MEM + p.From.Offset = int64(dir) + ploop := p + + p = gins(op, &tmp, &dst) + p.To.Type = obj.TYPE_MEM + p.To.Offset = int64(dir) + + p = gins(ppc64.ACMP, &src, &nend) + + gc.Patch(gc.Gbranch(ppc64.ABNE, nil, 0), ploop) + gc.Regfree(&nend) + } else { + // TODO(austin): Instead of generating ADD $-8,R8; ADD + // $-8,R7; n*(MOVDU 8(R8),R9; MOVDU R9,8(R7);) just + // generate the offsets directly and eliminate the + // ADDs. That will produce shorter, more + // pipeline-able code. + var p *obj.Prog + for { + tmp14 := c + c-- + if tmp14 <= 0 { + break + } + + p = gins(op, &src, &tmp) + p.From.Type = obj.TYPE_MEM + p.From.Offset = int64(dir) + + p = gins(op, &tmp, &dst) + p.To.Type = obj.TYPE_MEM + p.To.Offset = int64(dir) + } + } + + gc.Regfree(&dst) + gc.Regfree(&src) + gc.Regfree(&tmp) +} diff --git a/src/cmd/compile/internal/ppc64/galign.go b/src/cmd/compile/internal/ppc64/galign.go new file mode 100644 index 0000000000..73aef6fde9 --- /dev/null +++ b/src/cmd/compile/internal/ppc64/galign.go @@ -0,0 +1,100 @@ +// Copyright 2009 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 ppc64 + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/ppc64" +) + +var thechar int = '9' + +var thestring string = "ppc64" + +var thelinkarch *obj.LinkArch + +func linkarchinit() { + thestring = obj.Getgoarch() + gc.Thearch.Thestring = thestring + if thestring == "ppc64le" { + thelinkarch = &ppc64.Linkppc64le + } else { + thelinkarch = &ppc64.Linkppc64 + } + gc.Thearch.Thelinkarch = thelinkarch +} + +var MAXWIDTH int64 = 1 << 50 + +/* + * go declares several platform-specific type aliases: + * int, uint, and uintptr + */ +var typedefs = []gc.Typedef{ + gc.Typedef{"int", gc.TINT, gc.TINT64}, + gc.Typedef{"uint", gc.TUINT, gc.TUINT64}, + gc.Typedef{"uintptr", gc.TUINTPTR, gc.TUINT64}, +} + +func betypeinit() { + gc.Widthptr = 8 + gc.Widthint = 8 + gc.Widthreg = 8 +} + +func Main() { + gc.Thearch.Thechar = thechar + gc.Thearch.Thestring = thestring + gc.Thearch.Thelinkarch = thelinkarch + gc.Thearch.Typedefs = typedefs + gc.Thearch.REGSP = ppc64.REGSP + gc.Thearch.REGCTXT = ppc64.REGCTXT + gc.Thearch.REGCALLX = ppc64.REG_R3 + gc.Thearch.REGCALLX2 = ppc64.REG_R4 + gc.Thearch.REGRETURN = ppc64.REG_R3 + gc.Thearch.REGMIN = ppc64.REG_R0 + gc.Thearch.REGMAX = ppc64.REG_R31 + gc.Thearch.FREGMIN = ppc64.REG_F0 + gc.Thearch.FREGMAX = ppc64.REG_F31 + gc.Thearch.MAXWIDTH = MAXWIDTH + gc.Thearch.ReservedRegs = resvd + + gc.Thearch.Betypeinit = betypeinit + gc.Thearch.Cgen_hmul = cgen_hmul + gc.Thearch.Cgen_shift = cgen_shift + gc.Thearch.Clearfat = clearfat + gc.Thearch.Defframe = defframe + gc.Thearch.Dodiv = dodiv + gc.Thearch.Excise = excise + 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 + gc.Thearch.Linkarchinit = linkarchinit + gc.Thearch.Peep = peep + gc.Thearch.Proginfo = proginfo + gc.Thearch.Regtyp = regtyp + gc.Thearch.Sameaddr = sameaddr + gc.Thearch.Smallindir = smallindir + gc.Thearch.Stackaddr = stackaddr + gc.Thearch.Blockcopy = blockcopy + gc.Thearch.Sudoaddable = sudoaddable + gc.Thearch.Sudoclean = sudoclean + gc.Thearch.Excludedregs = excludedregs + gc.Thearch.RtoB = RtoB + gc.Thearch.FtoB = RtoB + gc.Thearch.BtoR = BtoR + gc.Thearch.BtoF = BtoF + gc.Thearch.Optoas = optoas + gc.Thearch.Doregbits = doregbits + gc.Thearch.Regnames = regnames + + gc.Main() + gc.Exit(0) +} diff --git a/src/cmd/compile/internal/ppc64/ggen.go b/src/cmd/compile/internal/ppc64/ggen.go new file mode 100644 index 0000000000..1b936b8a5f --- /dev/null +++ b/src/cmd/compile/internal/ppc64/ggen.go @@ -0,0 +1,558 @@ +// Copyright 2009 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 ppc64 + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/ppc64" + "fmt" +) + +func defframe(ptxt *obj.Prog) { + var n *gc.Node + + // fill in argument size, stack size + ptxt.To.Type = obj.TYPE_TEXTSIZE + + ptxt.To.Val = int32(gc.Rnd(gc.Curfn.Type.Argwid, int64(gc.Widthptr))) + frame := uint32(gc.Rnd(gc.Stksize+gc.Maxarg, int64(gc.Widthreg))) + ptxt.To.Offset = int64(frame) + + // insert code to zero ambiguously live variables + // so that the garbage collector only sees initialized values + // when it looks for pointers. + p := ptxt + + hi := int64(0) + lo := hi + + // 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.Name.Needzero { + continue + } + if n.Class != gc.PAUTO { + gc.Fatal("needzero class %d", n.Class) + } + if n.Type.Width%int64(gc.Widthptr) != 0 || n.Xoffset%int64(gc.Widthptr) != 0 || n.Type.Width == 0 { + gc.Fatal("var %v has size %d offset %d", gc.Nconv(n, obj.FmtLong), int(n.Type.Width), int(n.Xoffset)) + } + + if lo != hi && n.Xoffset+n.Type.Width >= lo-int64(2*gc.Widthreg) { + // merge with range we already have + lo = n.Xoffset + + continue + } + + // zero old range + p = zerorange(p, int64(frame), lo, hi) + + // set new range + hi = n.Xoffset + n.Type.Width + + lo = n.Xoffset + } + + // zero final range + zerorange(p, int64(frame), lo, hi) +} + +func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog { + cnt := hi - lo + if cnt == 0 { + return p + } + if cnt < int64(4*gc.Widthptr) { + for i := int64(0); i < cnt; i += int64(gc.Widthptr) { + p = appendpp(p, ppc64.AMOVD, obj.TYPE_REG, ppc64.REGZERO, 0, obj.TYPE_MEM, ppc64.REGSP, 8+frame+lo+i) + } + } else if cnt <= int64(128*gc.Widthptr) { + p = appendpp(p, ppc64.AADD, obj.TYPE_CONST, 0, 8+frame+lo-8, obj.TYPE_REG, ppc64.REGRT1, 0) + p.Reg = ppc64.REGSP + p = appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0) + f := gc.Sysfunc("duffzero") + gc.Naddr(&p.To, f) + gc.Afunclit(&p.To, f) + p.To.Offset = 4 * (128 - cnt/int64(gc.Widthptr)) + } else { + p = appendpp(p, ppc64.AMOVD, obj.TYPE_CONST, 0, 8+frame+lo-8, obj.TYPE_REG, ppc64.REGTMP, 0) + p = appendpp(p, ppc64.AADD, obj.TYPE_REG, ppc64.REGTMP, 0, obj.TYPE_REG, ppc64.REGRT1, 0) + p.Reg = ppc64.REGSP + p = appendpp(p, ppc64.AMOVD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, ppc64.REGTMP, 0) + p = appendpp(p, ppc64.AADD, obj.TYPE_REG, ppc64.REGTMP, 0, obj.TYPE_REG, ppc64.REGRT2, 0) + p.Reg = ppc64.REGRT1 + p = appendpp(p, ppc64.AMOVDU, obj.TYPE_REG, ppc64.REGZERO, 0, obj.TYPE_MEM, ppc64.REGRT1, int64(gc.Widthptr)) + p1 := p + p = appendpp(p, ppc64.ACMP, obj.TYPE_REG, ppc64.REGRT1, 0, obj.TYPE_REG, ppc64.REGRT2, 0) + p = appendpp(p, ppc64.ABNE, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0) + gc.Patch(p, p1) + } + + return p +} + +func appendpp(p *obj.Prog, as int, ftype int, freg int, foffset int64, ttype int, treg int, toffset int64) *obj.Prog { + q := gc.Ctxt.NewProg() + gc.Clearp(q) + q.As = int16(as) + q.Lineno = p.Lineno + q.From.Type = int16(ftype) + q.From.Reg = int16(freg) + q.From.Offset = foffset + q.To.Type = int16(ttype) + q.To.Reg = int16(treg) + q.To.Offset = toffset + q.Link = p.Link + p.Link = q + return q +} + +func ginsnop() { + var reg gc.Node + gc.Nodreg(®, gc.Types[gc.TINT], ppc64.REG_R0) + gins(ppc64.AOR, ®, ®) +} + +var panicdiv *gc.Node + +/* + * generate division. + * generates one of: + * res = nl / nr + * res = nl % nr + * according to op. + */ +func dodiv(op int, nl *gc.Node, nr *gc.Node, res *gc.Node) { + // Have to be careful about handling + // most negative int divided by -1 correctly. + // The hardware will generate undefined result. + // Also need to explicitly trap on division on zero, + // the hardware will silently generate undefined result. + // DIVW will leave unpredicable result in higher 32-bit, + // so always use DIVD/DIVDU. + t := nl.Type + + t0 := t + check := 0 + if gc.Issigned[t.Etype] { + check = 1 + if gc.Isconst(nl, gc.CTINT) && nl.Int() != -(1<= nr.Ullman { + gc.Cgen(nl, &tl) + gc.Cgen(nr, &tr) + } else { + gc.Cgen(nr, &tr) + gc.Cgen(nl, &tl) + } + + if t != t0 { + // Convert + tl2 := tl + + tr2 := tr + tl.Type = t + tr.Type = t + gmove(&tl2, &tl) + gmove(&tr2, &tr) + } + + // Handle divide-by-zero panic. + p1 := gins(optoas(gc.OCMP, t), &tr, nil) + + p1.To.Type = obj.TYPE_REG + p1.To.Reg = ppc64.REGZERO + p1 = gc.Gbranch(optoas(gc.ONE, t), nil, +1) + if panicdiv == nil { + panicdiv = gc.Sysfunc("panicdivide") + } + gc.Ginscall(panicdiv, -1) + gc.Patch(p1, gc.Pc) + + var p2 *obj.Prog + if check != 0 { + var nm1 gc.Node + gc.Nodconst(&nm1, t, -1) + gins(optoas(gc.OCMP, t), &tr, &nm1) + p1 := gc.Gbranch(optoas(gc.ONE, t), nil, +1) + if op == gc.ODIV { + // a / (-1) is -a. + gins(optoas(gc.OMINUS, t), nil, &tl) + + gmove(&tl, res) + } else { + // a % (-1) is 0. + var nz gc.Node + gc.Nodconst(&nz, t, 0) + + gmove(&nz, res) + } + + p2 = gc.Gbranch(obj.AJMP, nil, 0) + gc.Patch(p1, gc.Pc) + } + + p1 = gins(a, &tr, &tl) + if op == gc.ODIV { + gc.Regfree(&tr) + gmove(&tl, res) + } else { + // A%B = A-(A/B*B) + var tm gc.Node + gc.Regalloc(&tm, t, nil) + + // patch div to use the 3 register form + // TODO(minux): add gins3? + p1.Reg = p1.To.Reg + + p1.To.Reg = tm.Reg + gins(optoas(gc.OMUL, t), &tr, &tm) + gc.Regfree(&tr) + gins(optoas(gc.OSUB, t), &tm, &tl) + gc.Regfree(&tm) + gmove(&tl, res) + } + + gc.Regfree(&tl) + if check != 0 { + gc.Patch(p2, gc.Pc) + } +} + +/* + * generate high multiply: + * res = (nl*nr) >> width + */ +func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) { + // largest ullman on left. + if nl.Ullman < nr.Ullman { + tmp := (*gc.Node)(nl) + nl = nr + nr = tmp + } + + t := (*gc.Type)(nl.Type) + w := int(int(t.Width * 8)) + var n1 gc.Node + gc.Cgenr(nl, &n1, res) + var n2 gc.Node + gc.Cgenr(nr, &n2, nil) + switch gc.Simtype[t.Etype] { + case gc.TINT8, + gc.TINT16, + gc.TINT32: + gins(optoas(gc.OMUL, t), &n2, &n1) + p := (*obj.Prog)(gins(ppc64.ASRAD, nil, &n1)) + p.From.Type = obj.TYPE_CONST + p.From.Offset = int64(w) + + case gc.TUINT8, + gc.TUINT16, + gc.TUINT32: + gins(optoas(gc.OMUL, t), &n2, &n1) + p := (*obj.Prog)(gins(ppc64.ASRD, nil, &n1)) + p.From.Type = obj.TYPE_CONST + p.From.Offset = int64(w) + + case gc.TINT64, + gc.TUINT64: + if gc.Issigned[t.Etype] { + gins(ppc64.AMULHD, &n2, &n1) + } else { + gins(ppc64.AMULHDU, &n2, &n1) + } + + default: + gc.Fatal("cgen_hmul %v", t) + } + + gc.Cgen(&n1, res) + gc.Regfree(&n1) + gc.Regfree(&n2) +} + +/* + * generate shift according to op, one of: + * res = nl << nr + * res = nl >> nr + */ +func cgen_shift(op int, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) { + a := int(optoas(op, nl.Type)) + + if nr.Op == gc.OLITERAL { + var n1 gc.Node + gc.Regalloc(&n1, nl.Type, res) + gc.Cgen(nl, &n1) + sc := uint64(nr.Int()) + if sc >= uint64(nl.Type.Width*8) { + // large shift gets 2 shifts by width-1 + var n3 gc.Node + gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1) + + gins(a, &n3, &n1) + gins(a, &n3, &n1) + } else { + gins(a, nr, &n1) + } + gmove(&n1, res) + gc.Regfree(&n1) + return + } + + if nl.Ullman >= gc.UINF { + var n4 gc.Node + gc.Tempname(&n4, nl.Type) + gc.Cgen(nl, &n4) + nl = &n4 + } + + if nr.Ullman >= gc.UINF { + var n5 gc.Node + gc.Tempname(&n5, nr.Type) + gc.Cgen(nr, &n5) + nr = &n5 + } + + // Allow either uint32 or uint64 as shift type, + // to avoid unnecessary conversion from uint32 to uint64 + // just to do the comparison. + tcount := gc.Types[gc.Simtype[nr.Type.Etype]] + + if tcount.Etype < gc.TUINT32 { + tcount = gc.Types[gc.TUINT32] + } + + var n1 gc.Node + gc.Regalloc(&n1, nr.Type, nil) // to hold the shift type in CX + var n3 gc.Node + gc.Regalloc(&n3, tcount, &n1) // to clear high bits of CX + + var n2 gc.Node + gc.Regalloc(&n2, nl.Type, res) + + if nl.Ullman >= nr.Ullman { + gc.Cgen(nl, &n2) + gc.Cgen(nr, &n1) + gmove(&n1, &n3) + } else { + gc.Cgen(nr, &n1) + gmove(&n1, &n3) + gc.Cgen(nl, &n2) + } + + gc.Regfree(&n3) + + // test and fix up large shifts + if !bounded { + gc.Nodconst(&n3, tcount, nl.Type.Width*8) + gins(optoas(gc.OCMP, tcount), &n1, &n3) + p1 := (*obj.Prog)(gc.Gbranch(optoas(gc.OLT, tcount), nil, +1)) + if op == gc.ORSH && gc.Issigned[nl.Type.Etype] { + gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1) + gins(a, &n3, &n2) + } else { + gc.Nodconst(&n3, nl.Type, 0) + gmove(&n3, &n2) + } + + gc.Patch(p1, gc.Pc) + } + + gins(a, &n1, &n2) + + gmove(&n2, res) + + gc.Regfree(&n1) + gc.Regfree(&n2) +} + +func clearfat(nl *gc.Node) { + /* clear a fat object */ + if gc.Debug['g'] != 0 { + fmt.Printf("clearfat %v (%v, size: %d)\n", nl, nl.Type, nl.Type.Width) + } + + w := uint64(uint64(nl.Type.Width)) + + // Avoid taking the address for simple enough types. + if gc.Componentgen(nil, nl) { + return + } + + c := uint64(w % 8) // bytes + q := uint64(w / 8) // dwords + + if gc.Reginuse(ppc64.REGRT1) { + gc.Fatal("%v in use during clearfat", obj.Rconv(ppc64.REGRT1)) + } + + var r0 gc.Node + gc.Nodreg(&r0, gc.Types[gc.TUINT64], ppc64.REGZERO) + var dst gc.Node + gc.Nodreg(&dst, gc.Types[gc.Tptr], ppc64.REGRT1) + gc.Regrealloc(&dst) + gc.Agen(nl, &dst) + + var boff uint64 + if q > 128 { + p := gins(ppc64.ASUB, nil, &dst) + p.From.Type = obj.TYPE_CONST + p.From.Offset = 8 + + var end gc.Node + gc.Regalloc(&end, gc.Types[gc.Tptr], nil) + p = gins(ppc64.AMOVD, &dst, &end) + p.From.Type = obj.TYPE_ADDR + p.From.Offset = int64(q * 8) + + p = gins(ppc64.AMOVDU, &r0, &dst) + p.To.Type = obj.TYPE_MEM + p.To.Offset = 8 + pl := (*obj.Prog)(p) + + p = gins(ppc64.ACMP, &dst, &end) + gc.Patch(gc.Gbranch(ppc64.ABNE, nil, 0), pl) + + gc.Regfree(&end) + + // The loop leaves R3 on the last zeroed dword + boff = 8 + } else if q >= 4 { + p := gins(ppc64.ASUB, nil, &dst) + p.From.Type = obj.TYPE_CONST + p.From.Offset = 8 + f := (*gc.Node)(gc.Sysfunc("duffzero")) + p = gins(obj.ADUFFZERO, nil, f) + gc.Afunclit(&p.To, f) + + // 4 and 128 = magic constants: see ../../runtime/asm_ppc64x.s + p.To.Offset = int64(4 * (128 - q)) + + // duffzero leaves R3 on the last zeroed dword + boff = 8 + } else { + var p *obj.Prog + for t := uint64(0); t < q; t++ { + p = gins(ppc64.AMOVD, &r0, &dst) + p.To.Type = obj.TYPE_MEM + p.To.Offset = int64(8 * t) + } + + boff = 8 * q + } + + var p *obj.Prog + for t := uint64(0); t < c; t++ { + p = gins(ppc64.AMOVB, &r0, &dst) + p.To.Type = obj.TYPE_MEM + p.To.Offset = int64(t + boff) + } + + gc.Regfree(&dst) +} + +// Called after regopt and peep have run. +// Expand CHECKNIL pseudo-op into actual nil pointer check. +func expandchecks(firstp *obj.Prog) { + var p1 *obj.Prog + var p2 *obj.Prog + + for p := (*obj.Prog)(firstp); p != nil; p = p.Link { + if gc.Debug_checknil != 0 && gc.Ctxt.Debugvlog != 0 { + fmt.Printf("expandchecks: %v\n", p) + } + if p.As != obj.ACHECKNIL { + continue + } + if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers + gc.Warnl(int(p.Lineno), "generated nil check") + } + if p.From.Type != obj.TYPE_REG { + gc.Fatal("invalid nil check %v\n", p) + } + + /* + // check is + // TD $4, R0, arg (R0 is always zero) + // eqv. to: + // tdeq r0, arg + // NOTE: this needs special runtime support to make SIGTRAP recoverable. + reg = p->from.reg; + p->as = ATD; + p->from = p->to = p->from3 = zprog.from; + p->from.type = TYPE_CONST; + p->from.offset = 4; + p->from.reg = 0; + p->reg = REGZERO; + p->to.type = TYPE_REG; + p->to.reg = reg; + */ + // check is + // CMP arg, R0 + // BNE 2(PC) [likely] + // MOVD R0, 0(R0) + p1 = gc.Ctxt.NewProg() + + p2 = gc.Ctxt.NewProg() + gc.Clearp(p1) + gc.Clearp(p2) + p1.Link = p2 + p2.Link = p.Link + p.Link = p1 + p1.Lineno = p.Lineno + p2.Lineno = p.Lineno + p1.Pc = 9999 + p2.Pc = 9999 + p.As = ppc64.ACMP + p.To.Type = obj.TYPE_REG + p.To.Reg = ppc64.REGZERO + p1.As = ppc64.ABNE + + //p1->from.type = TYPE_CONST; + //p1->from.offset = 1; // likely + p1.To.Type = obj.TYPE_BRANCH + + p1.To.Val = p2.Link + + // crash by write to memory address 0. + p2.As = ppc64.AMOVD + + p2.From.Type = obj.TYPE_REG + p2.From.Reg = ppc64.REGZERO + p2.To.Type = obj.TYPE_MEM + p2.To.Reg = ppc64.REGZERO + p2.To.Offset = 0 + } +} + +// res = runtime.getg() +func getg(res *gc.Node) { + var n1 gc.Node + gc.Nodreg(&n1, res.Type, ppc64.REGG) + gmove(&n1, res) +} diff --git a/src/cmd/compile/internal/ppc64/gsubr.go b/src/cmd/compile/internal/ppc64/gsubr.go new file mode 100644 index 0000000000..2501972846 --- /dev/null +++ b/src/cmd/compile/internal/ppc64/gsubr.go @@ -0,0 +1,1031 @@ +// Derived from Inferno utils/6c/txt.c +// http://code.google.com/p/inferno-os/source/browse/utils/6c/txt.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ppc64 + +import ( + "cmd/compile/internal/big" + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/ppc64" + "fmt" +) + +var resvd = []int{ + ppc64.REGZERO, + ppc64.REGSP, // reserved for SP + // We need to preserve the C ABI TLS pointer because sigtramp + // may happen during C code and needs to access the g. C + // clobbers REGG, so if Go were to clobber REGTLS, sigtramp + // won't know which convention to use. By preserving REGTLS, + // we can just retrieve g from TLS when we aren't sure. + ppc64.REGTLS, + + // TODO(austin): Consolidate REGTLS and REGG? + ppc64.REGG, + ppc64.REGTMP, // REGTMP + ppc64.FREGCVI, + ppc64.FREGZERO, + ppc64.FREGHALF, + ppc64.FREGONE, + ppc64.FREGTWO, +} + +/* + * generate + * as $c, n + */ +func ginscon(as int, c int64, n2 *gc.Node) { + var n1 gc.Node + + gc.Nodconst(&n1, gc.Types[gc.TINT64], c) + + if as != ppc64.AMOVD && (c < -ppc64.BIG || c > ppc64.BIG) || n2.Op != gc.OREGISTER || as == ppc64.AMULLD { + // cannot have more than 16-bit of immediate in ADD, etc. + // instead, MOV into register first. + var ntmp gc.Node + gc.Regalloc(&ntmp, gc.Types[gc.TINT64], nil) + + rawgins(ppc64.AMOVD, &n1, &ntmp) + rawgins(as, &ntmp, n2) + gc.Regfree(&ntmp) + return + } + + rawgins(as, &n1, n2) +} + +/* + * generate + * as n, $c (CMP/CMPU) + */ +func ginscon2(as int, n2 *gc.Node, c int64) { + var n1 gc.Node + + gc.Nodconst(&n1, gc.Types[gc.TINT64], c) + + switch as { + default: + gc.Fatal("ginscon2") + + case ppc64.ACMP: + if -ppc64.BIG <= c && c <= ppc64.BIG { + rawgins(as, n2, &n1) + return + } + + case ppc64.ACMPU: + if 0 <= c && c <= 2*ppc64.BIG { + rawgins(as, n2, &n1) + return + } + } + + // MOV n1 into register first + var ntmp gc.Node + gc.Regalloc(&ntmp, gc.Types[gc.TINT64], nil) + + rawgins(ppc64.AMOVD, &n1, &ntmp) + rawgins(as, n2, &ntmp) + 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, n2.Int()) + } 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 +var ( + bigi gc.Node + bigf gc.Node + bignodes_did bool +) + +func bignodes() { + if bignodes_did { + return + } + bignodes_did = true + + var i big.Int + i.SetInt64(1) + i.Lsh(&i, 63) + + gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0) + bigi.SetBigInt(&i) + + bigi.Convconst(&bigf, gc.Types[gc.TFLOAT64]) +} + +/* + * generate move: + * t = f + * hard part is conversions. + */ +func gmove(f *gc.Node, t *gc.Node) { + if gc.Debug['M'] != 0 { + fmt.Printf("gmove %v -> %v\n", gc.Nconv(f, obj.FmtLong), gc.Nconv(t, obj.FmtLong)) + } + + ft := int(gc.Simsimtype(f.Type)) + tt := int(gc.Simsimtype(t.Type)) + cvt := (*gc.Type)(t.Type) + + if gc.Iscomplex[ft] || gc.Iscomplex[tt] { + gc.Complexmove(f, t) + return + } + + // cannot have two memory operands + var r2 gc.Node + var r1 gc.Node + var a int + if gc.Ismem(f) && gc.Ismem(t) { + goto hard + } + + // convert constant to desired type + if f.Op == gc.OLITERAL { + var con gc.Node + switch tt { + default: + f.Convconst(&con, t.Type) + + case gc.TINT32, + gc.TINT16, + gc.TINT8: + var con gc.Node + f.Convconst(&con, gc.Types[gc.TINT64]) + var r1 gc.Node + gc.Regalloc(&r1, con.Type, t) + gins(ppc64.AMOVD, &con, &r1) + gmove(&r1, t) + gc.Regfree(&r1) + return + + case gc.TUINT32, + gc.TUINT16, + gc.TUINT8: + var con gc.Node + f.Convconst(&con, gc.Types[gc.TUINT64]) + var r1 gc.Node + gc.Regalloc(&r1, con.Type, t) + gins(ppc64.AMOVD, &con, &r1) + gmove(&r1, t) + gc.Regfree(&r1) + return + } + + f = &con + ft = tt // so big switch will choose a simple mov + + // constants can't move directly to memory. + if gc.Ismem(t) { + goto hard + } + } + + // float constants come from memory. + //if(isfloat[tt]) + // goto hard; + + // 64-bit immediates are also from memory. + //if(isint[tt]) + // goto hard; + //// 64-bit immediates are really 32-bit sign-extended + //// unless moving into a register. + //if(isint[tt]) { + // if(mpcmpfixfix(con.val.u.xval, minintval[TINT32]) < 0) + // goto hard; + // if(mpcmpfixfix(con.val.u.xval, maxintval[TINT32]) > 0) + // goto hard; + //} + + // value -> value copy, only one memory operand. + // figure out the instruction to use. + // break out of switch for one-instruction gins. + // goto rdst for "destination must be register". + // goto hard for "convert to cvt type first". + // otherwise handle and return. + + switch uint32(ft)<<16 | uint32(tt) { + default: + gc.Fatal("gmove %v -> %v", gc.Tconv(f.Type, obj.FmtLong), gc.Tconv(t.Type, obj.FmtLong)) + + /* + * integer copy and truncate + */ + case gc.TINT8<<16 | gc.TINT8, // same size + gc.TUINT8<<16 | gc.TINT8, + gc.TINT16<<16 | gc.TINT8, + // truncate + gc.TUINT16<<16 | gc.TINT8, + gc.TINT32<<16 | gc.TINT8, + gc.TUINT32<<16 | gc.TINT8, + gc.TINT64<<16 | gc.TINT8, + gc.TUINT64<<16 | gc.TINT8: + a = ppc64.AMOVB + + case gc.TINT8<<16 | gc.TUINT8, // same size + gc.TUINT8<<16 | gc.TUINT8, + gc.TINT16<<16 | gc.TUINT8, + // truncate + gc.TUINT16<<16 | gc.TUINT8, + gc.TINT32<<16 | gc.TUINT8, + gc.TUINT32<<16 | gc.TUINT8, + gc.TINT64<<16 | gc.TUINT8, + gc.TUINT64<<16 | gc.TUINT8: + a = ppc64.AMOVBZ + + case gc.TINT16<<16 | gc.TINT16, // same size + gc.TUINT16<<16 | gc.TINT16, + gc.TINT32<<16 | gc.TINT16, + // truncate + gc.TUINT32<<16 | gc.TINT16, + gc.TINT64<<16 | gc.TINT16, + gc.TUINT64<<16 | gc.TINT16: + a = ppc64.AMOVH + + case gc.TINT16<<16 | gc.TUINT16, // same size + gc.TUINT16<<16 | gc.TUINT16, + gc.TINT32<<16 | gc.TUINT16, + // truncate + gc.TUINT32<<16 | gc.TUINT16, + gc.TINT64<<16 | gc.TUINT16, + gc.TUINT64<<16 | gc.TUINT16: + a = ppc64.AMOVHZ + + case gc.TINT32<<16 | gc.TINT32, // same size + gc.TUINT32<<16 | gc.TINT32, + gc.TINT64<<16 | gc.TINT32, + // truncate + gc.TUINT64<<16 | gc.TINT32: + a = ppc64.AMOVW + + case gc.TINT32<<16 | gc.TUINT32, // same size + gc.TUINT32<<16 | gc.TUINT32, + gc.TINT64<<16 | gc.TUINT32, + gc.TUINT64<<16 | gc.TUINT32: + a = ppc64.AMOVWZ + + case gc.TINT64<<16 | gc.TINT64, // same size + gc.TINT64<<16 | gc.TUINT64, + gc.TUINT64<<16 | gc.TINT64, + gc.TUINT64<<16 | gc.TUINT64: + a = ppc64.AMOVD + + /* + * integer up-conversions + */ + case gc.TINT8<<16 | gc.TINT16, // sign extend int8 + gc.TINT8<<16 | gc.TUINT16, + gc.TINT8<<16 | gc.TINT32, + gc.TINT8<<16 | gc.TUINT32, + gc.TINT8<<16 | gc.TINT64, + gc.TINT8<<16 | gc.TUINT64: + a = ppc64.AMOVB + + goto rdst + + case gc.TUINT8<<16 | gc.TINT16, // zero extend uint8 + gc.TUINT8<<16 | gc.TUINT16, + gc.TUINT8<<16 | gc.TINT32, + gc.TUINT8<<16 | gc.TUINT32, + gc.TUINT8<<16 | gc.TINT64, + gc.TUINT8<<16 | gc.TUINT64: + a = ppc64.AMOVBZ + + goto rdst + + case gc.TINT16<<16 | gc.TINT32, // sign extend int16 + gc.TINT16<<16 | gc.TUINT32, + gc.TINT16<<16 | gc.TINT64, + gc.TINT16<<16 | gc.TUINT64: + a = ppc64.AMOVH + + goto rdst + + case gc.TUINT16<<16 | gc.TINT32, // zero extend uint16 + gc.TUINT16<<16 | gc.TUINT32, + gc.TUINT16<<16 | gc.TINT64, + gc.TUINT16<<16 | gc.TUINT64: + a = ppc64.AMOVHZ + + goto rdst + + case gc.TINT32<<16 | gc.TINT64, // sign extend int32 + gc.TINT32<<16 | gc.TUINT64: + a = ppc64.AMOVW + + goto rdst + + case gc.TUINT32<<16 | gc.TINT64, // zero extend uint32 + gc.TUINT32<<16 | gc.TUINT64: + a = ppc64.AMOVWZ + + goto rdst + + //warn("gmove: convert float to int not implemented: %N -> %N\n", f, t); + //return; + // algorithm is: + // if small enough, use native float64 -> int64 conversion. + // otherwise, subtract 2^63, convert, and add it back. + /* + * float to integer + */ + case gc.TFLOAT32<<16 | gc.TINT32, + gc.TFLOAT64<<16 | gc.TINT32, + gc.TFLOAT32<<16 | gc.TINT64, + gc.TFLOAT64<<16 | gc.TINT64, + gc.TFLOAT32<<16 | gc.TINT16, + gc.TFLOAT32<<16 | gc.TINT8, + gc.TFLOAT32<<16 | gc.TUINT16, + gc.TFLOAT32<<16 | gc.TUINT8, + gc.TFLOAT64<<16 | gc.TINT16, + gc.TFLOAT64<<16 | gc.TINT8, + gc.TFLOAT64<<16 | gc.TUINT16, + gc.TFLOAT64<<16 | gc.TUINT8, + gc.TFLOAT32<<16 | gc.TUINT32, + gc.TFLOAT64<<16 | gc.TUINT32, + gc.TFLOAT32<<16 | gc.TUINT64, + gc.TFLOAT64<<16 | gc.TUINT64: + bignodes() + + var r1 gc.Node + gc.Regalloc(&r1, gc.Types[ft], f) + gmove(f, &r1) + if tt == gc.TUINT64 { + gc.Regalloc(&r2, gc.Types[gc.TFLOAT64], nil) + gmove(&bigf, &r2) + gins(ppc64.AFCMPU, &r1, &r2) + p1 := (*obj.Prog)(gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TFLOAT64]), nil, +1)) + gins(ppc64.AFSUB, &r2, &r1) + gc.Patch(p1, gc.Pc) + gc.Regfree(&r2) + } + + gc.Regalloc(&r2, gc.Types[gc.TFLOAT64], nil) + var r3 gc.Node + gc.Regalloc(&r3, gc.Types[gc.TINT64], t) + gins(ppc64.AFCTIDZ, &r1, &r2) + p1 := (*obj.Prog)(gins(ppc64.AFMOVD, &r2, nil)) + p1.To.Type = obj.TYPE_MEM + p1.To.Reg = ppc64.REGSP + p1.To.Offset = -8 + p1 = gins(ppc64.AMOVD, nil, &r3) + p1.From.Type = obj.TYPE_MEM + p1.From.Reg = ppc64.REGSP + p1.From.Offset = -8 + gc.Regfree(&r2) + gc.Regfree(&r1) + if tt == gc.TUINT64 { + p1 := (*obj.Prog)(gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TFLOAT64]), nil, +1)) // use CR0 here again + gc.Nodreg(&r1, gc.Types[gc.TINT64], ppc64.REGTMP) + gins(ppc64.AMOVD, &bigi, &r1) + gins(ppc64.AADD, &r1, &r3) + gc.Patch(p1, gc.Pc) + } + + gmove(&r3, t) + gc.Regfree(&r3) + return + + //warn("gmove: convert int to float not implemented: %N -> %N\n", f, t); + //return; + // algorithm is: + // if small enough, use native int64 -> uint64 conversion. + // otherwise, halve (rounding to odd?), convert, and double. + /* + * integer to float + */ + case gc.TINT32<<16 | gc.TFLOAT32, + gc.TINT32<<16 | gc.TFLOAT64, + gc.TINT64<<16 | gc.TFLOAT32, + gc.TINT64<<16 | gc.TFLOAT64, + gc.TINT16<<16 | gc.TFLOAT32, + gc.TINT16<<16 | gc.TFLOAT64, + gc.TINT8<<16 | gc.TFLOAT32, + gc.TINT8<<16 | gc.TFLOAT64, + gc.TUINT16<<16 | gc.TFLOAT32, + gc.TUINT16<<16 | gc.TFLOAT64, + gc.TUINT8<<16 | gc.TFLOAT32, + gc.TUINT8<<16 | gc.TFLOAT64, + gc.TUINT32<<16 | gc.TFLOAT32, + gc.TUINT32<<16 | gc.TFLOAT64, + gc.TUINT64<<16 | gc.TFLOAT32, + gc.TUINT64<<16 | gc.TFLOAT64: + bignodes() + + var r1 gc.Node + gc.Regalloc(&r1, gc.Types[gc.TINT64], nil) + gmove(f, &r1) + if ft == gc.TUINT64 { + gc.Nodreg(&r2, gc.Types[gc.TUINT64], ppc64.REGTMP) + gmove(&bigi, &r2) + gins(ppc64.ACMPU, &r1, &r2) + p1 := (*obj.Prog)(gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT64]), nil, +1)) + p2 := (*obj.Prog)(gins(ppc64.ASRD, nil, &r1)) + p2.From.Type = obj.TYPE_CONST + p2.From.Offset = 1 + gc.Patch(p1, gc.Pc) + } + + gc.Regalloc(&r2, gc.Types[gc.TFLOAT64], t) + p1 := (*obj.Prog)(gins(ppc64.AMOVD, &r1, nil)) + p1.To.Type = obj.TYPE_MEM + p1.To.Reg = ppc64.REGSP + p1.To.Offset = -8 + p1 = gins(ppc64.AFMOVD, nil, &r2) + p1.From.Type = obj.TYPE_MEM + p1.From.Reg = ppc64.REGSP + p1.From.Offset = -8 + gins(ppc64.AFCFID, &r2, &r2) + gc.Regfree(&r1) + if ft == gc.TUINT64 { + p1 := (*obj.Prog)(gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT64]), nil, +1)) // use CR0 here again + gc.Nodreg(&r1, gc.Types[gc.TFLOAT64], ppc64.FREGTWO) + gins(ppc64.AFMUL, &r1, &r2) + gc.Patch(p1, gc.Pc) + } + + gmove(&r2, t) + gc.Regfree(&r2) + return + + /* + * float to float + */ + case gc.TFLOAT32<<16 | gc.TFLOAT32: + a = ppc64.AFMOVS + + case gc.TFLOAT64<<16 | gc.TFLOAT64: + a = ppc64.AFMOVD + + case gc.TFLOAT32<<16 | gc.TFLOAT64: + a = ppc64.AFMOVS + goto rdst + + case gc.TFLOAT64<<16 | gc.TFLOAT32: + a = ppc64.AFRSP + goto rdst + } + + gins(a, f, t) + return + + // requires register destination +rdst: + { + gc.Regalloc(&r1, t.Type, t) + + gins(a, f, &r1) + gmove(&r1, t) + gc.Regfree(&r1) + return + } + + // requires register intermediate +hard: + gc.Regalloc(&r1, cvt, t) + + gmove(f, &r1) + gmove(&r1, t) + gc.Regfree(&r1) + return +} + +func intLiteral(n *gc.Node) (x int64, ok bool) { + switch { + case n == nil: + return + case gc.Isconst(n, gc.CTINT): + return n.Int(), true + case gc.Isconst(n, gc.CTBOOL): + return int64(obj.Bool2int(n.Bool())), true + } + return +} + +// gins is called by the front end. +// It synthesizes some multiple-instruction sequences +// so the front end can stay simpler. +func gins(as int, f, t *gc.Node) *obj.Prog { + if as >= obj.A_ARCHSPECIFIC { + if x, ok := intLiteral(f); ok { + ginscon(as, x, t) + return nil // caller must not use + } + } + if as == ppc64.ACMP || as == ppc64.ACMPU { + if x, ok := intLiteral(t); ok { + ginscon2(as, f, x) + return nil // caller must not use + } + } + return rawgins(as, f, t) +} + +/* + * generate one instruction: + * as f, t + */ +func rawgins(as int, f *gc.Node, t *gc.Node) *obj.Prog { + // TODO(austin): Add self-move test like in 6g (but be careful + // of truncation moves) + + p := gc.Prog(as) + gc.Naddr(&p.From, f) + gc.Naddr(&p.To, t) + + switch as { + case obj.ACALL: + if p.To.Type == obj.TYPE_REG && p.To.Reg != ppc64.REG_CTR { + // Allow front end to emit CALL REG, and rewrite into MOV REG, CTR; CALL CTR. + pp := gc.Prog(as) + pp.From = p.From + pp.To.Type = obj.TYPE_REG + pp.To.Reg = ppc64.REG_CTR + + p.As = ppc64.AMOVD + p.From = p.To + p.To.Type = obj.TYPE_REG + p.To.Reg = ppc64.REG_CTR + + if gc.Debug['g'] != 0 { + fmt.Printf("%v\n", p) + fmt.Printf("%v\n", pp) + } + + return pp + } + + // Bad things the front end has done to us. Crash to find call stack. + case ppc64.AAND, ppc64.AMULLD: + if p.From.Type == obj.TYPE_CONST { + gc.Debug['h'] = 1 + gc.Fatal("bad inst: %v", p) + } + case ppc64.ACMP, ppc64.ACMPU: + if p.From.Type == obj.TYPE_MEM || p.To.Type == obj.TYPE_MEM { + gc.Debug['h'] = 1 + gc.Fatal("bad inst: %v", p) + } + } + + if gc.Debug['g'] != 0 { + fmt.Printf("%v\n", p) + } + + w := int32(0) + switch as { + case ppc64.AMOVB, + ppc64.AMOVBU, + ppc64.AMOVBZ, + ppc64.AMOVBZU: + w = 1 + + case ppc64.AMOVH, + ppc64.AMOVHU, + ppc64.AMOVHZ, + ppc64.AMOVHZU: + w = 2 + + case ppc64.AMOVW, + ppc64.AMOVWU, + ppc64.AMOVWZ, + ppc64.AMOVWZU: + w = 4 + + case ppc64.AMOVD, + ppc64.AMOVDU: + if p.From.Type == obj.TYPE_CONST || p.From.Type == obj.TYPE_ADDR { + break + } + w = 8 + } + + if w != 0 && ((f != nil && p.From.Width < int64(w)) || (t != nil && p.To.Type != obj.TYPE_REG && p.To.Width > int64(w))) { + gc.Dump("f", f) + gc.Dump("t", t) + gc.Fatal("bad width: %v (%d, %d)\n", p, p.From.Width, p.To.Width) + } + + return p +} + +/* + * return Axxx for Oxxx on type t. + */ +func optoas(op int, t *gc.Type) int { + if t == nil { + gc.Fatal("optoas: t is nil") + } + + a := int(obj.AXXX) + switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) { + default: + gc.Fatal("optoas: no entry for op=%v type=%v", gc.Oconv(int(op), 0), t) + + case gc.OEQ<<16 | gc.TBOOL, + gc.OEQ<<16 | gc.TINT8, + gc.OEQ<<16 | gc.TUINT8, + gc.OEQ<<16 | gc.TINT16, + gc.OEQ<<16 | gc.TUINT16, + gc.OEQ<<16 | gc.TINT32, + gc.OEQ<<16 | gc.TUINT32, + gc.OEQ<<16 | gc.TINT64, + gc.OEQ<<16 | gc.TUINT64, + gc.OEQ<<16 | gc.TPTR32, + gc.OEQ<<16 | gc.TPTR64, + gc.OEQ<<16 | gc.TFLOAT32, + gc.OEQ<<16 | gc.TFLOAT64: + a = ppc64.ABEQ + + case gc.ONE<<16 | gc.TBOOL, + gc.ONE<<16 | gc.TINT8, + gc.ONE<<16 | gc.TUINT8, + gc.ONE<<16 | gc.TINT16, + gc.ONE<<16 | gc.TUINT16, + gc.ONE<<16 | gc.TINT32, + gc.ONE<<16 | gc.TUINT32, + gc.ONE<<16 | gc.TINT64, + gc.ONE<<16 | gc.TUINT64, + gc.ONE<<16 | gc.TPTR32, + gc.ONE<<16 | gc.TPTR64, + gc.ONE<<16 | gc.TFLOAT32, + gc.ONE<<16 | gc.TFLOAT64: + a = ppc64.ABNE + + case gc.OLT<<16 | gc.TINT8, // ACMP + gc.OLT<<16 | gc.TINT16, + gc.OLT<<16 | gc.TINT32, + gc.OLT<<16 | gc.TINT64, + gc.OLT<<16 | gc.TUINT8, + // ACMPU + gc.OLT<<16 | gc.TUINT16, + gc.OLT<<16 | gc.TUINT32, + gc.OLT<<16 | gc.TUINT64, + gc.OLT<<16 | gc.TFLOAT32, + // AFCMPU + gc.OLT<<16 | gc.TFLOAT64: + a = ppc64.ABLT + + case gc.OLE<<16 | gc.TINT8, // ACMP + gc.OLE<<16 | gc.TINT16, + gc.OLE<<16 | gc.TINT32, + gc.OLE<<16 | gc.TINT64, + gc.OLE<<16 | gc.TUINT8, + // ACMPU + gc.OLE<<16 | gc.TUINT16, + gc.OLE<<16 | gc.TUINT32, + gc.OLE<<16 | gc.TUINT64: + // No OLE for floats, because it mishandles NaN. + // Front end must reverse comparison or use OLT and OEQ together. + a = ppc64.ABLE + + case gc.OGT<<16 | gc.TINT8, + gc.OGT<<16 | gc.TINT16, + gc.OGT<<16 | gc.TINT32, + gc.OGT<<16 | gc.TINT64, + gc.OGT<<16 | gc.TUINT8, + gc.OGT<<16 | gc.TUINT16, + gc.OGT<<16 | gc.TUINT32, + gc.OGT<<16 | gc.TUINT64, + gc.OGT<<16 | gc.TFLOAT32, + gc.OGT<<16 | gc.TFLOAT64: + a = ppc64.ABGT + + case gc.OGE<<16 | gc.TINT8, + gc.OGE<<16 | gc.TINT16, + gc.OGE<<16 | gc.TINT32, + gc.OGE<<16 | gc.TINT64, + gc.OGE<<16 | gc.TUINT8, + gc.OGE<<16 | gc.TUINT16, + gc.OGE<<16 | gc.TUINT32, + gc.OGE<<16 | gc.TUINT64: + // No OGE for floats, because it mishandles NaN. + // Front end must reverse comparison or use OLT and OEQ together. + a = ppc64.ABGE + + case gc.OCMP<<16 | gc.TBOOL, + gc.OCMP<<16 | gc.TINT8, + gc.OCMP<<16 | gc.TINT16, + gc.OCMP<<16 | gc.TINT32, + gc.OCMP<<16 | gc.TPTR32, + gc.OCMP<<16 | gc.TINT64: + a = ppc64.ACMP + + case gc.OCMP<<16 | gc.TUINT8, + gc.OCMP<<16 | gc.TUINT16, + gc.OCMP<<16 | gc.TUINT32, + gc.OCMP<<16 | gc.TUINT64, + gc.OCMP<<16 | gc.TPTR64: + a = ppc64.ACMPU + + case gc.OCMP<<16 | gc.TFLOAT32, + gc.OCMP<<16 | gc.TFLOAT64: + a = ppc64.AFCMPU + + case gc.OAS<<16 | gc.TBOOL, + gc.OAS<<16 | gc.TINT8: + a = ppc64.AMOVB + + case gc.OAS<<16 | gc.TUINT8: + a = ppc64.AMOVBZ + + case gc.OAS<<16 | gc.TINT16: + a = ppc64.AMOVH + + case gc.OAS<<16 | gc.TUINT16: + a = ppc64.AMOVHZ + + case gc.OAS<<16 | gc.TINT32: + a = ppc64.AMOVW + + case gc.OAS<<16 | gc.TUINT32, + gc.OAS<<16 | gc.TPTR32: + a = ppc64.AMOVWZ + + case gc.OAS<<16 | gc.TINT64, + gc.OAS<<16 | gc.TUINT64, + gc.OAS<<16 | gc.TPTR64: + a = ppc64.AMOVD + + case gc.OAS<<16 | gc.TFLOAT32: + a = ppc64.AFMOVS + + case gc.OAS<<16 | gc.TFLOAT64: + a = ppc64.AFMOVD + + case gc.OADD<<16 | gc.TINT8, + gc.OADD<<16 | gc.TUINT8, + gc.OADD<<16 | gc.TINT16, + gc.OADD<<16 | gc.TUINT16, + gc.OADD<<16 | gc.TINT32, + gc.OADD<<16 | gc.TUINT32, + gc.OADD<<16 | gc.TPTR32, + gc.OADD<<16 | gc.TINT64, + gc.OADD<<16 | gc.TUINT64, + gc.OADD<<16 | gc.TPTR64: + a = ppc64.AADD + + case gc.OADD<<16 | gc.TFLOAT32: + a = ppc64.AFADDS + + case gc.OADD<<16 | gc.TFLOAT64: + a = ppc64.AFADD + + case gc.OSUB<<16 | gc.TINT8, + gc.OSUB<<16 | gc.TUINT8, + gc.OSUB<<16 | gc.TINT16, + gc.OSUB<<16 | gc.TUINT16, + gc.OSUB<<16 | gc.TINT32, + gc.OSUB<<16 | gc.TUINT32, + gc.OSUB<<16 | gc.TPTR32, + gc.OSUB<<16 | gc.TINT64, + gc.OSUB<<16 | gc.TUINT64, + gc.OSUB<<16 | gc.TPTR64: + a = ppc64.ASUB + + case gc.OSUB<<16 | gc.TFLOAT32: + a = ppc64.AFSUBS + + case gc.OSUB<<16 | gc.TFLOAT64: + a = ppc64.AFSUB + + case gc.OMINUS<<16 | gc.TINT8, + gc.OMINUS<<16 | gc.TUINT8, + gc.OMINUS<<16 | gc.TINT16, + gc.OMINUS<<16 | gc.TUINT16, + gc.OMINUS<<16 | gc.TINT32, + gc.OMINUS<<16 | gc.TUINT32, + gc.OMINUS<<16 | gc.TPTR32, + gc.OMINUS<<16 | gc.TINT64, + gc.OMINUS<<16 | gc.TUINT64, + gc.OMINUS<<16 | gc.TPTR64: + a = ppc64.ANEG + + case gc.OAND<<16 | gc.TINT8, + gc.OAND<<16 | gc.TUINT8, + gc.OAND<<16 | gc.TINT16, + gc.OAND<<16 | gc.TUINT16, + gc.OAND<<16 | gc.TINT32, + gc.OAND<<16 | gc.TUINT32, + gc.OAND<<16 | gc.TPTR32, + gc.OAND<<16 | gc.TINT64, + gc.OAND<<16 | gc.TUINT64, + gc.OAND<<16 | gc.TPTR64: + a = ppc64.AAND + + case gc.OOR<<16 | gc.TINT8, + gc.OOR<<16 | gc.TUINT8, + gc.OOR<<16 | gc.TINT16, + gc.OOR<<16 | gc.TUINT16, + gc.OOR<<16 | gc.TINT32, + gc.OOR<<16 | gc.TUINT32, + gc.OOR<<16 | gc.TPTR32, + gc.OOR<<16 | gc.TINT64, + gc.OOR<<16 | gc.TUINT64, + gc.OOR<<16 | gc.TPTR64: + a = ppc64.AOR + + case gc.OXOR<<16 | gc.TINT8, + gc.OXOR<<16 | gc.TUINT8, + gc.OXOR<<16 | gc.TINT16, + gc.OXOR<<16 | gc.TUINT16, + gc.OXOR<<16 | gc.TINT32, + gc.OXOR<<16 | gc.TUINT32, + gc.OXOR<<16 | gc.TPTR32, + gc.OXOR<<16 | gc.TINT64, + gc.OXOR<<16 | gc.TUINT64, + gc.OXOR<<16 | gc.TPTR64: + a = ppc64.AXOR + + // TODO(minux): handle rotates + //case CASE(OLROT, TINT8): + //case CASE(OLROT, TUINT8): + //case CASE(OLROT, TINT16): + //case CASE(OLROT, TUINT16): + //case CASE(OLROT, TINT32): + //case CASE(OLROT, TUINT32): + //case CASE(OLROT, TPTR32): + //case CASE(OLROT, TINT64): + //case CASE(OLROT, TUINT64): + //case CASE(OLROT, TPTR64): + // a = 0//???; RLDC? + // break; + + case gc.OLSH<<16 | gc.TINT8, + gc.OLSH<<16 | gc.TUINT8, + gc.OLSH<<16 | gc.TINT16, + gc.OLSH<<16 | gc.TUINT16, + gc.OLSH<<16 | gc.TINT32, + gc.OLSH<<16 | gc.TUINT32, + gc.OLSH<<16 | gc.TPTR32, + gc.OLSH<<16 | gc.TINT64, + gc.OLSH<<16 | gc.TUINT64, + gc.OLSH<<16 | gc.TPTR64: + a = ppc64.ASLD + + case gc.ORSH<<16 | gc.TUINT8, + gc.ORSH<<16 | gc.TUINT16, + gc.ORSH<<16 | gc.TUINT32, + gc.ORSH<<16 | gc.TPTR32, + gc.ORSH<<16 | gc.TUINT64, + gc.ORSH<<16 | gc.TPTR64: + a = ppc64.ASRD + + case gc.ORSH<<16 | gc.TINT8, + gc.ORSH<<16 | gc.TINT16, + gc.ORSH<<16 | gc.TINT32, + gc.ORSH<<16 | gc.TINT64: + a = ppc64.ASRAD + + // TODO(minux): handle rotates + //case CASE(ORROTC, TINT8): + //case CASE(ORROTC, TUINT8): + //case CASE(ORROTC, TINT16): + //case CASE(ORROTC, TUINT16): + //case CASE(ORROTC, TINT32): + //case CASE(ORROTC, TUINT32): + //case CASE(ORROTC, TINT64): + //case CASE(ORROTC, TUINT64): + // a = 0//??? RLDC?? + // break; + + case gc.OHMUL<<16 | gc.TINT64: + a = ppc64.AMULHD + + case gc.OHMUL<<16 | gc.TUINT64, + gc.OHMUL<<16 | gc.TPTR64: + a = ppc64.AMULHDU + + case gc.OMUL<<16 | gc.TINT8, + gc.OMUL<<16 | gc.TINT16, + gc.OMUL<<16 | gc.TINT32, + gc.OMUL<<16 | gc.TINT64: + a = ppc64.AMULLD + + case gc.OMUL<<16 | gc.TUINT8, + gc.OMUL<<16 | gc.TUINT16, + gc.OMUL<<16 | gc.TUINT32, + gc.OMUL<<16 | gc.TPTR32, + // don't use word multiply, the high 32-bit are undefined. + gc.OMUL<<16 | gc.TUINT64, + gc.OMUL<<16 | gc.TPTR64: + // for 64-bit multiplies, signedness doesn't matter. + a = ppc64.AMULLD + + case gc.OMUL<<16 | gc.TFLOAT32: + a = ppc64.AFMULS + + case gc.OMUL<<16 | gc.TFLOAT64: + a = ppc64.AFMUL + + case gc.ODIV<<16 | gc.TINT8, + gc.ODIV<<16 | gc.TINT16, + gc.ODIV<<16 | gc.TINT32, + gc.ODIV<<16 | gc.TINT64: + a = ppc64.ADIVD + + case gc.ODIV<<16 | gc.TUINT8, + gc.ODIV<<16 | gc.TUINT16, + gc.ODIV<<16 | gc.TUINT32, + gc.ODIV<<16 | gc.TPTR32, + gc.ODIV<<16 | gc.TUINT64, + gc.ODIV<<16 | gc.TPTR64: + a = ppc64.ADIVDU + + case gc.ODIV<<16 | gc.TFLOAT32: + a = ppc64.AFDIVS + + case gc.ODIV<<16 | gc.TFLOAT64: + a = ppc64.AFDIV + } + + return a +} + +const ( + ODynam = 1 << 0 + OAddable = 1 << 1 +) + +func xgen(n *gc.Node, a *gc.Node, o int) bool { + // TODO(minux) + + return -1 != 0 /*TypeKind(100016)*/ +} + +func sudoclean() { + return +} + +/* + * generate code to compute address of n, + * a reference to a (perhaps nested) field inside + * an array or struct. + * return 0 on failure, 1 on success. + * on success, leaves usable address in a. + * + * caller is responsible for calling sudoclean + * after successful sudoaddable, + * to release the register used for a. + */ +func sudoaddable(as int, n *gc.Node, a *obj.Addr) bool { + // TODO(minux) + + *a = obj.Addr{} + return false +} diff --git a/src/cmd/compile/internal/ppc64/opt.go b/src/cmd/compile/internal/ppc64/opt.go new file mode 100644 index 0000000000..1704f63c48 --- /dev/null +++ b/src/cmd/compile/internal/ppc64/opt.go @@ -0,0 +1,12 @@ +// Copyright 2014 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 ppc64 + +// Many Power ISA arithmetic and logical instructions come in four +// standard variants. These bits let us map between variants. +const ( + V_CC = 1 << 0 // xCC (affect CR field 0 flags) + V_V = 1 << 1 // xV (affect SO and OV flags) +) diff --git a/src/cmd/compile/internal/ppc64/peep.go b/src/cmd/compile/internal/ppc64/peep.go new file mode 100644 index 0000000000..16eeb39097 --- /dev/null +++ b/src/cmd/compile/internal/ppc64/peep.go @@ -0,0 +1,1051 @@ +// Derived from Inferno utils/6c/peep.c +// http://code.google.com/p/inferno-os/source/browse/utils/6c/peep.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ppc64 + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/ppc64" + "fmt" +) + +var gactive uint32 + +func peep(firstp *obj.Prog) { + g := (*gc.Graph)(gc.Flowstart(firstp, nil)) + if g == nil { + return + } + gactive = 0 + + var p *obj.Prog + var r *gc.Flow + var t int +loop1: + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + gc.Dumpit("loop1", g.Start, 0) + } + + t = 0 + for r = g.Start; r != nil; r = r.Link { + p = r.Prog + + // TODO(austin) Handle smaller moves. arm and amd64 + // distinguish between moves that moves that *must* + // sign/zero extend and moves that don't care so they + // can eliminate moves that don't care without + // breaking moves that do care. This might let us + // simplify or remove the next peep loop, too. + if p.As == ppc64.AMOVD || p.As == ppc64.AFMOVD { + if regtyp(&p.To) { + // Try to eliminate reg->reg moves + if regtyp(&p.From) { + if p.From.Type == p.To.Type { + if copyprop(r) { + excise(r) + t++ + } else if subprop(r) && copyprop(r) { + excise(r) + t++ + } + } + } + + // Convert uses to $0 to uses of R0 and + // propagate R0 + if regzer(&p.From) != 0 { + if p.To.Type == obj.TYPE_REG { + p.From.Type = obj.TYPE_REG + p.From.Reg = ppc64.REGZERO + if copyprop(r) { + excise(r) + t++ + } else if subprop(r) && copyprop(r) { + excise(r) + t++ + } + } + } + } + } + } + + if t != 0 { + goto loop1 + } + + /* + * look for MOVB x,R; MOVB R,R (for small MOVs not handled above) + */ + var p1 *obj.Prog + var r1 *gc.Flow + for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { + p = r.Prog + switch p.As { + default: + continue + + case ppc64.AMOVH, + ppc64.AMOVHZ, + ppc64.AMOVB, + ppc64.AMOVBZ, + ppc64.AMOVW, + ppc64.AMOVWZ: + if p.To.Type != obj.TYPE_REG { + continue + } + } + + r1 = r.Link + if r1 == nil { + continue + } + p1 = r1.Prog + if p1.As != p.As { + continue + } + if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg { + continue + } + if p1.To.Type != obj.TYPE_REG || p1.To.Reg != p.To.Reg { + continue + } + excise(r1) + } + + if gc.Debug['D'] > 1 { + goto ret /* allow following code improvement to be suppressed */ + } + + /* + * look for OP x,y,R; CMP R, $0 -> OPCC x,y,R + * when OP can set condition codes correctly + */ + for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { + p = r.Prog + switch p.As { + case ppc64.ACMP, + ppc64.ACMPW: /* always safe? */ + if regzer(&p.To) == 0 { + continue + } + r1 = r.S1 + if r1 == nil { + continue + } + switch r1.Prog.As { + default: + continue + + /* the conditions can be complex and these are currently little used */ + case ppc64.ABCL, + ppc64.ABC: + continue + + case ppc64.ABEQ, + ppc64.ABGE, + ppc64.ABGT, + ppc64.ABLE, + ppc64.ABLT, + ppc64.ABNE, + ppc64.ABVC, + ppc64.ABVS: + break + } + + r1 = r + for { + r1 = gc.Uniqp(r1) + if r1 == nil || r1.Prog.As != obj.ANOP { + break + } + } + + if r1 == nil { + continue + } + p1 = r1.Prog + if p1.To.Type != obj.TYPE_REG || p1.To.Reg != p.From.Reg { + continue + } + switch p1.As { + /* irregular instructions */ + case ppc64.ASUB, + ppc64.AADD, + ppc64.AXOR, + ppc64.AOR: + if p1.From.Type == obj.TYPE_CONST || p1.From.Type == obj.TYPE_ADDR { + continue + } + } + + switch p1.As { + default: + continue + + case ppc64.AMOVW, + ppc64.AMOVD: + if p1.From.Type != obj.TYPE_REG { + continue + } + continue + + case ppc64.AANDCC, + ppc64.AANDNCC, + ppc64.AORCC, + ppc64.AORNCC, + ppc64.AXORCC, + ppc64.ASUBCC, + ppc64.ASUBECC, + ppc64.ASUBMECC, + ppc64.ASUBZECC, + ppc64.AADDCC, + ppc64.AADDCCC, + ppc64.AADDECC, + ppc64.AADDMECC, + ppc64.AADDZECC, + ppc64.ARLWMICC, + ppc64.ARLWNMCC, + /* don't deal with floating point instructions for now */ + /* + case AFABS: + case AFADD: + case AFADDS: + case AFCTIW: + case AFCTIWZ: + case AFDIV: + case AFDIVS: + case AFMADD: + case AFMADDS: + case AFMOVD: + case AFMSUB: + case AFMSUBS: + case AFMUL: + case AFMULS: + case AFNABS: + case AFNEG: + case AFNMADD: + case AFNMADDS: + case AFNMSUB: + case AFNMSUBS: + case AFRSP: + case AFSUB: + case AFSUBS: + case ACNTLZW: + case AMTFSB0: + case AMTFSB1: + */ + ppc64.AADD, + ppc64.AADDV, + ppc64.AADDC, + ppc64.AADDCV, + ppc64.AADDME, + ppc64.AADDMEV, + ppc64.AADDE, + ppc64.AADDEV, + ppc64.AADDZE, + ppc64.AADDZEV, + ppc64.AAND, + ppc64.AANDN, + ppc64.ADIVW, + ppc64.ADIVWV, + ppc64.ADIVWU, + ppc64.ADIVWUV, + ppc64.ADIVD, + ppc64.ADIVDV, + ppc64.ADIVDU, + ppc64.ADIVDUV, + ppc64.AEQV, + ppc64.AEXTSB, + ppc64.AEXTSH, + ppc64.AEXTSW, + ppc64.AMULHW, + ppc64.AMULHWU, + ppc64.AMULLW, + ppc64.AMULLWV, + ppc64.AMULHD, + ppc64.AMULHDU, + ppc64.AMULLD, + ppc64.AMULLDV, + ppc64.ANAND, + ppc64.ANEG, + ppc64.ANEGV, + ppc64.ANOR, + ppc64.AOR, + ppc64.AORN, + ppc64.AREM, + ppc64.AREMV, + ppc64.AREMU, + ppc64.AREMUV, + ppc64.AREMD, + ppc64.AREMDV, + ppc64.AREMDU, + ppc64.AREMDUV, + ppc64.ARLWMI, + ppc64.ARLWNM, + ppc64.ASLW, + ppc64.ASRAW, + ppc64.ASRW, + ppc64.ASLD, + ppc64.ASRAD, + ppc64.ASRD, + ppc64.ASUB, + ppc64.ASUBV, + ppc64.ASUBC, + ppc64.ASUBCV, + ppc64.ASUBME, + ppc64.ASUBMEV, + ppc64.ASUBE, + ppc64.ASUBEV, + ppc64.ASUBZE, + ppc64.ASUBZEV, + ppc64.AXOR: + t = variant2as(int(p1.As), as2variant(int(p1.As))|V_CC) + } + + if gc.Debug['D'] != 0 { + fmt.Printf("cmp %v; %v -> ", p1, p) + } + p1.As = int16(t) + if gc.Debug['D'] != 0 { + fmt.Printf("%v\n", p1) + } + excise(r) + continue + } + } + +ret: + gc.Flowend(g) +} + +func excise(r *gc.Flow) { + p := (*obj.Prog)(r.Prog) + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + fmt.Printf("%v ===delete===\n", p) + } + obj.Nopout(p) + gc.Ostats.Ndelmov++ +} + +/* + * regzer returns 1 if a's value is 0 (a is R0 or $0) + */ +func regzer(a *obj.Addr) int { + if a.Type == obj.TYPE_CONST || a.Type == obj.TYPE_ADDR { + if a.Sym == nil && a.Reg == 0 { + if a.Offset == 0 { + return 1 + } + } + } + if a.Type == obj.TYPE_REG { + if a.Reg == ppc64.REGZERO { + return 1 + } + } + return 0 +} + +func regtyp(a *obj.Addr) bool { + // TODO(rsc): Floating point register exclusions? + return a.Type == obj.TYPE_REG && ppc64.REG_R0 <= a.Reg && a.Reg <= ppc64.REG_F31 && a.Reg != ppc64.REGZERO +} + +/* + * the idea is to substitute + * one register for another + * from one MOV to another + * MOV a, R1 + * ADD b, R1 / no use of R2 + * MOV R1, R2 + * would be converted to + * MOV a, R2 + * ADD b, R2 + * MOV R2, R1 + * hopefully, then the former or latter MOV + * will be eliminated by copy propagation. + * + * r0 (the argument, not the register) is the MOV at the end of the + * above sequences. This returns 1 if it modified any instructions. + */ +func subprop(r0 *gc.Flow) bool { + p := (*obj.Prog)(r0.Prog) + v1 := (*obj.Addr)(&p.From) + if !regtyp(v1) { + return false + } + v2 := (*obj.Addr)(&p.To) + if !regtyp(v2) { + return false + } + for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { + if gc.Uniqs(r) == nil { + break + } + p = r.Prog + if p.As == obj.AVARDEF || p.As == obj.AVARKILL { + continue + } + if p.Info.Flags&gc.Call != 0 { + return false + } + + if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightWrite { + if p.To.Type == v1.Type { + if p.To.Reg == v1.Reg { + copysub(&p.To, v1, v2, 1) + if gc.Debug['P'] != 0 { + fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog) + if p.From.Type == v2.Type { + fmt.Printf(" excise") + } + fmt.Printf("\n") + } + + for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { + p = r.Prog + copysub(&p.From, v1, v2, 1) + copysub1(p, v1, v2, 1) + copysub(&p.To, v1, v2, 1) + if gc.Debug['P'] != 0 { + fmt.Printf("%v\n", r.Prog) + } + } + + t := int(int(v1.Reg)) + v1.Reg = v2.Reg + v2.Reg = int16(t) + if gc.Debug['P'] != 0 { + fmt.Printf("%v last\n", r.Prog) + } + return true + } + } + } + + if copyau(&p.From, v2) || copyau1(p, v2) || copyau(&p.To, v2) { + break + } + if copysub(&p.From, v1, v2, 0) != 0 || copysub1(p, v1, v2, 0) != 0 || copysub(&p.To, v1, v2, 0) != 0 { + break + } + } + + return false +} + +/* + * The idea is to remove redundant copies. + * v1->v2 F=0 + * (use v2 s/v2/v1/)* + * set v1 F=1 + * use v2 return fail (v1->v2 move must remain) + * ----------------- + * v1->v2 F=0 + * (use v2 s/v2/v1/)* + * set v1 F=1 + * set v2 return success (caller can remove v1->v2 move) + */ +func copyprop(r0 *gc.Flow) bool { + p := (*obj.Prog)(r0.Prog) + v1 := (*obj.Addr)(&p.From) + v2 := (*obj.Addr)(&p.To) + if copyas(v1, v2) { + if gc.Debug['P'] != 0 { + fmt.Printf("eliminating self-move: %v\n", r0.Prog) + } + return true + } + + gactive++ + if gc.Debug['P'] != 0 { + fmt.Printf("trying to eliminate %v->%v move from:\n%v\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r0.Prog) + } + return copy1(v1, v2, r0.S1, 0) +} + +// copy1 replaces uses of v2 with v1 starting at r and returns 1 if +// all uses were rewritten. +func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f int) bool { + if uint32(r.Active) == gactive { + if gc.Debug['P'] != 0 { + fmt.Printf("act set; return 1\n") + } + return true + } + + r.Active = int32(gactive) + if gc.Debug['P'] != 0 { + fmt.Printf("copy1 replace %v with %v f=%d\n", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), f) + } + var t int + var p *obj.Prog + for ; r != nil; r = r.S1 { + p = r.Prog + if gc.Debug['P'] != 0 { + fmt.Printf("%v", p) + } + if f == 0 && gc.Uniqp(r) == nil { + // Multiple predecessors; conservatively + // assume v1 was set on other path + f = 1 + + if gc.Debug['P'] != 0 { + fmt.Printf("; merge; f=%d", f) + } + } + + t = copyu(p, v2, nil) + switch t { + case 2: /* rar, can't split */ + if gc.Debug['P'] != 0 { + fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2)) + } + return false + + case 3: /* set */ + if gc.Debug['P'] != 0 { + fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2)) + } + return true + + case 1, /* used, substitute */ + 4: /* use and set */ + if f != 0 { + if gc.Debug['P'] == 0 { + return false + } + if t == 4 { + fmt.Printf("; %v used+set and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f) + } else { + fmt.Printf("; %v used and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f) + } + return false + } + + if copyu(p, v2, v1) != 0 { + if gc.Debug['P'] != 0 { + fmt.Printf("; sub fail; return 0\n") + } + return false + } + + if gc.Debug['P'] != 0 { + fmt.Printf("; sub %v->%v\n => %v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), p) + } + if t == 4 { + if gc.Debug['P'] != 0 { + fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2)) + } + return true + } + } + + if f == 0 { + t = copyu(p, v1, nil) + if f == 0 && (t == 2 || t == 3 || t == 4) { + f = 1 + if gc.Debug['P'] != 0 { + fmt.Printf("; %v set and !f; f=%d", gc.Ctxt.Dconv(v1), f) + } + } + } + + if gc.Debug['P'] != 0 { + fmt.Printf("\n") + } + if r.S2 != nil { + if !copy1(v1, v2, r.S2, f) { + return false + } + } + } + + return true +} + +// If s==nil, copyu returns the set/use of v in p; otherwise, it +// modifies p to replace reads of v with reads of s and returns 0 for +// success or non-zero for failure. +// +// If s==nil, copy returns one of the following values: +// 1 if v only used +// 2 if v is set and used in one address (read-alter-rewrite; +// can't substitute) +// 3 if v is only set +// 4 if v is set in one address and used in another (so addresses +// can be rewritten independently) +// 0 otherwise (not touched) +func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int { + if p.From3.Type != obj.TYPE_NONE { + // 9g never generates a from3 + fmt.Printf("copyu: from3 (%v) not implemented\n", gc.Ctxt.Dconv(&p.From3)) + } + + switch p.As { + default: + fmt.Printf("copyu: can't find %v\n", obj.Aconv(int(p.As))) + return 2 + + case obj.ANOP, /* read p->from, write p->to */ + ppc64.AMOVH, + ppc64.AMOVHZ, + ppc64.AMOVB, + ppc64.AMOVBZ, + ppc64.AMOVW, + ppc64.AMOVWZ, + ppc64.AMOVD, + ppc64.ANEG, + ppc64.ANEGCC, + ppc64.AADDME, + ppc64.AADDMECC, + ppc64.AADDZE, + ppc64.AADDZECC, + ppc64.ASUBME, + ppc64.ASUBMECC, + ppc64.ASUBZE, + ppc64.ASUBZECC, + ppc64.AFCTIW, + ppc64.AFCTIWZ, + ppc64.AFCTID, + ppc64.AFCTIDZ, + ppc64.AFCFID, + ppc64.AFCFIDCC, + ppc64.AFMOVS, + ppc64.AFMOVD, + ppc64.AFRSP, + ppc64.AFNEG, + ppc64.AFNEGCC: + if s != nil { + if copysub(&p.From, v, s, 1) != 0 { + return 1 + } + + // Update only indirect uses of v in p->to + if !copyas(&p.To, v) { + if copysub(&p.To, v, s, 1) != 0 { + return 1 + } + } + return 0 + } + + if copyas(&p.To, v) { + // Fix up implicit from + if p.From.Type == obj.TYPE_NONE { + p.From = p.To + } + if copyau(&p.From, v) { + return 4 + } + return 3 + } + + if copyau(&p.From, v) { + return 1 + } + if copyau(&p.To, v) { + // p->to only indirectly uses v + return 1 + } + + return 0 + + case ppc64.AMOVBU, /* rar p->from, write p->to or read p->from, rar p->to */ + ppc64.AMOVBZU, + ppc64.AMOVHU, + ppc64.AMOVHZU, + ppc64.AMOVWZU, + ppc64.AMOVDU: + if p.From.Type == obj.TYPE_MEM { + if copyas(&p.From, v) { + // No s!=nil check; need to fail + // anyway in that case + return 2 + } + + if s != nil { + if copysub(&p.To, v, s, 1) != 0 { + return 1 + } + return 0 + } + + if copyas(&p.To, v) { + return 3 + } + } else if p.To.Type == obj.TYPE_MEM { + if copyas(&p.To, v) { + return 2 + } + if s != nil { + if copysub(&p.From, v, s, 1) != 0 { + return 1 + } + return 0 + } + + if copyau(&p.From, v) { + return 1 + } + } else { + fmt.Printf("copyu: bad %v\n", p) + } + + return 0 + + case ppc64.ARLWMI, /* read p->from, read p->reg, rar p->to */ + ppc64.ARLWMICC: + if copyas(&p.To, v) { + return 2 + } + fallthrough + + /* fall through */ + case ppc64.AADD, + /* read p->from, read p->reg, write p->to */ + ppc64.AADDC, + ppc64.AADDE, + ppc64.ASUB, + ppc64.ASLW, + ppc64.ASRW, + ppc64.ASRAW, + ppc64.ASLD, + ppc64.ASRD, + ppc64.ASRAD, + ppc64.AOR, + ppc64.AORCC, + ppc64.AORN, + ppc64.AORNCC, + ppc64.AAND, + ppc64.AANDCC, + ppc64.AANDN, + ppc64.AANDNCC, + ppc64.ANAND, + ppc64.ANANDCC, + ppc64.ANOR, + ppc64.ANORCC, + ppc64.AXOR, + ppc64.AMULHW, + ppc64.AMULHWU, + ppc64.AMULLW, + ppc64.AMULLD, + ppc64.ADIVW, + ppc64.ADIVD, + ppc64.ADIVWU, + ppc64.ADIVDU, + ppc64.AREM, + ppc64.AREMU, + ppc64.AREMD, + ppc64.AREMDU, + ppc64.ARLWNM, + ppc64.ARLWNMCC, + ppc64.AFADDS, + ppc64.AFADD, + ppc64.AFSUBS, + ppc64.AFSUB, + ppc64.AFMULS, + ppc64.AFMUL, + ppc64.AFDIVS, + ppc64.AFDIV: + if s != nil { + if copysub(&p.From, v, s, 1) != 0 { + return 1 + } + if copysub1(p, v, s, 1) != 0 { + return 1 + } + + // Update only indirect uses of v in p->to + if !copyas(&p.To, v) { + if copysub(&p.To, v, s, 1) != 0 { + return 1 + } + } + return 0 + } + + if copyas(&p.To, v) { + if p.Reg == 0 { + // Fix up implicit reg (e.g., ADD + // R3,R4 -> ADD R3,R4,R4) so we can + // update reg and to separately. + p.Reg = p.To.Reg + } + + if copyau(&p.From, v) { + return 4 + } + if copyau1(p, v) { + return 4 + } + return 3 + } + + if copyau(&p.From, v) { + return 1 + } + if copyau1(p, v) { + return 1 + } + if copyau(&p.To, v) { + return 1 + } + return 0 + + case ppc64.ABEQ, + ppc64.ABGT, + ppc64.ABGE, + ppc64.ABLT, + ppc64.ABLE, + ppc64.ABNE, + ppc64.ABVC, + ppc64.ABVS: + return 0 + + case obj.ACHECKNIL, /* read p->from */ + ppc64.ACMP, /* read p->from, read p->to */ + ppc64.ACMPU, + ppc64.ACMPW, + ppc64.ACMPWU, + ppc64.AFCMPO, + ppc64.AFCMPU: + if s != nil { + if copysub(&p.From, v, s, 1) != 0 { + return 1 + } + return copysub(&p.To, v, s, 1) + } + + if copyau(&p.From, v) { + return 1 + } + if copyau(&p.To, v) { + return 1 + } + return 0 + + // 9g never generates a branch to a GPR (this isn't + // even a normal instruction; liblink turns it in to a + // mov and a branch). + case ppc64.ABR: /* read p->to */ + if s != nil { + if copysub(&p.To, v, s, 1) != 0 { + return 1 + } + return 0 + } + + if copyau(&p.To, v) { + return 1 + } + return 0 + + case ppc64.ARETURN: /* funny */ + if s != nil { + return 0 + } + + // All registers die at this point, so claim + // everything is set (and not used). + return 3 + + case ppc64.ABL: /* funny */ + if v.Type == obj.TYPE_REG { + // TODO(rsc): REG_R0 and REG_F0 used to be + // (when register numbers started at 0) exregoffset and exfregoffset, + // which are unset entirely. + // It's strange that this handles R0 and F0 differently from the other + // registers. Possible failure to optimize? + if ppc64.REG_R0 < v.Reg && v.Reg <= ppc64.REGEXT { + return 2 + } + if v.Reg == ppc64.REGARG { + return 2 + } + if ppc64.REG_F0 < v.Reg && v.Reg <= ppc64.FREGEXT { + return 2 + } + } + + if p.From.Type == obj.TYPE_REG && v.Type == obj.TYPE_REG && p.From.Reg == v.Reg { + return 2 + } + + if s != nil { + if copysub(&p.To, v, s, 1) != 0 { + return 1 + } + return 0 + } + + if copyau(&p.To, v) { + return 4 + } + return 3 + + // R0 is zero, used by DUFFZERO, cannot be substituted. + // R3 is ptr to memory, used and set, cannot be substituted. + case obj.ADUFFZERO: + if v.Type == obj.TYPE_REG { + if v.Reg == 0 { + return 1 + } + if v.Reg == 3 { + return 2 + } + } + + return 0 + + // R3, R4 are ptr to src, dst, used and set, cannot be substituted. + // R5 is scratch, set by DUFFCOPY, cannot be substituted. + case obj.ADUFFCOPY: + if v.Type == obj.TYPE_REG { + if v.Reg == 3 || v.Reg == 4 { + return 2 + } + if v.Reg == 5 { + return 3 + } + } + + return 0 + + case obj.ATEXT: /* funny */ + if v.Type == obj.TYPE_REG { + if v.Reg == ppc64.REGARG { + return 3 + } + } + return 0 + + case obj.APCDATA, + obj.AFUNCDATA, + obj.AVARDEF, + obj.AVARKILL: + return 0 + } +} + +// copyas returns 1 if a and v address the same register. +// +// If a is the from operand, this means this operation reads the +// register in v. If a is the to operand, this means this operation +// writes the register in v. +func copyas(a *obj.Addr, v *obj.Addr) bool { + if regtyp(v) { + if a.Type == v.Type { + if a.Reg == v.Reg { + return true + } + } + } + return false +} + +// copyau returns 1 if a either directly or indirectly addresses the +// same register as v. +// +// If a is the from operand, this means this operation reads the +// register in v. If a is the to operand, this means the operation +// either reads or writes the register in v (if !copyas(a, v), then +// the operation reads the register in v). +func copyau(a *obj.Addr, v *obj.Addr) bool { + if copyas(a, v) { + return true + } + if v.Type == obj.TYPE_REG { + if a.Type == obj.TYPE_MEM || (a.Type == obj.TYPE_ADDR && a.Reg != 0) { + if v.Reg == a.Reg { + return true + } + } + } + return false +} + +// copyau1 returns 1 if p->reg references the same register as v and v +// is a direct reference. +func copyau1(p *obj.Prog, v *obj.Addr) bool { + if regtyp(v) && v.Reg != 0 { + if p.Reg == v.Reg { + return true + } + } + return false +} + +// copysub replaces v with s in a if f!=0 or indicates it if could if f==0. +// Returns 1 on failure to substitute (it always succeeds on ppc64). +func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f int) int { + if f != 0 { + if copyau(a, v) { + a.Reg = s.Reg + } + } + return 0 +} + +// copysub1 replaces v with s in p1->reg if f!=0 or indicates if it could if f==0. +// Returns 1 on failure to substitute (it always succeeds on ppc64). +func copysub1(p1 *obj.Prog, v *obj.Addr, s *obj.Addr, f int) int { + if f != 0 { + if copyau1(p1, v) { + p1.Reg = s.Reg + } + } + return 0 +} + +func sameaddr(a *obj.Addr, v *obj.Addr) bool { + if a.Type != v.Type { + return false + } + if regtyp(v) && a.Reg == v.Reg { + return true + } + if v.Type == obj.NAME_AUTO || v.Type == obj.NAME_PARAM { + if v.Offset == a.Offset { + return true + } + } + return false +} + +func smallindir(a *obj.Addr, reg *obj.Addr) bool { + return reg.Type == obj.TYPE_REG && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && 0 <= a.Offset && a.Offset < 4096 +} + +func stackaddr(a *obj.Addr) bool { + return a.Type == obj.TYPE_REG && a.Reg == ppc64.REGSP +} diff --git a/src/cmd/compile/internal/ppc64/prog.go b/src/cmd/compile/internal/ppc64/prog.go new file mode 100644 index 0000000000..c7e182769d --- /dev/null +++ b/src/cmd/compile/internal/ppc64/prog.go @@ -0,0 +1,314 @@ +// Copyright 2014 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 ppc64 + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/ppc64" +) + +const ( + LeftRdwr uint32 = gc.LeftRead | gc.LeftWrite + RightRdwr uint32 = gc.RightRead | gc.RightWrite +) + +// This table gives the basic information about instruction +// generated by the compiler and processed in the optimizer. +// See opt.h for bit definitions. +// +// Instructions not generated need not be listed. +// As an exception to that rule, we typically write down all the +// size variants of an operation even if we just use a subset. +// +// The table is formatted for 8-space tabs. +var progtable = [ppc64.ALAST]obj.ProgInfo{ + obj.ATYPE: {gc.Pseudo | gc.Skip, 0, 0, 0}, + obj.ATEXT: {gc.Pseudo, 0, 0, 0}, + obj.AFUNCDATA: {gc.Pseudo, 0, 0, 0}, + obj.APCDATA: {gc.Pseudo, 0, 0, 0}, + obj.AUNDEF: {gc.Break, 0, 0, 0}, + obj.AUSEFIELD: {gc.OK, 0, 0, 0}, + obj.ACHECKNIL: {gc.LeftRead, 0, 0, 0}, + obj.AVARDEF: {gc.Pseudo | gc.RightWrite, 0, 0, 0}, + obj.AVARKILL: {gc.Pseudo | gc.RightWrite, 0, 0, 0}, + + // NOP is an internal no-op that also stands + // for USED and SET annotations, not the Power opcode. + obj.ANOP: {gc.LeftRead | gc.RightWrite, 0, 0, 0}, + + // Integer + ppc64.AADD: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + ppc64.ASUB: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + ppc64.ANEG: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + ppc64.AAND: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + ppc64.AOR: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + ppc64.AXOR: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + ppc64.AMULLD: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + ppc64.AMULLW: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + ppc64.AMULHD: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + ppc64.AMULHDU: {gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + ppc64.ADIVD: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + ppc64.ADIVDU: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + ppc64.ASLD: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + ppc64.ASRD: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + ppc64.ASRAD: {gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + ppc64.ACMP: {gc.SizeQ | gc.LeftRead | gc.RightRead, 0, 0, 0}, + ppc64.ACMPU: {gc.SizeQ | gc.LeftRead | gc.RightRead, 0, 0, 0}, + ppc64.ATD: {gc.SizeQ | gc.RightRead, 0, 0, 0}, + + // Floating point. + ppc64.AFADD: {gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + ppc64.AFADDS: {gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + ppc64.AFSUB: {gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + ppc64.AFSUBS: {gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + ppc64.AFMUL: {gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + ppc64.AFMULS: {gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + ppc64.AFDIV: {gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + ppc64.AFDIVS: {gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + ppc64.AFCTIDZ: {gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + ppc64.AFCFID: {gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite, 0, 0, 0}, + ppc64.AFCMPU: {gc.SizeD | gc.LeftRead | gc.RightRead, 0, 0, 0}, + ppc64.AFRSP: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + + // Moves + ppc64.AMOVB: {gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv, 0, 0, 0}, + ppc64.AMOVBU: {gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv | gc.PostInc, 0, 0, 0}, + ppc64.AMOVBZ: {gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv, 0, 0, 0}, + ppc64.AMOVH: {gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv, 0, 0, 0}, + ppc64.AMOVHU: {gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv | gc.PostInc, 0, 0, 0}, + ppc64.AMOVHZ: {gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv, 0, 0, 0}, + ppc64.AMOVW: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv, 0, 0, 0}, + + // there is no AMOVWU. + ppc64.AMOVWZU: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv | gc.PostInc, 0, 0, 0}, + ppc64.AMOVWZ: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv, 0, 0, 0}, + ppc64.AMOVD: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, + ppc64.AMOVDU: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move | gc.PostInc, 0, 0, 0}, + ppc64.AFMOVS: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv, 0, 0, 0}, + ppc64.AFMOVD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, + + // Jumps + ppc64.ABR: {gc.Jump | gc.Break, 0, 0, 0}, + ppc64.ABL: {gc.Call, 0, 0, 0}, + ppc64.ABEQ: {gc.Cjmp, 0, 0, 0}, + ppc64.ABNE: {gc.Cjmp, 0, 0, 0}, + ppc64.ABGE: {gc.Cjmp, 0, 0, 0}, + ppc64.ABLT: {gc.Cjmp, 0, 0, 0}, + ppc64.ABGT: {gc.Cjmp, 0, 0, 0}, + ppc64.ABLE: {gc.Cjmp, 0, 0, 0}, + ppc64.ARETURN: {gc.Break, 0, 0, 0}, + obj.ADUFFZERO: {gc.Call, 0, 0, 0}, + obj.ADUFFCOPY: {gc.Call, 0, 0, 0}, +} + +var initproginfo_initialized int + +func initproginfo() { + var addvariant = []int{V_CC, V_V, V_CC | V_V} + + if initproginfo_initialized != 0 { + return + } + initproginfo_initialized = 1 + + // Perform one-time expansion of instructions in progtable to + // their CC, V, and VCC variants + var as2 int + var i int + var variant int + for as := int(0); as < len(progtable); as++ { + if progtable[as].Flags == 0 { + continue + } + variant = as2variant(as) + for i = 0; i < len(addvariant); i++ { + as2 = variant2as(as, variant|addvariant[i]) + if as2 != 0 && progtable[as2].Flags == 0 { + progtable[as2] = progtable[as] + } + } + } +} + +func proginfo(p *obj.Prog) { + initproginfo() + + info := &p.Info + *info = progtable[p.As] + if info.Flags == 0 { + gc.Fatal("proginfo: unknown instruction %v", p) + } + + if (info.Flags&gc.RegRead != 0) && p.Reg == 0 { + info.Flags &^= gc.RegRead + info.Flags |= gc.RightRead /*CanRegRead |*/ + } + + if (p.From.Type == obj.TYPE_MEM || p.From.Type == obj.TYPE_ADDR) && p.From.Reg != 0 { + info.Regindex |= RtoB(int(p.From.Reg)) + if info.Flags&gc.PostInc != 0 { + info.Regset |= RtoB(int(p.From.Reg)) + } + } + + if (p.To.Type == obj.TYPE_MEM || p.To.Type == obj.TYPE_ADDR) && p.To.Reg != 0 { + info.Regindex |= RtoB(int(p.To.Reg)) + if info.Flags&gc.PostInc != 0 { + info.Regset |= RtoB(int(p.To.Reg)) + } + } + + if p.From.Type == obj.TYPE_ADDR && p.From.Sym != nil && (info.Flags&gc.LeftRead != 0) { + info.Flags &^= gc.LeftRead + info.Flags |= gc.LeftAddr + } + + if p.As == obj.ADUFFZERO { + info.Reguse |= 1<<0 | RtoB(ppc64.REG_R3) + info.Regset |= RtoB(ppc64.REG_R3) + } + + if p.As == obj.ADUFFCOPY { + // TODO(austin) Revisit when duffcopy is implemented + info.Reguse |= RtoB(ppc64.REG_R3) | RtoB(ppc64.REG_R4) | RtoB(ppc64.REG_R5) + + info.Regset |= RtoB(ppc64.REG_R3) | RtoB(ppc64.REG_R4) + } +} + +// Instruction variants table. Initially this contains entries only +// for the "base" form of each instruction. On the first call to +// as2variant or variant2as, we'll add the variants to the table. +var varianttable = [ppc64.ALAST][4]int{ + ppc64.AADD: [4]int{ppc64.AADD, ppc64.AADDCC, ppc64.AADDV, ppc64.AADDVCC}, + ppc64.AADDC: [4]int{ppc64.AADDC, ppc64.AADDCCC, ppc64.AADDCV, ppc64.AADDCVCC}, + ppc64.AADDE: [4]int{ppc64.AADDE, ppc64.AADDECC, ppc64.AADDEV, ppc64.AADDEVCC}, + ppc64.AADDME: [4]int{ppc64.AADDME, ppc64.AADDMECC, ppc64.AADDMEV, ppc64.AADDMEVCC}, + ppc64.AADDZE: [4]int{ppc64.AADDZE, ppc64.AADDZECC, ppc64.AADDZEV, ppc64.AADDZEVCC}, + ppc64.AAND: [4]int{ppc64.AAND, ppc64.AANDCC, 0, 0}, + ppc64.AANDN: [4]int{ppc64.AANDN, ppc64.AANDNCC, 0, 0}, + ppc64.ACNTLZD: [4]int{ppc64.ACNTLZD, ppc64.ACNTLZDCC, 0, 0}, + ppc64.ACNTLZW: [4]int{ppc64.ACNTLZW, ppc64.ACNTLZWCC, 0, 0}, + ppc64.ADIVD: [4]int{ppc64.ADIVD, ppc64.ADIVDCC, ppc64.ADIVDV, ppc64.ADIVDVCC}, + ppc64.ADIVDU: [4]int{ppc64.ADIVDU, ppc64.ADIVDUCC, ppc64.ADIVDUV, ppc64.ADIVDUVCC}, + ppc64.ADIVW: [4]int{ppc64.ADIVW, ppc64.ADIVWCC, ppc64.ADIVWV, ppc64.ADIVWVCC}, + ppc64.ADIVWU: [4]int{ppc64.ADIVWU, ppc64.ADIVWUCC, ppc64.ADIVWUV, ppc64.ADIVWUVCC}, + ppc64.AEQV: [4]int{ppc64.AEQV, ppc64.AEQVCC, 0, 0}, + ppc64.AEXTSB: [4]int{ppc64.AEXTSB, ppc64.AEXTSBCC, 0, 0}, + ppc64.AEXTSH: [4]int{ppc64.AEXTSH, ppc64.AEXTSHCC, 0, 0}, + ppc64.AEXTSW: [4]int{ppc64.AEXTSW, ppc64.AEXTSWCC, 0, 0}, + ppc64.AFABS: [4]int{ppc64.AFABS, ppc64.AFABSCC, 0, 0}, + ppc64.AFADD: [4]int{ppc64.AFADD, ppc64.AFADDCC, 0, 0}, + ppc64.AFADDS: [4]int{ppc64.AFADDS, ppc64.AFADDSCC, 0, 0}, + ppc64.AFCFID: [4]int{ppc64.AFCFID, ppc64.AFCFIDCC, 0, 0}, + ppc64.AFCTID: [4]int{ppc64.AFCTID, ppc64.AFCTIDCC, 0, 0}, + ppc64.AFCTIDZ: [4]int{ppc64.AFCTIDZ, ppc64.AFCTIDZCC, 0, 0}, + ppc64.AFCTIW: [4]int{ppc64.AFCTIW, ppc64.AFCTIWCC, 0, 0}, + ppc64.AFCTIWZ: [4]int{ppc64.AFCTIWZ, ppc64.AFCTIWZCC, 0, 0}, + ppc64.AFDIV: [4]int{ppc64.AFDIV, ppc64.AFDIVCC, 0, 0}, + ppc64.AFDIVS: [4]int{ppc64.AFDIVS, ppc64.AFDIVSCC, 0, 0}, + ppc64.AFMADD: [4]int{ppc64.AFMADD, ppc64.AFMADDCC, 0, 0}, + ppc64.AFMADDS: [4]int{ppc64.AFMADDS, ppc64.AFMADDSCC, 0, 0}, + ppc64.AFMOVD: [4]int{ppc64.AFMOVD, ppc64.AFMOVDCC, 0, 0}, + ppc64.AFMSUB: [4]int{ppc64.AFMSUB, ppc64.AFMSUBCC, 0, 0}, + ppc64.AFMSUBS: [4]int{ppc64.AFMSUBS, ppc64.AFMSUBSCC, 0, 0}, + ppc64.AFMUL: [4]int{ppc64.AFMUL, ppc64.AFMULCC, 0, 0}, + ppc64.AFMULS: [4]int{ppc64.AFMULS, ppc64.AFMULSCC, 0, 0}, + ppc64.AFNABS: [4]int{ppc64.AFNABS, ppc64.AFNABSCC, 0, 0}, + ppc64.AFNEG: [4]int{ppc64.AFNEG, ppc64.AFNEGCC, 0, 0}, + ppc64.AFNMADD: [4]int{ppc64.AFNMADD, ppc64.AFNMADDCC, 0, 0}, + ppc64.AFNMADDS: [4]int{ppc64.AFNMADDS, ppc64.AFNMADDSCC, 0, 0}, + ppc64.AFNMSUB: [4]int{ppc64.AFNMSUB, ppc64.AFNMSUBCC, 0, 0}, + ppc64.AFNMSUBS: [4]int{ppc64.AFNMSUBS, ppc64.AFNMSUBSCC, 0, 0}, + ppc64.AFRES: [4]int{ppc64.AFRES, ppc64.AFRESCC, 0, 0}, + ppc64.AFRSP: [4]int{ppc64.AFRSP, ppc64.AFRSPCC, 0, 0}, + ppc64.AFRSQRTE: [4]int{ppc64.AFRSQRTE, ppc64.AFRSQRTECC, 0, 0}, + ppc64.AFSEL: [4]int{ppc64.AFSEL, ppc64.AFSELCC, 0, 0}, + ppc64.AFSQRT: [4]int{ppc64.AFSQRT, ppc64.AFSQRTCC, 0, 0}, + ppc64.AFSQRTS: [4]int{ppc64.AFSQRTS, ppc64.AFSQRTSCC, 0, 0}, + ppc64.AFSUB: [4]int{ppc64.AFSUB, ppc64.AFSUBCC, 0, 0}, + ppc64.AFSUBS: [4]int{ppc64.AFSUBS, ppc64.AFSUBSCC, 0, 0}, + ppc64.AMTFSB0: [4]int{ppc64.AMTFSB0, ppc64.AMTFSB0CC, 0, 0}, + ppc64.AMTFSB1: [4]int{ppc64.AMTFSB1, ppc64.AMTFSB1CC, 0, 0}, + ppc64.AMULHD: [4]int{ppc64.AMULHD, ppc64.AMULHDCC, 0, 0}, + ppc64.AMULHDU: [4]int{ppc64.AMULHDU, ppc64.AMULHDUCC, 0, 0}, + ppc64.AMULHW: [4]int{ppc64.AMULHW, ppc64.AMULHWCC, 0, 0}, + ppc64.AMULHWU: [4]int{ppc64.AMULHWU, ppc64.AMULHWUCC, 0, 0}, + ppc64.AMULLD: [4]int{ppc64.AMULLD, ppc64.AMULLDCC, ppc64.AMULLDV, ppc64.AMULLDVCC}, + ppc64.AMULLW: [4]int{ppc64.AMULLW, ppc64.AMULLWCC, ppc64.AMULLWV, ppc64.AMULLWVCC}, + ppc64.ANAND: [4]int{ppc64.ANAND, ppc64.ANANDCC, 0, 0}, + ppc64.ANEG: [4]int{ppc64.ANEG, ppc64.ANEGCC, ppc64.ANEGV, ppc64.ANEGVCC}, + ppc64.ANOR: [4]int{ppc64.ANOR, ppc64.ANORCC, 0, 0}, + ppc64.AOR: [4]int{ppc64.AOR, ppc64.AORCC, 0, 0}, + ppc64.AORN: [4]int{ppc64.AORN, ppc64.AORNCC, 0, 0}, + ppc64.AREM: [4]int{ppc64.AREM, ppc64.AREMCC, ppc64.AREMV, ppc64.AREMVCC}, + ppc64.AREMD: [4]int{ppc64.AREMD, ppc64.AREMDCC, ppc64.AREMDV, ppc64.AREMDVCC}, + ppc64.AREMDU: [4]int{ppc64.AREMDU, ppc64.AREMDUCC, ppc64.AREMDUV, ppc64.AREMDUVCC}, + ppc64.AREMU: [4]int{ppc64.AREMU, ppc64.AREMUCC, ppc64.AREMUV, ppc64.AREMUVCC}, + ppc64.ARLDC: [4]int{ppc64.ARLDC, ppc64.ARLDCCC, 0, 0}, + ppc64.ARLDCL: [4]int{ppc64.ARLDCL, ppc64.ARLDCLCC, 0, 0}, + ppc64.ARLDCR: [4]int{ppc64.ARLDCR, ppc64.ARLDCRCC, 0, 0}, + ppc64.ARLDMI: [4]int{ppc64.ARLDMI, ppc64.ARLDMICC, 0, 0}, + ppc64.ARLWMI: [4]int{ppc64.ARLWMI, ppc64.ARLWMICC, 0, 0}, + ppc64.ARLWNM: [4]int{ppc64.ARLWNM, ppc64.ARLWNMCC, 0, 0}, + ppc64.ASLD: [4]int{ppc64.ASLD, ppc64.ASLDCC, 0, 0}, + ppc64.ASLW: [4]int{ppc64.ASLW, ppc64.ASLWCC, 0, 0}, + ppc64.ASRAD: [4]int{ppc64.ASRAD, ppc64.ASRADCC, 0, 0}, + ppc64.ASRAW: [4]int{ppc64.ASRAW, ppc64.ASRAWCC, 0, 0}, + ppc64.ASRD: [4]int{ppc64.ASRD, ppc64.ASRDCC, 0, 0}, + ppc64.ASRW: [4]int{ppc64.ASRW, ppc64.ASRWCC, 0, 0}, + ppc64.ASUB: [4]int{ppc64.ASUB, ppc64.ASUBCC, ppc64.ASUBV, ppc64.ASUBVCC}, + ppc64.ASUBC: [4]int{ppc64.ASUBC, ppc64.ASUBCCC, ppc64.ASUBCV, ppc64.ASUBCVCC}, + ppc64.ASUBE: [4]int{ppc64.ASUBE, ppc64.ASUBECC, ppc64.ASUBEV, ppc64.ASUBEVCC}, + ppc64.ASUBME: [4]int{ppc64.ASUBME, ppc64.ASUBMECC, ppc64.ASUBMEV, ppc64.ASUBMEVCC}, + ppc64.ASUBZE: [4]int{ppc64.ASUBZE, ppc64.ASUBZECC, ppc64.ASUBZEV, ppc64.ASUBZEVCC}, + ppc64.AXOR: [4]int{ppc64.AXOR, ppc64.AXORCC, 0, 0}, +} + +var initvariants_initialized int + +func initvariants() { + if initvariants_initialized != 0 { + return + } + initvariants_initialized = 1 + + var j int + for i := int(0); i < len(varianttable); i++ { + if varianttable[i][0] == 0 { + // Instruction has no variants + varianttable[i][0] = i + + continue + } + + // Copy base form to other variants + if varianttable[i][0] == i { + for j = 0; j < len(varianttable[i]); j++ { + varianttable[varianttable[i][j]] = varianttable[i] + } + } + } +} + +// as2variant returns the variant (V_*) flags of instruction as. +func as2variant(as int) int { + initvariants() + for i := int(0); i < len(varianttable[as]); i++ { + if varianttable[as][i] == as { + return i + } + } + gc.Fatal("as2variant: instruction %v is not a variant of itself", obj.Aconv(as)) + return 0 +} + +// variant2as returns the instruction as with the given variant (V_*) flags. +// If no such variant exists, this returns 0. +func variant2as(as int, flags int) int { + initvariants() + return varianttable[as][flags] +} diff --git a/src/cmd/compile/internal/ppc64/reg.go b/src/cmd/compile/internal/ppc64/reg.go new file mode 100644 index 0000000000..fa1cb71975 --- /dev/null +++ b/src/cmd/compile/internal/ppc64/reg.go @@ -0,0 +1,162 @@ +// Derived from Inferno utils/6c/reg.c +// http://code.google.com/p/inferno-os/source/browse/utils/6c/reg.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ppc64 + +import "cmd/internal/obj/ppc64" +import "cmd/compile/internal/gc" + +const ( + NREGVAR = 64 /* 32 general + 32 floating */ +) + +var regname = []string{ + ".R0", + ".R1", + ".R2", + ".R3", + ".R4", + ".R5", + ".R6", + ".R7", + ".R8", + ".R9", + ".R10", + ".R11", + ".R12", + ".R13", + ".R14", + ".R15", + ".R16", + ".R17", + ".R18", + ".R19", + ".R20", + ".R21", + ".R22", + ".R23", + ".R24", + ".R25", + ".R26", + ".R27", + ".R28", + ".R29", + ".R30", + ".R31", + ".F0", + ".F1", + ".F2", + ".F3", + ".F4", + ".F5", + ".F6", + ".F7", + ".F8", + ".F9", + ".F10", + ".F11", + ".F12", + ".F13", + ".F14", + ".F15", + ".F16", + ".F17", + ".F18", + ".F19", + ".F20", + ".F21", + ".F22", + ".F23", + ".F24", + ".F25", + ".F26", + ".F27", + ".F28", + ".F29", + ".F30", + ".F31", +} + +func regnames(n *int) []string { + *n = NREGVAR + return regname +} + +func excludedregs() uint64 { + // Exclude registers with fixed functions + regbits := uint64(1<<0 | RtoB(ppc64.REGSP) | RtoB(ppc64.REGG) | RtoB(ppc64.REGTLS)) + + // Also exclude floating point registers with fixed constants + regbits |= RtoB(ppc64.REG_F27) | RtoB(ppc64.REG_F28) | RtoB(ppc64.REG_F29) | RtoB(ppc64.REG_F30) | RtoB(ppc64.REG_F31) + + return regbits +} + +func doregbits(r int) uint64 { + return 0 +} + +/* + * track register variables including external registers: + * bit reg + * 0 R0 + * 1 R1 + * ... ... + * 31 R31 + * 32+0 F0 + * 32+1 F1 + * ... ... + * 32+31 F31 + */ +func RtoB(r int) uint64 { + if r > ppc64.REG_R0 && r <= ppc64.REG_R31 { + return 1 << uint(r-ppc64.REG_R0) + } + if r >= ppc64.REG_F0 && r <= ppc64.REG_F31 { + return 1 << uint(32+r-ppc64.REG_F0) + } + return 0 +} + +func BtoR(b uint64) int { + b &= 0xffffffff + if b == 0 { + return 0 + } + return gc.Bitno(b) + ppc64.REG_R0 +} + +func BtoF(b uint64) int { + b >>= 32 + if b == 0 { + return 0 + } + return gc.Bitno(b) + ppc64.REG_F0 +} diff --git a/src/cmd/compile/internal/x86/cgen.go b/src/cmd/compile/internal/x86/cgen.go new file mode 100644 index 0000000000..1768674e42 --- /dev/null +++ b/src/cmd/compile/internal/x86/cgen.go @@ -0,0 +1,159 @@ +// Copyright 2009 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 x86 + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/x86" +) + +/* + * generate an addressable node in res, containing the value of n. + * n is an array index, and might be any size; res width is <= 32-bit. + * returns Prog* to patch to panic call. + */ +func igenindex(n *gc.Node, res *gc.Node, bounded bool) *obj.Prog { + if !gc.Is64(n.Type) { + if n.Addable && (gc.Simtype[n.Etype] == gc.TUINT32 || gc.Simtype[n.Etype] == gc.TINT32) { + // nothing to do. + *res = *n + } else { + gc.Tempname(res, gc.Types[gc.TUINT32]) + gc.Cgen(n, res) + } + + return nil + } + + var tmp gc.Node + gc.Tempname(&tmp, gc.Types[gc.TINT64]) + gc.Cgen(n, &tmp) + var lo gc.Node + var hi gc.Node + split64(&tmp, &lo, &hi) + gc.Tempname(res, gc.Types[gc.TUINT32]) + gmove(&lo, res) + if bounded { + splitclean() + return nil + } + + var zero gc.Node + gc.Nodconst(&zero, gc.Types[gc.TINT32], 0) + gins(x86.ACMPL, &hi, &zero) + splitclean() + return gc.Gbranch(x86.AJNE, nil, +1) +} + +func blockcopy(n, res *gc.Node, osrc, odst, w int64) { + var dst gc.Node + gc.Nodreg(&dst, gc.Types[gc.Tptr], x86.REG_DI) + var src gc.Node + gc.Nodreg(&src, gc.Types[gc.Tptr], x86.REG_SI) + + var tsrc gc.Node + gc.Tempname(&tsrc, gc.Types[gc.Tptr]) + var tdst gc.Node + gc.Tempname(&tdst, gc.Types[gc.Tptr]) + if !n.Addable { + gc.Agen(n, &tsrc) + } + if !res.Addable { + gc.Agen(res, &tdst) + } + if n.Addable { + gc.Agen(n, &src) + } else { + gmove(&tsrc, &src) + } + + if res.Op == gc.ONAME { + gc.Gvardef(res) + } + + if res.Addable { + gc.Agen(res, &dst) + } else { + gmove(&tdst, &dst) + } + + c := int32(w % 4) // bytes + q := int32(w / 4) // doublewords + + // if we are copying forward on the stack and + // the src and dst overlap, then reverse direction + if osrc < odst && int64(odst) < int64(osrc)+w { + // reverse direction + gins(x86.ASTD, nil, nil) // set direction flag + if c > 0 { + gconreg(x86.AADDL, w-1, x86.REG_SI) + gconreg(x86.AADDL, w-1, x86.REG_DI) + + gconreg(x86.AMOVL, int64(c), x86.REG_CX) + gins(x86.AREP, nil, nil) // repeat + gins(x86.AMOVSB, nil, nil) // MOVB *(SI)-,*(DI)- + } + + if q > 0 { + if c > 0 { + gconreg(x86.AADDL, -3, x86.REG_SI) + gconreg(x86.AADDL, -3, x86.REG_DI) + } else { + gconreg(x86.AADDL, w-4, x86.REG_SI) + gconreg(x86.AADDL, w-4, x86.REG_DI) + } + + gconreg(x86.AMOVL, int64(q), x86.REG_CX) + gins(x86.AREP, nil, nil) // repeat + gins(x86.AMOVSL, nil, nil) // MOVL *(SI)-,*(DI)- + } + + // we leave with the flag clear + gins(x86.ACLD, nil, nil) + } else { + gins(x86.ACLD, nil, nil) // paranoia. TODO(rsc): remove? + + // normal direction + if q > 128 || (q >= 4 && gc.Nacl) { + gconreg(x86.AMOVL, int64(q), x86.REG_CX) + gins(x86.AREP, nil, nil) // repeat + gins(x86.AMOVSL, nil, nil) // MOVL *(SI)+,*(DI)+ + } else if q >= 4 { + p := gins(obj.ADUFFCOPY, nil, nil) + p.To.Type = obj.TYPE_ADDR + p.To.Sym = gc.Linksym(gc.Pkglookup("duffcopy", gc.Runtimepkg)) + + // 10 and 128 = magic constants: see ../../runtime/asm_386.s + p.To.Offset = 10 * (128 - int64(q)) + } else if !gc.Nacl && c == 0 { + var cx gc.Node + gc.Nodreg(&cx, gc.Types[gc.TINT32], x86.REG_CX) + + // We don't need the MOVSL side-effect of updating SI and DI, + // and issuing a sequence of MOVLs directly is faster. + src.Op = gc.OINDREG + + dst.Op = gc.OINDREG + for q > 0 { + gmove(&src, &cx) // MOVL x+(SI),CX + gmove(&cx, &dst) // MOVL CX,x+(DI) + src.Xoffset += 4 + dst.Xoffset += 4 + q-- + } + } else { + for q > 0 { + gins(x86.AMOVSL, nil, nil) // MOVL *(SI)+,*(DI)+ + q-- + } + } + + for c > 0 { + gins(x86.AMOVSB, nil, nil) // MOVB *(SI)+,*(DI)+ + c-- + } + } +} diff --git a/src/cmd/compile/internal/x86/cgen64.go b/src/cmd/compile/internal/x86/cgen64.go new file mode 100644 index 0000000000..0b061ffb60 --- /dev/null +++ b/src/cmd/compile/internal/x86/cgen64.go @@ -0,0 +1,598 @@ +// Copyright 2009 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 x86 + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/x86" +) + +/* + * attempt to generate 64-bit + * res = n + * return 1 on success, 0 if op not handled. + */ +func cgen64(n *gc.Node, res *gc.Node) { + if res.Op != gc.OINDREG && res.Op != gc.ONAME { + gc.Dump("n", n) + gc.Dump("res", res) + gc.Fatal("cgen64 %v of %v", gc.Oconv(int(n.Op), 0), gc.Oconv(int(res.Op), 0)) + } + + switch n.Op { + default: + gc.Fatal("cgen64 %v", gc.Oconv(int(n.Op), 0)) + + case gc.OMINUS: + gc.Cgen(n.Left, res) + var hi1 gc.Node + var lo1 gc.Node + split64(res, &lo1, &hi1) + gins(x86.ANEGL, nil, &lo1) + gins(x86.AADCL, ncon(0), &hi1) + gins(x86.ANEGL, nil, &hi1) + splitclean() + return + + case gc.OCOM: + gc.Cgen(n.Left, res) + var lo1 gc.Node + var hi1 gc.Node + split64(res, &lo1, &hi1) + gins(x86.ANOTL, nil, &lo1) + gins(x86.ANOTL, nil, &hi1) + splitclean() + return + + // binary operators. + // common setup below. + case gc.OADD, + gc.OSUB, + gc.OMUL, + gc.OLROT, + gc.OLSH, + gc.ORSH, + gc.OAND, + gc.OOR, + gc.OXOR: + break + } + + l := n.Left + r := n.Right + if !l.Addable { + var t1 gc.Node + gc.Tempname(&t1, l.Type) + gc.Cgen(l, &t1) + l = &t1 + } + + if r != nil && !r.Addable { + var t2 gc.Node + gc.Tempname(&t2, r.Type) + gc.Cgen(r, &t2) + r = &t2 + } + + var ax gc.Node + gc.Nodreg(&ax, gc.Types[gc.TINT32], x86.REG_AX) + var cx gc.Node + gc.Nodreg(&cx, gc.Types[gc.TINT32], x86.REG_CX) + var dx gc.Node + gc.Nodreg(&dx, gc.Types[gc.TINT32], x86.REG_DX) + + // Setup for binary operation. + var hi1 gc.Node + var lo1 gc.Node + split64(l, &lo1, &hi1) + + var lo2 gc.Node + var hi2 gc.Node + if gc.Is64(r.Type) { + split64(r, &lo2, &hi2) + } + + // Do op. Leave result in DX:AX. + switch n.Op { + // TODO: Constants + case gc.OADD: + gins(x86.AMOVL, &lo1, &ax) + + gins(x86.AMOVL, &hi1, &dx) + gins(x86.AADDL, &lo2, &ax) + gins(x86.AADCL, &hi2, &dx) + + // TODO: Constants. + case gc.OSUB: + gins(x86.AMOVL, &lo1, &ax) + + gins(x86.AMOVL, &hi1, &dx) + gins(x86.ASUBL, &lo2, &ax) + gins(x86.ASBBL, &hi2, &dx) + + // let's call the next two EX and FX. + case gc.OMUL: + var ex gc.Node + gc.Regalloc(&ex, gc.Types[gc.TPTR32], nil) + + var fx gc.Node + gc.Regalloc(&fx, gc.Types[gc.TPTR32], nil) + + // load args into DX:AX and EX:CX. + gins(x86.AMOVL, &lo1, &ax) + + gins(x86.AMOVL, &hi1, &dx) + gins(x86.AMOVL, &lo2, &cx) + gins(x86.AMOVL, &hi2, &ex) + + // if DX and EX are zero, use 32 x 32 -> 64 unsigned multiply. + gins(x86.AMOVL, &dx, &fx) + + gins(x86.AORL, &ex, &fx) + p1 := gc.Gbranch(x86.AJNE, nil, 0) + gins(x86.AMULL, &cx, nil) // implicit &ax + p2 := gc.Gbranch(obj.AJMP, nil, 0) + gc.Patch(p1, gc.Pc) + + // full 64x64 -> 64, from 32x32 -> 64. + gins(x86.AIMULL, &cx, &dx) + + gins(x86.AMOVL, &ax, &fx) + gins(x86.AIMULL, &ex, &fx) + gins(x86.AADDL, &dx, &fx) + gins(x86.AMOVL, &cx, &dx) + gins(x86.AMULL, &dx, nil) // implicit &ax + gins(x86.AADDL, &fx, &dx) + gc.Patch(p2, gc.Pc) + + gc.Regfree(&ex) + gc.Regfree(&fx) + + // We only rotate by a constant c in [0,64). + // if c >= 32: + // lo, hi = hi, lo + // c -= 32 + // if c == 0: + // no-op + // else: + // t = hi + // shld hi:lo, c + // shld lo:t, c + case gc.OLROT: + v := uint64(r.Int()) + + if v >= 32 { + // reverse during load to do the first 32 bits of rotate + v -= 32 + + gins(x86.AMOVL, &lo1, &dx) + gins(x86.AMOVL, &hi1, &ax) + } else { + gins(x86.AMOVL, &lo1, &ax) + gins(x86.AMOVL, &hi1, &dx) + } + + if v == 0 { + } else // done + { + gins(x86.AMOVL, &dx, &cx) + p1 := gins(x86.ASHLL, ncon(uint32(v)), &dx) + p1.From.Index = x86.REG_AX // double-width shift + p1.From.Scale = 0 + p1 = gins(x86.ASHLL, ncon(uint32(v)), &ax) + p1.From.Index = x86.REG_CX // double-width shift + p1.From.Scale = 0 + } + + case gc.OLSH: + if r.Op == gc.OLITERAL { + v := uint64(r.Int()) + if v >= 64 { + if gc.Is64(r.Type) { + splitclean() + } + splitclean() + split64(res, &lo2, &hi2) + gins(x86.AMOVL, ncon(0), &lo2) + gins(x86.AMOVL, ncon(0), &hi2) + splitclean() + return + } + + if v >= 32 { + if gc.Is64(r.Type) { + splitclean() + } + split64(res, &lo2, &hi2) + gmove(&lo1, &hi2) + if v > 32 { + gins(x86.ASHLL, ncon(uint32(v-32)), &hi2) + } + + gins(x86.AMOVL, ncon(0), &lo2) + splitclean() + splitclean() + return + } + + // general shift + gins(x86.AMOVL, &lo1, &ax) + + gins(x86.AMOVL, &hi1, &dx) + p1 := gins(x86.ASHLL, ncon(uint32(v)), &dx) + p1.From.Index = x86.REG_AX // double-width shift + p1.From.Scale = 0 + gins(x86.ASHLL, ncon(uint32(v)), &ax) + break + } + + // load value into DX:AX. + gins(x86.AMOVL, &lo1, &ax) + + gins(x86.AMOVL, &hi1, &dx) + + // load shift value into register. + // if high bits are set, zero value. + var p1 *obj.Prog + + if gc.Is64(r.Type) { + gins(x86.ACMPL, &hi2, ncon(0)) + p1 = gc.Gbranch(x86.AJNE, nil, +1) + gins(x86.AMOVL, &lo2, &cx) + } else { + cx.Type = gc.Types[gc.TUINT32] + gmove(r, &cx) + } + + // if shift count is >=64, zero value + gins(x86.ACMPL, &cx, ncon(64)) + + p2 := gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1) + if p1 != nil { + gc.Patch(p1, gc.Pc) + } + gins(x86.AXORL, &dx, &dx) + gins(x86.AXORL, &ax, &ax) + gc.Patch(p2, gc.Pc) + + // if shift count is >= 32, zero low. + gins(x86.ACMPL, &cx, ncon(32)) + + p1 = gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1) + gins(x86.AMOVL, &ax, &dx) + gins(x86.ASHLL, &cx, &dx) // SHLL only uses bottom 5 bits of count + gins(x86.AXORL, &ax, &ax) + p2 = gc.Gbranch(obj.AJMP, nil, 0) + gc.Patch(p1, gc.Pc) + + // general shift + p1 = gins(x86.ASHLL, &cx, &dx) + + p1.From.Index = x86.REG_AX // double-width shift + p1.From.Scale = 0 + gins(x86.ASHLL, &cx, &ax) + gc.Patch(p2, gc.Pc) + + case gc.ORSH: + if r.Op == gc.OLITERAL { + v := uint64(r.Int()) + if v >= 64 { + if gc.Is64(r.Type) { + splitclean() + } + splitclean() + split64(res, &lo2, &hi2) + if hi1.Type.Etype == gc.TINT32 { + gmove(&hi1, &lo2) + gins(x86.ASARL, ncon(31), &lo2) + gmove(&hi1, &hi2) + gins(x86.ASARL, ncon(31), &hi2) + } else { + gins(x86.AMOVL, ncon(0), &lo2) + gins(x86.AMOVL, ncon(0), &hi2) + } + + splitclean() + return + } + + if v >= 32 { + if gc.Is64(r.Type) { + splitclean() + } + split64(res, &lo2, &hi2) + gmove(&hi1, &lo2) + if v > 32 { + gins(optoas(gc.ORSH, hi1.Type), ncon(uint32(v-32)), &lo2) + } + if hi1.Type.Etype == gc.TINT32 { + gmove(&hi1, &hi2) + gins(x86.ASARL, ncon(31), &hi2) + } else { + gins(x86.AMOVL, ncon(0), &hi2) + } + splitclean() + splitclean() + return + } + + // general shift + gins(x86.AMOVL, &lo1, &ax) + + gins(x86.AMOVL, &hi1, &dx) + p1 := gins(x86.ASHRL, ncon(uint32(v)), &ax) + p1.From.Index = x86.REG_DX // double-width shift + p1.From.Scale = 0 + gins(optoas(gc.ORSH, hi1.Type), ncon(uint32(v)), &dx) + break + } + + // load value into DX:AX. + gins(x86.AMOVL, &lo1, &ax) + + gins(x86.AMOVL, &hi1, &dx) + + // load shift value into register. + // if high bits are set, zero value. + var p1 *obj.Prog + + if gc.Is64(r.Type) { + gins(x86.ACMPL, &hi2, ncon(0)) + p1 = gc.Gbranch(x86.AJNE, nil, +1) + gins(x86.AMOVL, &lo2, &cx) + } else { + cx.Type = gc.Types[gc.TUINT32] + gmove(r, &cx) + } + + // if shift count is >=64, zero or sign-extend value + gins(x86.ACMPL, &cx, ncon(64)) + + p2 := gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1) + if p1 != nil { + gc.Patch(p1, gc.Pc) + } + if hi1.Type.Etype == gc.TINT32 { + gins(x86.ASARL, ncon(31), &dx) + gins(x86.AMOVL, &dx, &ax) + } else { + gins(x86.AXORL, &dx, &dx) + gins(x86.AXORL, &ax, &ax) + } + + gc.Patch(p2, gc.Pc) + + // if shift count is >= 32, sign-extend hi. + gins(x86.ACMPL, &cx, ncon(32)) + + p1 = gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1) + gins(x86.AMOVL, &dx, &ax) + if hi1.Type.Etype == gc.TINT32 { + gins(x86.ASARL, &cx, &ax) // SARL only uses bottom 5 bits of count + gins(x86.ASARL, ncon(31), &dx) + } else { + gins(x86.ASHRL, &cx, &ax) + gins(x86.AXORL, &dx, &dx) + } + + p2 = gc.Gbranch(obj.AJMP, nil, 0) + gc.Patch(p1, gc.Pc) + + // general shift + p1 = gins(x86.ASHRL, &cx, &ax) + + p1.From.Index = x86.REG_DX // double-width shift + p1.From.Scale = 0 + gins(optoas(gc.ORSH, hi1.Type), &cx, &dx) + gc.Patch(p2, gc.Pc) + + // make constant the right side (it usually is anyway). + case gc.OXOR, + gc.OAND, + gc.OOR: + if lo1.Op == gc.OLITERAL { + nswap(&lo1, &lo2) + nswap(&hi1, &hi2) + } + + if lo2.Op == gc.OLITERAL { + // special cases for constants. + lv := uint32(lo2.Int()) + hv := uint32(hi2.Int()) + splitclean() // right side + split64(res, &lo2, &hi2) + switch n.Op { + case gc.OXOR: + gmove(&lo1, &lo2) + gmove(&hi1, &hi2) + switch lv { + case 0: + break + + case 0xffffffff: + gins(x86.ANOTL, nil, &lo2) + + default: + gins(x86.AXORL, ncon(lv), &lo2) + } + + switch hv { + case 0: + break + + case 0xffffffff: + gins(x86.ANOTL, nil, &hi2) + + default: + gins(x86.AXORL, ncon(hv), &hi2) + } + + case gc.OAND: + switch lv { + case 0: + gins(x86.AMOVL, ncon(0), &lo2) + + default: + gmove(&lo1, &lo2) + if lv != 0xffffffff { + gins(x86.AANDL, ncon(lv), &lo2) + } + } + + switch hv { + case 0: + gins(x86.AMOVL, ncon(0), &hi2) + + default: + gmove(&hi1, &hi2) + if hv != 0xffffffff { + gins(x86.AANDL, ncon(hv), &hi2) + } + } + + case gc.OOR: + switch lv { + case 0: + gmove(&lo1, &lo2) + + case 0xffffffff: + gins(x86.AMOVL, ncon(0xffffffff), &lo2) + + default: + gmove(&lo1, &lo2) + gins(x86.AORL, ncon(lv), &lo2) + } + + switch hv { + case 0: + gmove(&hi1, &hi2) + + case 0xffffffff: + gins(x86.AMOVL, ncon(0xffffffff), &hi2) + + default: + gmove(&hi1, &hi2) + gins(x86.AORL, ncon(hv), &hi2) + } + } + + splitclean() + splitclean() + return + } + + gins(x86.AMOVL, &lo1, &ax) + gins(x86.AMOVL, &hi1, &dx) + gins(optoas(int(n.Op), lo1.Type), &lo2, &ax) + gins(optoas(int(n.Op), lo1.Type), &hi2, &dx) + } + + if gc.Is64(r.Type) { + splitclean() + } + splitclean() + + split64(res, &lo1, &hi1) + gins(x86.AMOVL, &ax, &lo1) + gins(x86.AMOVL, &dx, &hi1) + splitclean() +} + +/* + * generate comparison of nl, nr, both 64-bit. + * nl is memory; nr is constant or memory. + */ +func cmp64(nl *gc.Node, nr *gc.Node, op int, likely int, to *obj.Prog) { + var lo1 gc.Node + var hi1 gc.Node + var lo2 gc.Node + var hi2 gc.Node + var rr gc.Node + + split64(nl, &lo1, &hi1) + split64(nr, &lo2, &hi2) + + // compare most significant word; + // if they differ, we're done. + t := hi1.Type + + if nl.Op == gc.OLITERAL || nr.Op == gc.OLITERAL { + gins(x86.ACMPL, &hi1, &hi2) + } else { + gc.Regalloc(&rr, gc.Types[gc.TINT32], nil) + gins(x86.AMOVL, &hi1, &rr) + gins(x86.ACMPL, &rr, &hi2) + gc.Regfree(&rr) + } + + var br *obj.Prog + switch op { + default: + gc.Fatal("cmp64 %v %v", gc.Oconv(int(op), 0), t) + + // cmp hi + // jne L + // cmp lo + // jeq to + // L: + case gc.OEQ: + br = gc.Gbranch(x86.AJNE, nil, -likely) + + // cmp hi + // jne to + // cmp lo + // jne to + case gc.ONE: + gc.Patch(gc.Gbranch(x86.AJNE, nil, likely), to) + + // cmp hi + // jgt to + // jlt L + // cmp lo + // jge to (or jgt to) + // L: + case gc.OGE, + gc.OGT: + gc.Patch(gc.Gbranch(optoas(gc.OGT, t), nil, likely), to) + + br = gc.Gbranch(optoas(gc.OLT, t), nil, -likely) + + // cmp hi + // jlt to + // jgt L + // cmp lo + // jle to (or jlt to) + // L: + case gc.OLE, + gc.OLT: + gc.Patch(gc.Gbranch(optoas(gc.OLT, t), nil, likely), to) + + br = gc.Gbranch(optoas(gc.OGT, t), nil, -likely) + } + + // compare least significant word + t = lo1.Type + + if nl.Op == gc.OLITERAL || nr.Op == gc.OLITERAL { + gins(x86.ACMPL, &lo1, &lo2) + } else { + gc.Regalloc(&rr, gc.Types[gc.TINT32], nil) + gins(x86.AMOVL, &lo1, &rr) + gins(x86.ACMPL, &rr, &lo2) + gc.Regfree(&rr) + } + + // jump again + gc.Patch(gc.Gbranch(optoas(op, t), nil, likely), to) + + // point first branch down here if appropriate + if br != nil { + gc.Patch(br, gc.Pc) + } + + splitclean() + splitclean() +} diff --git a/src/cmd/compile/internal/x86/galign.go b/src/cmd/compile/internal/x86/galign.go new file mode 100644 index 0000000000..2b602e1bb3 --- /dev/null +++ b/src/cmd/compile/internal/x86/galign.go @@ -0,0 +1,110 @@ +// Copyright 2009 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 x86 + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/x86" + "fmt" + "os" +) + +var thechar int = '8' + +var thestring string = "386" + +var thelinkarch *obj.LinkArch = &x86.Link386 + +func linkarchinit() { +} + +var MAXWIDTH int64 = (1 << 32) - 1 + +/* + * go declares several platform-specific type aliases: + * int, uint, and uintptr + */ +var typedefs = []gc.Typedef{ + gc.Typedef{"int", gc.TINT, gc.TINT32}, + gc.Typedef{"uint", gc.TUINT, gc.TUINT32}, + gc.Typedef{"uintptr", gc.TUINTPTR, gc.TUINT32}, +} + +func betypeinit() { + gc.Widthptr = 4 + gc.Widthint = 4 + gc.Widthreg = 4 +} + +func Main() { + gc.Thearch.Thechar = thechar + gc.Thearch.Thestring = thestring + gc.Thearch.Thelinkarch = thelinkarch + gc.Thearch.Typedefs = typedefs + gc.Thearch.REGSP = x86.REGSP + gc.Thearch.REGCTXT = x86.REGCTXT + gc.Thearch.REGCALLX = x86.REG_BX + gc.Thearch.REGCALLX2 = x86.REG_AX + gc.Thearch.REGRETURN = x86.REG_AX + gc.Thearch.REGMIN = x86.REG_AX + gc.Thearch.REGMAX = x86.REG_DI + switch v := obj.Getgo386(); v { + case "387": + gc.Thearch.FREGMIN = x86.REG_F0 + gc.Thearch.FREGMAX = x86.REG_F7 + gc.Thearch.Use387 = true + case "sse2": + gc.Thearch.FREGMIN = x86.REG_X0 + gc.Thearch.FREGMAX = x86.REG_X7 + default: + fmt.Fprintf(os.Stderr, "unsupported setting GO386=%s\n", v) + gc.Exit(1) + } + gc.Thearch.MAXWIDTH = MAXWIDTH + gc.Thearch.ReservedRegs = resvd + + gc.Thearch.Betypeinit = betypeinit + gc.Thearch.Bgen_float = bgen_float + gc.Thearch.Cgen64 = cgen64 + gc.Thearch.Cgen_bmul = cgen_bmul + gc.Thearch.Cgen_float = cgen_float + gc.Thearch.Cgen_hmul = cgen_hmul + gc.Thearch.Cgen_shift = cgen_shift + gc.Thearch.Clearfat = clearfat + gc.Thearch.Cmp64 = cmp64 + gc.Thearch.Defframe = defframe + gc.Thearch.Dodiv = cgen_div + gc.Thearch.Excise = excise + 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 + gc.Thearch.Igenindex = igenindex + gc.Thearch.Linkarchinit = linkarchinit + gc.Thearch.Peep = peep + gc.Thearch.Proginfo = proginfo + gc.Thearch.Regtyp = regtyp + gc.Thearch.Sameaddr = sameaddr + gc.Thearch.Smallindir = smallindir + gc.Thearch.Stackaddr = stackaddr + gc.Thearch.Blockcopy = blockcopy + gc.Thearch.Sudoaddable = sudoaddable + gc.Thearch.Sudoclean = sudoclean + gc.Thearch.Excludedregs = excludedregs + gc.Thearch.RtoB = RtoB + gc.Thearch.FtoB = FtoB + gc.Thearch.BtoR = BtoR + gc.Thearch.BtoF = BtoF + gc.Thearch.Optoas = optoas + gc.Thearch.Doregbits = doregbits + gc.Thearch.Regnames = regnames + + gc.Main() + gc.Exit(0) +} diff --git a/src/cmd/compile/internal/x86/ggen.go b/src/cmd/compile/internal/x86/ggen.go new file mode 100644 index 0000000000..dabc139f30 --- /dev/null +++ b/src/cmd/compile/internal/x86/ggen.go @@ -0,0 +1,940 @@ +// Copyright 2009 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 x86 + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/x86" +) + +func defframe(ptxt *obj.Prog) { + var n *gc.Node + + // fill in argument size, stack size + ptxt.To.Type = obj.TYPE_TEXTSIZE + + ptxt.To.Val = int32(gc.Rnd(gc.Curfn.Type.Argwid, int64(gc.Widthptr))) + frame := uint32(gc.Rnd(gc.Stksize+gc.Maxarg, int64(gc.Widthreg))) + ptxt.To.Offset = int64(frame) + + // insert code to zero ambiguously live variables + // so that the garbage collector only sees initialized values + // when it looks for pointers. + p := ptxt + + hi := int64(0) + lo := hi + ax := uint32(0) + for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next { + n = l.N + if !n.Name.Needzero { + continue + } + if n.Class != gc.PAUTO { + gc.Fatal("needzero class %d", n.Class) + } + if n.Type.Width%int64(gc.Widthptr) != 0 || n.Xoffset%int64(gc.Widthptr) != 0 || n.Type.Width == 0 { + gc.Fatal("var %v has size %d offset %d", gc.Nconv(n, obj.FmtLong), int(n.Type.Width), int(n.Xoffset)) + } + if lo != hi && n.Xoffset+n.Type.Width == lo-int64(2*gc.Widthptr) { + // merge with range we already have + lo = n.Xoffset + + continue + } + + // zero old range + p = zerorange(p, int64(frame), lo, hi, &ax) + + // set new range + hi = n.Xoffset + n.Type.Width + + lo = n.Xoffset + } + + // zero final range + zerorange(p, int64(frame), lo, hi, &ax) +} + +func zerorange(p *obj.Prog, frame int64, lo int64, hi int64, ax *uint32) *obj.Prog { + cnt := hi - lo + if cnt == 0 { + return p + } + if *ax == 0 { + p = appendpp(p, x86.AMOVL, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, x86.REG_AX, 0) + *ax = 1 + } + + if cnt <= int64(4*gc.Widthreg) { + for i := int64(0); i < cnt; i += int64(gc.Widthreg) { + p = appendpp(p, x86.AMOVL, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, frame+lo+i) + } + } else if !gc.Nacl && cnt <= int64(128*gc.Widthreg) { + p = appendpp(p, x86.ALEAL, obj.TYPE_MEM, x86.REG_SP, frame+lo, obj.TYPE_REG, x86.REG_DI, 0) + p = appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_ADDR, 0, 1*(128-cnt/int64(gc.Widthreg))) + p.To.Sym = gc.Linksym(gc.Pkglookup("duffzero", gc.Runtimepkg)) + } else { + p = appendpp(p, x86.AMOVL, obj.TYPE_CONST, 0, cnt/int64(gc.Widthreg), obj.TYPE_REG, x86.REG_CX, 0) + p = appendpp(p, x86.ALEAL, obj.TYPE_MEM, x86.REG_SP, frame+lo, obj.TYPE_REG, x86.REG_DI, 0) + p = appendpp(p, x86.AREP, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0) + p = appendpp(p, x86.ASTOSL, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0) + } + + return p +} + +func appendpp(p *obj.Prog, as int, ftype int, freg int, foffset int64, ttype int, treg int, toffset int64) *obj.Prog { + q := gc.Ctxt.NewProg() + gc.Clearp(q) + q.As = int16(as) + q.Lineno = p.Lineno + q.From.Type = int16(ftype) + q.From.Reg = int16(freg) + q.From.Offset = foffset + q.To.Type = int16(ttype) + q.To.Reg = int16(treg) + q.To.Offset = toffset + q.Link = p.Link + p.Link = q + return q +} + +func clearfat(nl *gc.Node) { + /* clear a fat object */ + if gc.Debug['g'] != 0 { + gc.Dump("\nclearfat", nl) + } + + w := uint32(nl.Type.Width) + + // Avoid taking the address for simple enough types. + if gc.Componentgen(nil, nl) { + return + } + + c := w % 4 // bytes + q := w / 4 // quads + + if q < 4 { + // Write sequence of MOV 0, off(base) instead of using STOSL. + // The hope is that although the code will be slightly longer, + // the MOVs will have no dependencies and pipeline better + // than the unrolled STOSL loop. + // NOTE: Must use agen, not igen, so that optimizer sees address + // being taken. We are not writing on field boundaries. + var n1 gc.Node + gc.Regalloc(&n1, gc.Types[gc.Tptr], nil) + + gc.Agen(nl, &n1) + n1.Op = gc.OINDREG + var z gc.Node + gc.Nodconst(&z, gc.Types[gc.TUINT64], 0) + for { + tmp14 := q + q-- + if tmp14 <= 0 { + break + } + n1.Type = z.Type + gins(x86.AMOVL, &z, &n1) + n1.Xoffset += 4 + } + + gc.Nodconst(&z, gc.Types[gc.TUINT8], 0) + for { + tmp15 := c + c-- + if tmp15 <= 0 { + break + } + n1.Type = z.Type + gins(x86.AMOVB, &z, &n1) + n1.Xoffset++ + } + + gc.Regfree(&n1) + return + } + + var n1 gc.Node + gc.Nodreg(&n1, gc.Types[gc.Tptr], x86.REG_DI) + gc.Agen(nl, &n1) + gconreg(x86.AMOVL, 0, x86.REG_AX) + + if q > 128 || (q >= 4 && gc.Nacl) { + gconreg(x86.AMOVL, int64(q), x86.REG_CX) + gins(x86.AREP, nil, nil) // repeat + gins(x86.ASTOSL, nil, nil) // STOL AL,*(DI)+ + } else if q >= 4 { + p := gins(obj.ADUFFZERO, nil, nil) + p.To.Type = obj.TYPE_ADDR + p.To.Sym = gc.Linksym(gc.Pkglookup("duffzero", gc.Runtimepkg)) + + // 1 and 128 = magic constants: see ../../runtime/asm_386.s + p.To.Offset = 1 * (128 - int64(q)) + } else { + for q > 0 { + gins(x86.ASTOSL, nil, nil) // STOL AL,*(DI)+ + q-- + } + } + + for c > 0 { + gins(x86.ASTOSB, nil, nil) // STOB AL,*(DI)+ + c-- + } +} + +var panicdiv *gc.Node + +/* + * generate division. + * caller must set: + * ax = allocated AX register + * dx = allocated DX register + * generates one of: + * res = nl / nr + * res = nl % nr + * according to op. + */ +func dodiv(op int, nl *gc.Node, nr *gc.Node, res *gc.Node, ax *gc.Node, dx *gc.Node) { + // Have to be careful about handling + // most negative int divided by -1 correctly. + // The hardware will trap. + // Also the byte divide instruction needs AH, + // which we otherwise don't have to deal with. + // Easiest way to avoid for int8, int16: use int32. + // For int32 and int64, use explicit test. + // Could use int64 hw for int32. + t := nl.Type + + t0 := t + check := 0 + if gc.Issigned[t.Etype] { + check = 1 + if gc.Isconst(nl, gc.CTINT) && nl.Int() != -1< 0 && !gc.Samereg(x, res) { + gc.Tempname(oldx, gc.Types[gc.TINT32]) + gmove(x, oldx) + } + + gc.Regalloc(x, t, x) +} + +func restx(x *gc.Node, oldx *gc.Node) { + gc.Regfree(x) + + if oldx.Op != 0 { + x.Type = gc.Types[gc.TINT32] + gmove(oldx, x) + } +} + +/* + * generate division according to op, one of: + * res = nl / nr + * res = nl % nr + */ +func cgen_div(op int, nl *gc.Node, nr *gc.Node, res *gc.Node) { + if gc.Is64(nl.Type) { + gc.Fatal("cgen_div %v", nl.Type) + } + + var t *gc.Type + if gc.Issigned[nl.Type.Etype] { + t = gc.Types[gc.TINT32] + } else { + t = gc.Types[gc.TUINT32] + } + var ax gc.Node + var oldax gc.Node + savex(x86.REG_AX, &ax, &oldax, res, t) + var olddx gc.Node + var dx gc.Node + savex(x86.REG_DX, &dx, &olddx, res, t) + dodiv(op, nl, nr, res, &ax, &dx) + restx(&dx, &olddx) + restx(&ax, &oldax) +} + +/* + * generate shift according to op, one of: + * res = nl << nr + * res = nl >> nr + */ +func cgen_shift(op int, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) { + if nl.Type.Width > 4 { + gc.Fatal("cgen_shift %v", nl.Type) + } + + w := int(nl.Type.Width * 8) + + a := optoas(op, nl.Type) + + if nr.Op == gc.OLITERAL { + var n2 gc.Node + gc.Tempname(&n2, nl.Type) + gc.Cgen(nl, &n2) + var n1 gc.Node + gc.Regalloc(&n1, nl.Type, res) + gmove(&n2, &n1) + sc := uint64(nr.Int()) + if sc >= uint64(nl.Type.Width*8) { + // large shift gets 2 shifts by width-1 + gins(a, ncon(uint32(w)-1), &n1) + + gins(a, ncon(uint32(w)-1), &n1) + } else { + gins(a, nr, &n1) + } + gmove(&n1, res) + gc.Regfree(&n1) + return + } + + var oldcx gc.Node + var cx gc.Node + gc.Nodreg(&cx, gc.Types[gc.TUINT32], x86.REG_CX) + if reg[x86.REG_CX] > 1 && !gc.Samereg(&cx, res) { + gc.Tempname(&oldcx, gc.Types[gc.TUINT32]) + gmove(&cx, &oldcx) + } + + var n1 gc.Node + var nt gc.Node + if nr.Type.Width > 4 { + gc.Tempname(&nt, nr.Type) + n1 = nt + } else { + gc.Nodreg(&n1, gc.Types[gc.TUINT32], x86.REG_CX) + gc.Regalloc(&n1, nr.Type, &n1) // to hold the shift type in CX + } + + var n2 gc.Node + if gc.Samereg(&cx, res) { + gc.Regalloc(&n2, nl.Type, nil) + } else { + gc.Regalloc(&n2, nl.Type, res) + } + if nl.Ullman >= nr.Ullman { + gc.Cgen(nl, &n2) + gc.Cgen(nr, &n1) + } else { + gc.Cgen(nr, &n1) + gc.Cgen(nl, &n2) + } + + // test and fix up large shifts + if bounded { + if nr.Type.Width > 4 { + // delayed reg alloc + gc.Nodreg(&n1, gc.Types[gc.TUINT32], x86.REG_CX) + + gc.Regalloc(&n1, gc.Types[gc.TUINT32], &n1) // to hold the shift type in CX + var lo gc.Node + var hi gc.Node + split64(&nt, &lo, &hi) + gmove(&lo, &n1) + splitclean() + } + } else { + var p1 *obj.Prog + if nr.Type.Width > 4 { + // delayed reg alloc + gc.Nodreg(&n1, gc.Types[gc.TUINT32], x86.REG_CX) + + gc.Regalloc(&n1, gc.Types[gc.TUINT32], &n1) // to hold the shift type in CX + var lo gc.Node + var hi gc.Node + split64(&nt, &lo, &hi) + gmove(&lo, &n1) + gins(optoas(gc.OCMP, gc.Types[gc.TUINT32]), &hi, ncon(0)) + p2 := gc.Gbranch(optoas(gc.ONE, gc.Types[gc.TUINT32]), nil, +1) + gins(optoas(gc.OCMP, gc.Types[gc.TUINT32]), &n1, ncon(uint32(w))) + p1 = gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1) + splitclean() + gc.Patch(p2, gc.Pc) + } else { + gins(optoas(gc.OCMP, nr.Type), &n1, ncon(uint32(w))) + p1 = gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1) + } + + if op == gc.ORSH && gc.Issigned[nl.Type.Etype] { + gins(a, ncon(uint32(w)-1), &n2) + } else { + gmove(ncon(0), &n2) + } + + gc.Patch(p1, gc.Pc) + } + + gins(a, &n1, &n2) + + if oldcx.Op != 0 { + gmove(&oldcx, &cx) + } + + gmove(&n2, res) + + gc.Regfree(&n1) + gc.Regfree(&n2) +} + +/* + * generate byte multiply: + * res = nl * nr + * there is no 2-operand byte multiply instruction so + * we do a full-width multiplication and truncate afterwards. + */ +func cgen_bmul(op int, nl *gc.Node, nr *gc.Node, res *gc.Node) bool { + if optoas(op, nl.Type) != x86.AIMULB { + return false + } + + // copy from byte to full registers + t := gc.Types[gc.TUINT32] + + if gc.Issigned[nl.Type.Etype] { + t = gc.Types[gc.TINT32] + } + + // largest ullman on left. + if nl.Ullman < nr.Ullman { + tmp := nl + nl = nr + nr = tmp + } + + var nt gc.Node + gc.Tempname(&nt, nl.Type) + gc.Cgen(nl, &nt) + var n1 gc.Node + gc.Regalloc(&n1, t, res) + gc.Cgen(nr, &n1) + var n2 gc.Node + gc.Regalloc(&n2, t, nil) + gmove(&nt, &n2) + a := optoas(op, t) + gins(a, &n2, &n1) + gc.Regfree(&n2) + gmove(&n1, res) + gc.Regfree(&n1) + + return true +} + +/* + * generate high multiply: + * res = (nl*nr) >> width + */ +func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) { + var n1 gc.Node + var n2 gc.Node + var ax gc.Node + var dx gc.Node + + t := nl.Type + a := optoas(gc.OHMUL, t) + + // gen nl in n1. + gc.Tempname(&n1, t) + + gc.Cgen(nl, &n1) + + // gen nr in n2. + gc.Regalloc(&n2, t, res) + + gc.Cgen(nr, &n2) + + // multiply. + gc.Nodreg(&ax, t, x86.REG_AX) + + gmove(&n2, &ax) + gins(a, &n1, nil) + gc.Regfree(&n2) + + if t.Width == 1 { + // byte multiply behaves differently. + gc.Nodreg(&ax, t, x86.REG_AH) + + gc.Nodreg(&dx, t, x86.REG_DX) + gmove(&ax, &dx) + } + + gc.Nodreg(&dx, t, x86.REG_DX) + gmove(&dx, res) +} + +/* + * generate floating-point operation. + */ +func cgen_float(n *gc.Node, res *gc.Node) { + nl := n.Left + switch n.Op { + case gc.OEQ, + gc.ONE, + gc.OLT, + gc.OLE, + gc.OGE: + p1 := gc.Gbranch(obj.AJMP, nil, 0) + p2 := gc.Pc + gmove(gc.Nodbool(true), res) + p3 := gc.Gbranch(obj.AJMP, nil, 0) + gc.Patch(p1, gc.Pc) + gc.Bgen(n, true, 0, p2) + gmove(gc.Nodbool(false), res) + gc.Patch(p3, gc.Pc) + return + + case gc.OPLUS: + gc.Cgen(nl, res) + return + + case gc.OCONV: + if gc.Eqtype(n.Type, nl.Type) || gc.Noconv(n.Type, nl.Type) { + gc.Cgen(nl, res) + return + } + + var n2 gc.Node + gc.Tempname(&n2, n.Type) + var n1 gc.Node + gc.Mgen(nl, &n1, res) + gmove(&n1, &n2) + gmove(&n2, res) + gc.Mfree(&n1) + return + } + + if gc.Thearch.Use387 { + cgen_float387(n, res) + } else { + cgen_floatsse(n, res) + } +} + +// floating-point. 387 (not SSE2) +func cgen_float387(n *gc.Node, res *gc.Node) { + var f0 gc.Node + var f1 gc.Node + + nl := n.Left + nr := n.Right + gc.Nodreg(&f0, nl.Type, x86.REG_F0) + gc.Nodreg(&f1, n.Type, x86.REG_F0+1) + if nr != nil { + // binary + if nl.Ullman >= nr.Ullman { + gc.Cgen(nl, &f0) + if nr.Addable { + gins(foptoas(int(n.Op), n.Type, 0), nr, &f0) + } else { + gc.Cgen(nr, &f0) + gins(foptoas(int(n.Op), n.Type, Fpop), &f0, &f1) + } + } else { + gc.Cgen(nr, &f0) + if nl.Addable { + gins(foptoas(int(n.Op), n.Type, Frev), nl, &f0) + } else { + gc.Cgen(nl, &f0) + gins(foptoas(int(n.Op), n.Type, Frev|Fpop), &f0, &f1) + } + } + + gmove(&f0, res) + return + } + + // unary + gc.Cgen(nl, &f0) + + if n.Op != gc.OCONV && n.Op != gc.OPLUS { + gins(foptoas(int(n.Op), n.Type, 0), nil, nil) + } + gmove(&f0, res) + return +} + +func cgen_floatsse(n *gc.Node, res *gc.Node) { + var a int + + nl := n.Left + nr := n.Right + switch n.Op { + default: + gc.Dump("cgen_floatsse", n) + gc.Fatal("cgen_floatsse %v", gc.Oconv(int(n.Op), 0)) + return + + case gc.OMINUS, + gc.OCOM: + nr = gc.Nodintconst(-1) + gc.Convlit(&nr, n.Type) + a = foptoas(gc.OMUL, nl.Type, 0) + goto sbop + + // symmetric binary + case gc.OADD, + gc.OMUL: + a = foptoas(int(n.Op), nl.Type, 0) + + goto sbop + + // asymmetric binary + case gc.OSUB, + gc.OMOD, + gc.ODIV: + a = foptoas(int(n.Op), nl.Type, 0) + + goto abop + } + +sbop: // symmetric binary + if nl.Ullman < nr.Ullman || nl.Op == gc.OLITERAL { + r := nl + nl = nr + nr = r + } + +abop: // asymmetric binary + if nl.Ullman >= nr.Ullman { + var nt gc.Node + gc.Tempname(&nt, nl.Type) + gc.Cgen(nl, &nt) + var n2 gc.Node + gc.Mgen(nr, &n2, nil) + var n1 gc.Node + gc.Regalloc(&n1, nl.Type, res) + gmove(&nt, &n1) + gins(a, &n2, &n1) + gmove(&n1, res) + gc.Regfree(&n1) + gc.Mfree(&n2) + } else { + var n2 gc.Node + gc.Regalloc(&n2, nr.Type, res) + gc.Cgen(nr, &n2) + var n1 gc.Node + gc.Regalloc(&n1, nl.Type, nil) + gc.Cgen(nl, &n1) + gins(a, &n2, &n1) + gc.Regfree(&n2) + gmove(&n1, res) + gc.Regfree(&n1) + } + + return +} + +func bgen_float(n *gc.Node, wantTrue bool, likely int, to *obj.Prog) { + nl := n.Left + nr := n.Right + a := int(n.Op) + if !wantTrue { + // brcom is not valid on floats when NaN is involved. + p1 := gc.Gbranch(obj.AJMP, nil, 0) + p2 := gc.Gbranch(obj.AJMP, nil, 0) + gc.Patch(p1, gc.Pc) + + // No need to avoid re-genning ninit. + bgen_float(n, true, -likely, p2) + + gc.Patch(gc.Gbranch(obj.AJMP, nil, 0), to) + gc.Patch(p2, gc.Pc) + return + } + + if gc.Thearch.Use387 { + a = gc.Brrev(a) // because the args are stacked + if a == gc.OGE || a == gc.OGT { + // only < and <= work right with NaN; reverse if needed + nl, nr = nr, nl + a = gc.Brrev(a) + } + + var ax, n2, tmp gc.Node + gc.Nodreg(&tmp, nr.Type, x86.REG_F0) + gc.Nodreg(&n2, nr.Type, x86.REG_F0+1) + gc.Nodreg(&ax, gc.Types[gc.TUINT16], x86.REG_AX) + if gc.Simsimtype(nr.Type) == gc.TFLOAT64 { + if nl.Ullman > nr.Ullman { + gc.Cgen(nl, &tmp) + gc.Cgen(nr, &tmp) + gins(x86.AFXCHD, &tmp, &n2) + } else { + gc.Cgen(nr, &tmp) + gc.Cgen(nl, &tmp) + } + + gins(x86.AFUCOMIP, &tmp, &n2) + gins(x86.AFMOVDP, &tmp, &tmp) // annoying pop but still better than STSW+SAHF + } else { + // TODO(rsc): The moves back and forth to memory + // here are for truncating the value to 32 bits. + // This handles 32-bit comparison but presumably + // all the other ops have the same problem. + // We need to figure out what the right general + // solution is, besides telling people to use float64. + var t1 gc.Node + gc.Tempname(&t1, gc.Types[gc.TFLOAT32]) + + var t2 gc.Node + gc.Tempname(&t2, gc.Types[gc.TFLOAT32]) + gc.Cgen(nr, &t1) + gc.Cgen(nl, &t2) + gmove(&t2, &tmp) + gins(x86.AFCOMFP, &t1, &tmp) + gins(x86.AFSTSW, nil, &ax) + gins(x86.ASAHF, nil, nil) + } + } else { + // Not 387 + if !nl.Addable { + nl = gc.CgenTemp(nl) + } + if !nr.Addable { + nr = gc.CgenTemp(nr) + } + + var n2 gc.Node + gc.Regalloc(&n2, nr.Type, nil) + gmove(nr, &n2) + nr = &n2 + + if nl.Op != gc.OREGISTER { + var n3 gc.Node + gc.Regalloc(&n3, nl.Type, nil) + gmove(nl, &n3) + nl = &n3 + } + + if a == gc.OGE || a == gc.OGT { + // only < and <= work right with NaN; reverse if needed + nl, nr = nr, nl + a = gc.Brrev(a) + } + + gins(foptoas(gc.OCMP, nr.Type, 0), nl, nr) + if nl.Op == gc.OREGISTER { + gc.Regfree(nl) + } + gc.Regfree(nr) + } + + switch a { + case gc.OEQ: + // neither NE nor P + p1 := gc.Gbranch(x86.AJNE, nil, -likely) + p2 := gc.Gbranch(x86.AJPS, nil, -likely) + gc.Patch(gc.Gbranch(obj.AJMP, nil, 0), to) + gc.Patch(p1, gc.Pc) + gc.Patch(p2, gc.Pc) + case gc.ONE: + // either NE or P + gc.Patch(gc.Gbranch(x86.AJNE, nil, likely), to) + gc.Patch(gc.Gbranch(x86.AJPS, nil, likely), to) + default: + gc.Patch(gc.Gbranch(optoas(a, nr.Type), nil, likely), to) + } +} + +// Called after regopt and peep have run. +// Expand CHECKNIL pseudo-op into actual nil pointer check. +func expandchecks(firstp *obj.Prog) { + var p1 *obj.Prog + var p2 *obj.Prog + + for p := firstp; p != nil; p = p.Link { + if p.As != obj.ACHECKNIL { + continue + } + if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers + gc.Warnl(int(p.Lineno), "generated nil check") + } + + // check is + // CMP arg, $0 + // JNE 2(PC) (likely) + // MOV AX, 0 + p1 = gc.Ctxt.NewProg() + + p2 = gc.Ctxt.NewProg() + gc.Clearp(p1) + gc.Clearp(p2) + p1.Link = p2 + p2.Link = p.Link + p.Link = p1 + p1.Lineno = p.Lineno + p2.Lineno = p.Lineno + p1.Pc = 9999 + p2.Pc = 9999 + p.As = x86.ACMPL + p.To.Type = obj.TYPE_CONST + p.To.Offset = 0 + p1.As = x86.AJNE + p1.From.Type = obj.TYPE_CONST + p1.From.Offset = 1 // likely + p1.To.Type = obj.TYPE_BRANCH + p1.To.Val = p2.Link + + // crash by write to memory address 0. + // if possible, since we know arg is 0, use 0(arg), + // which will be shorter to encode than plain 0. + p2.As = x86.AMOVL + + p2.From.Type = obj.TYPE_REG + p2.From.Reg = x86.REG_AX + if regtyp(&p.From) { + p2.To.Type = obj.TYPE_MEM + p2.To.Reg = p.From.Reg + } else { + p2.To.Type = obj.TYPE_MEM + } + p2.To.Offset = 0 + } +} + +// addr += index*width if possible. +func addindex(index *gc.Node, width int64, addr *gc.Node) bool { + switch width { + case 1, 2, 4, 8: + p1 := gins(x86.ALEAL, index, addr) + p1.From.Type = obj.TYPE_MEM + p1.From.Scale = int16(width) + p1.From.Index = p1.From.Reg + p1.From.Reg = p1.To.Reg + return true + } + return false +} + +// res = runtime.getg() +func getg(res *gc.Node) { + var n1 gc.Node + gc.Regalloc(&n1, res.Type, res) + mov := optoas(gc.OAS, gc.Types[gc.Tptr]) + p := gins(mov, nil, &n1) + p.From.Type = obj.TYPE_REG + p.From.Reg = x86.REG_TLS + p = gins(mov, nil, &n1) + p.From = p.To + p.From.Type = obj.TYPE_MEM + p.From.Index = x86.REG_TLS + p.From.Scale = 1 + gmove(&n1, res) + gc.Regfree(&n1) +} diff --git a/src/cmd/compile/internal/x86/gsubr.go b/src/cmd/compile/internal/x86/gsubr.go new file mode 100644 index 0000000000..baf251781c --- /dev/null +++ b/src/cmd/compile/internal/x86/gsubr.go @@ -0,0 +1,1798 @@ +// Derived from Inferno utils/8c/txt.c +// http://code.google.com/p/inferno-os/source/browse/utils/8c/txt.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package x86 + +import ( + "cmd/compile/internal/big" + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/x86" + "fmt" +) + +// TODO(rsc): Can make this bigger if we move +// the text segment up higher in 8l for all GOOS. +// At the same time, can raise StackBig in ../../runtime/stack.h. +var unmappedzero uint32 = 4096 + +// foptoas flags +const ( + Frev = 1 << 0 + Fpop = 1 << 1 + Fpop2 = 1 << 2 +) + +/* + * return Axxx for Oxxx on type t. + */ +func optoas(op int, t *gc.Type) int { + if t == nil { + gc.Fatal("optoas: t is nil") + } + + a := obj.AXXX + switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) { + default: + gc.Fatal("optoas: no entry %v-%v", gc.Oconv(int(op), 0), t) + + case gc.OADDR<<16 | gc.TPTR32: + a = x86.ALEAL + + case gc.OEQ<<16 | gc.TBOOL, + gc.OEQ<<16 | gc.TINT8, + gc.OEQ<<16 | gc.TUINT8, + gc.OEQ<<16 | gc.TINT16, + gc.OEQ<<16 | gc.TUINT16, + gc.OEQ<<16 | gc.TINT32, + gc.OEQ<<16 | gc.TUINT32, + gc.OEQ<<16 | gc.TINT64, + gc.OEQ<<16 | gc.TUINT64, + gc.OEQ<<16 | gc.TPTR32, + gc.OEQ<<16 | gc.TPTR64, + gc.OEQ<<16 | gc.TFLOAT32, + gc.OEQ<<16 | gc.TFLOAT64: + a = x86.AJEQ + + case gc.ONE<<16 | gc.TBOOL, + gc.ONE<<16 | gc.TINT8, + gc.ONE<<16 | gc.TUINT8, + gc.ONE<<16 | gc.TINT16, + gc.ONE<<16 | gc.TUINT16, + gc.ONE<<16 | gc.TINT32, + gc.ONE<<16 | gc.TUINT32, + gc.ONE<<16 | gc.TINT64, + gc.ONE<<16 | gc.TUINT64, + gc.ONE<<16 | gc.TPTR32, + gc.ONE<<16 | gc.TPTR64, + gc.ONE<<16 | gc.TFLOAT32, + gc.ONE<<16 | gc.TFLOAT64: + a = x86.AJNE + + case gc.OLT<<16 | gc.TINT8, + gc.OLT<<16 | gc.TINT16, + gc.OLT<<16 | gc.TINT32, + gc.OLT<<16 | gc.TINT64: + a = x86.AJLT + + case gc.OLT<<16 | gc.TUINT8, + gc.OLT<<16 | gc.TUINT16, + gc.OLT<<16 | gc.TUINT32, + gc.OLT<<16 | gc.TUINT64: + a = x86.AJCS + + case gc.OLE<<16 | gc.TINT8, + gc.OLE<<16 | gc.TINT16, + gc.OLE<<16 | gc.TINT32, + gc.OLE<<16 | gc.TINT64: + a = x86.AJLE + + case gc.OLE<<16 | gc.TUINT8, + gc.OLE<<16 | gc.TUINT16, + gc.OLE<<16 | gc.TUINT32, + gc.OLE<<16 | gc.TUINT64: + a = x86.AJLS + + case gc.OGT<<16 | gc.TINT8, + gc.OGT<<16 | gc.TINT16, + gc.OGT<<16 | gc.TINT32, + gc.OGT<<16 | gc.TINT64: + a = x86.AJGT + + case gc.OGT<<16 | gc.TUINT8, + gc.OGT<<16 | gc.TUINT16, + gc.OGT<<16 | gc.TUINT32, + gc.OGT<<16 | gc.TUINT64, + gc.OLT<<16 | gc.TFLOAT32, + gc.OLT<<16 | gc.TFLOAT64: + a = x86.AJHI + + case gc.OGE<<16 | gc.TINT8, + gc.OGE<<16 | gc.TINT16, + gc.OGE<<16 | gc.TINT32, + gc.OGE<<16 | gc.TINT64: + a = x86.AJGE + + case gc.OGE<<16 | gc.TUINT8, + gc.OGE<<16 | gc.TUINT16, + gc.OGE<<16 | gc.TUINT32, + gc.OGE<<16 | gc.TUINT64, + gc.OLE<<16 | gc.TFLOAT32, + gc.OLE<<16 | gc.TFLOAT64: + a = x86.AJCC + + case gc.OCMP<<16 | gc.TBOOL, + gc.OCMP<<16 | gc.TINT8, + gc.OCMP<<16 | gc.TUINT8: + a = x86.ACMPB + + case gc.OCMP<<16 | gc.TINT16, + gc.OCMP<<16 | gc.TUINT16: + a = x86.ACMPW + + case gc.OCMP<<16 | gc.TINT32, + gc.OCMP<<16 | gc.TUINT32, + gc.OCMP<<16 | gc.TPTR32: + a = x86.ACMPL + + case gc.OAS<<16 | gc.TBOOL, + gc.OAS<<16 | gc.TINT8, + gc.OAS<<16 | gc.TUINT8: + a = x86.AMOVB + + case gc.OAS<<16 | gc.TINT16, + gc.OAS<<16 | gc.TUINT16: + a = x86.AMOVW + + case gc.OAS<<16 | gc.TINT32, + gc.OAS<<16 | gc.TUINT32, + gc.OAS<<16 | gc.TPTR32: + a = x86.AMOVL + + case gc.OAS<<16 | gc.TFLOAT32: + a = x86.AMOVSS + + case gc.OAS<<16 | gc.TFLOAT64: + a = x86.AMOVSD + + case gc.OADD<<16 | gc.TINT8, + gc.OADD<<16 | gc.TUINT8: + a = x86.AADDB + + case gc.OADD<<16 | gc.TINT16, + gc.OADD<<16 | gc.TUINT16: + a = x86.AADDW + + case gc.OADD<<16 | gc.TINT32, + gc.OADD<<16 | gc.TUINT32, + gc.OADD<<16 | gc.TPTR32: + a = x86.AADDL + + case gc.OSUB<<16 | gc.TINT8, + gc.OSUB<<16 | gc.TUINT8: + a = x86.ASUBB + + case gc.OSUB<<16 | gc.TINT16, + gc.OSUB<<16 | gc.TUINT16: + a = x86.ASUBW + + case gc.OSUB<<16 | gc.TINT32, + gc.OSUB<<16 | gc.TUINT32, + gc.OSUB<<16 | gc.TPTR32: + a = x86.ASUBL + + case gc.OINC<<16 | gc.TINT8, + gc.OINC<<16 | gc.TUINT8: + a = x86.AINCB + + case gc.OINC<<16 | gc.TINT16, + gc.OINC<<16 | gc.TUINT16: + a = x86.AINCW + + case gc.OINC<<16 | gc.TINT32, + gc.OINC<<16 | gc.TUINT32, + gc.OINC<<16 | gc.TPTR32: + a = x86.AINCL + + case gc.ODEC<<16 | gc.TINT8, + gc.ODEC<<16 | gc.TUINT8: + a = x86.ADECB + + case gc.ODEC<<16 | gc.TINT16, + gc.ODEC<<16 | gc.TUINT16: + a = x86.ADECW + + case gc.ODEC<<16 | gc.TINT32, + gc.ODEC<<16 | gc.TUINT32, + gc.ODEC<<16 | gc.TPTR32: + a = x86.ADECL + + case gc.OCOM<<16 | gc.TINT8, + gc.OCOM<<16 | gc.TUINT8: + a = x86.ANOTB + + case gc.OCOM<<16 | gc.TINT16, + gc.OCOM<<16 | gc.TUINT16: + a = x86.ANOTW + + case gc.OCOM<<16 | gc.TINT32, + gc.OCOM<<16 | gc.TUINT32, + gc.OCOM<<16 | gc.TPTR32: + a = x86.ANOTL + + case gc.OMINUS<<16 | gc.TINT8, + gc.OMINUS<<16 | gc.TUINT8: + a = x86.ANEGB + + case gc.OMINUS<<16 | gc.TINT16, + gc.OMINUS<<16 | gc.TUINT16: + a = x86.ANEGW + + case gc.OMINUS<<16 | gc.TINT32, + gc.OMINUS<<16 | gc.TUINT32, + gc.OMINUS<<16 | gc.TPTR32: + a = x86.ANEGL + + case gc.OAND<<16 | gc.TINT8, + gc.OAND<<16 | gc.TUINT8: + a = x86.AANDB + + case gc.OAND<<16 | gc.TINT16, + gc.OAND<<16 | gc.TUINT16: + a = x86.AANDW + + case gc.OAND<<16 | gc.TINT32, + gc.OAND<<16 | gc.TUINT32, + gc.OAND<<16 | gc.TPTR32: + a = x86.AANDL + + case gc.OOR<<16 | gc.TINT8, + gc.OOR<<16 | gc.TUINT8: + a = x86.AORB + + case gc.OOR<<16 | gc.TINT16, + gc.OOR<<16 | gc.TUINT16: + a = x86.AORW + + case gc.OOR<<16 | gc.TINT32, + gc.OOR<<16 | gc.TUINT32, + gc.OOR<<16 | gc.TPTR32: + a = x86.AORL + + case gc.OXOR<<16 | gc.TINT8, + gc.OXOR<<16 | gc.TUINT8: + a = x86.AXORB + + case gc.OXOR<<16 | gc.TINT16, + gc.OXOR<<16 | gc.TUINT16: + a = x86.AXORW + + case gc.OXOR<<16 | gc.TINT32, + gc.OXOR<<16 | gc.TUINT32, + gc.OXOR<<16 | gc.TPTR32: + a = x86.AXORL + + case gc.OLROT<<16 | gc.TINT8, + gc.OLROT<<16 | gc.TUINT8: + a = x86.AROLB + + case gc.OLROT<<16 | gc.TINT16, + gc.OLROT<<16 | gc.TUINT16: + a = x86.AROLW + + case gc.OLROT<<16 | gc.TINT32, + gc.OLROT<<16 | gc.TUINT32, + gc.OLROT<<16 | gc.TPTR32: + a = x86.AROLL + + case gc.OLSH<<16 | gc.TINT8, + gc.OLSH<<16 | gc.TUINT8: + a = x86.ASHLB + + case gc.OLSH<<16 | gc.TINT16, + gc.OLSH<<16 | gc.TUINT16: + a = x86.ASHLW + + case gc.OLSH<<16 | gc.TINT32, + gc.OLSH<<16 | gc.TUINT32, + gc.OLSH<<16 | gc.TPTR32: + a = x86.ASHLL + + case gc.ORSH<<16 | gc.TUINT8: + a = x86.ASHRB + + case gc.ORSH<<16 | gc.TUINT16: + a = x86.ASHRW + + case gc.ORSH<<16 | gc.TUINT32, + gc.ORSH<<16 | gc.TPTR32: + a = x86.ASHRL + + case gc.ORSH<<16 | gc.TINT8: + a = x86.ASARB + + case gc.ORSH<<16 | gc.TINT16: + a = x86.ASARW + + case gc.ORSH<<16 | gc.TINT32: + a = x86.ASARL + + case gc.OHMUL<<16 | gc.TINT8, + gc.OMUL<<16 | gc.TINT8, + gc.OMUL<<16 | gc.TUINT8: + a = x86.AIMULB + + case gc.OHMUL<<16 | gc.TINT16, + gc.OMUL<<16 | gc.TINT16, + gc.OMUL<<16 | gc.TUINT16: + a = x86.AIMULW + + case gc.OHMUL<<16 | gc.TINT32, + gc.OMUL<<16 | gc.TINT32, + gc.OMUL<<16 | gc.TUINT32, + gc.OMUL<<16 | gc.TPTR32: + a = x86.AIMULL + + case gc.OHMUL<<16 | gc.TUINT8: + a = x86.AMULB + + case gc.OHMUL<<16 | gc.TUINT16: + a = x86.AMULW + + case gc.OHMUL<<16 | gc.TUINT32, + gc.OHMUL<<16 | gc.TPTR32: + a = x86.AMULL + + case gc.ODIV<<16 | gc.TINT8, + gc.OMOD<<16 | gc.TINT8: + a = x86.AIDIVB + + case gc.ODIV<<16 | gc.TUINT8, + gc.OMOD<<16 | gc.TUINT8: + a = x86.ADIVB + + case gc.ODIV<<16 | gc.TINT16, + gc.OMOD<<16 | gc.TINT16: + a = x86.AIDIVW + + case gc.ODIV<<16 | gc.TUINT16, + gc.OMOD<<16 | gc.TUINT16: + a = x86.ADIVW + + case gc.ODIV<<16 | gc.TINT32, + gc.OMOD<<16 | gc.TINT32: + a = x86.AIDIVL + + case gc.ODIV<<16 | gc.TUINT32, + gc.ODIV<<16 | gc.TPTR32, + gc.OMOD<<16 | gc.TUINT32, + gc.OMOD<<16 | gc.TPTR32: + a = x86.ADIVL + + case gc.OEXTEND<<16 | gc.TINT16: + a = x86.ACWD + + case gc.OEXTEND<<16 | gc.TINT32: + a = x86.ACDQ + } + + return a +} + +func foptoas(op int, t *gc.Type, flg int) int { + a := obj.AXXX + et := int(gc.Simtype[t.Etype]) + + if !gc.Thearch.Use387 { + switch uint32(op)<<16 | uint32(et) { + default: + gc.Fatal("foptoas-sse: no entry %v-%v", gc.Oconv(int(op), 0), t) + + case gc.OCMP<<16 | gc.TFLOAT32: + a = x86.AUCOMISS + + case gc.OCMP<<16 | gc.TFLOAT64: + a = x86.AUCOMISD + + case gc.OAS<<16 | gc.TFLOAT32: + a = x86.AMOVSS + + case gc.OAS<<16 | gc.TFLOAT64: + a = x86.AMOVSD + + case gc.OADD<<16 | gc.TFLOAT32: + a = x86.AADDSS + + case gc.OADD<<16 | gc.TFLOAT64: + a = x86.AADDSD + + case gc.OSUB<<16 | gc.TFLOAT32: + a = x86.ASUBSS + + case gc.OSUB<<16 | gc.TFLOAT64: + a = x86.ASUBSD + + case gc.OMUL<<16 | gc.TFLOAT32: + a = x86.AMULSS + + case gc.OMUL<<16 | gc.TFLOAT64: + a = x86.AMULSD + + case gc.ODIV<<16 | gc.TFLOAT32: + a = x86.ADIVSS + + case gc.ODIV<<16 | gc.TFLOAT64: + a = x86.ADIVSD + } + + return a + } + + // If we need Fpop, it means we're working on + // two different floating-point registers, not memory. + // There the instruction only has a float64 form. + if flg&Fpop != 0 { + et = gc.TFLOAT64 + } + + // clear Frev if unneeded + switch op { + case gc.OADD, + gc.OMUL: + flg &^= Frev + } + + switch uint32(op)<<16 | (uint32(et)<<8 | uint32(flg)) { + case gc.OADD<<16 | (gc.TFLOAT32<<8 | 0): + return x86.AFADDF + + case gc.OADD<<16 | (gc.TFLOAT64<<8 | 0): + return x86.AFADDD + + case gc.OADD<<16 | (gc.TFLOAT64<<8 | Fpop): + return x86.AFADDDP + + case gc.OSUB<<16 | (gc.TFLOAT32<<8 | 0): + return x86.AFSUBF + + case gc.OSUB<<16 | (gc.TFLOAT32<<8 | Frev): + return x86.AFSUBRF + + case gc.OSUB<<16 | (gc.TFLOAT64<<8 | 0): + return x86.AFSUBD + + case gc.OSUB<<16 | (gc.TFLOAT64<<8 | Frev): + return x86.AFSUBRD + + case gc.OSUB<<16 | (gc.TFLOAT64<<8 | Fpop): + return x86.AFSUBDP + + case gc.OSUB<<16 | (gc.TFLOAT64<<8 | (Fpop | Frev)): + return x86.AFSUBRDP + + case gc.OMUL<<16 | (gc.TFLOAT32<<8 | 0): + return x86.AFMULF + + case gc.OMUL<<16 | (gc.TFLOAT64<<8 | 0): + return x86.AFMULD + + case gc.OMUL<<16 | (gc.TFLOAT64<<8 | Fpop): + return x86.AFMULDP + + case gc.ODIV<<16 | (gc.TFLOAT32<<8 | 0): + return x86.AFDIVF + + case gc.ODIV<<16 | (gc.TFLOAT32<<8 | Frev): + return x86.AFDIVRF + + case gc.ODIV<<16 | (gc.TFLOAT64<<8 | 0): + return x86.AFDIVD + + case gc.ODIV<<16 | (gc.TFLOAT64<<8 | Frev): + return x86.AFDIVRD + + case gc.ODIV<<16 | (gc.TFLOAT64<<8 | Fpop): + return x86.AFDIVDP + + case gc.ODIV<<16 | (gc.TFLOAT64<<8 | (Fpop | Frev)): + return x86.AFDIVRDP + + case gc.OCMP<<16 | (gc.TFLOAT32<<8 | 0): + return x86.AFCOMF + + case gc.OCMP<<16 | (gc.TFLOAT32<<8 | Fpop): + return x86.AFCOMFP + + case gc.OCMP<<16 | (gc.TFLOAT64<<8 | 0): + return x86.AFCOMD + + case gc.OCMP<<16 | (gc.TFLOAT64<<8 | Fpop): + return x86.AFCOMDP + + case gc.OCMP<<16 | (gc.TFLOAT64<<8 | Fpop2): + return x86.AFCOMDPP + + case gc.OMINUS<<16 | (gc.TFLOAT32<<8 | 0): + return x86.AFCHS + + case gc.OMINUS<<16 | (gc.TFLOAT64<<8 | 0): + return x86.AFCHS + } + + gc.Fatal("foptoas %v %v %#x", gc.Oconv(int(op), 0), t, flg) + return 0 +} + +var resvd = []int{ + // REG_DI, // for movstring + // REG_SI, // for movstring + + x86.REG_AX, // for divide + x86.REG_CX, // for shift + x86.REG_DX, // for divide + x86.REG_SP, // for stack +} + +/* + * generate + * as $c, reg + */ +func gconreg(as int, c int64, reg int) { + var n1 gc.Node + var n2 gc.Node + + gc.Nodconst(&n1, gc.Types[gc.TINT64], c) + gc.Nodreg(&n2, gc.Types[gc.TINT64], reg) + gins(as, &n1, &n2) +} + +/* + * generate + * as $c, n + */ +func ginscon(as int, c int64, n2 *gc.Node) { + var n1 gc.Node + gc.Nodconst(&n1, gc.Types[gc.TINT32], c) + 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 + */ +func nswap(a *gc.Node, b *gc.Node) { + t := *a + *a = *b + *b = t +} + +/* + * return constant i node. + * overwritten by next call, but useful in calls to gins. + */ + +var ncon_n gc.Node + +func ncon(i uint32) *gc.Node { + if ncon_n.Type == nil { + gc.Nodconst(&ncon_n, gc.Types[gc.TUINT32], 0) + } + ncon_n.SetInt(int64(i)) + return &ncon_n +} + +var sclean [10]gc.Node + +var nsclean int + +/* + * n is a 64-bit value. fill in lo and hi to refer to its 32-bit halves. + */ +func split64(n *gc.Node, lo *gc.Node, hi *gc.Node) { + if !gc.Is64(n.Type) { + gc.Fatal("split64 %v", n.Type) + } + + if nsclean >= len(sclean) { + gc.Fatal("split64 clean") + } + sclean[nsclean].Op = gc.OEMPTY + nsclean++ + switch n.Op { + default: + switch n.Op { + default: + var n1 gc.Node + if !dotaddable(n, &n1) { + gc.Igen(n, &n1, nil) + sclean[nsclean-1] = n1 + } + + n = &n1 + + case gc.ONAME: + if n.Class == gc.PPARAMREF { + var n1 gc.Node + gc.Cgen(n.Name.Heapaddr, &n1) + sclean[nsclean-1] = n1 + n = &n1 + } + + // nothing + case gc.OINDREG: + break + } + + *lo = *n + *hi = *n + lo.Type = gc.Types[gc.TUINT32] + if n.Type.Etype == gc.TINT64 { + hi.Type = gc.Types[gc.TINT32] + } else { + hi.Type = gc.Types[gc.TUINT32] + } + hi.Xoffset += 4 + + case gc.OLITERAL: + var n1 gc.Node + n.Convconst(&n1, n.Type) + i := n1.Int() + gc.Nodconst(lo, gc.Types[gc.TUINT32], int64(uint32(i))) + i >>= 32 + if n.Type.Etype == gc.TINT64 { + gc.Nodconst(hi, gc.Types[gc.TINT32], int64(int32(i))) + } else { + gc.Nodconst(hi, gc.Types[gc.TUINT32], int64(uint32(i))) + } + } +} + +func splitclean() { + if nsclean <= 0 { + gc.Fatal("splitclean") + } + nsclean-- + if sclean[nsclean].Op != gc.OEMPTY { + gc.Regfree(&sclean[nsclean]) + } +} + +// set up nodes representing fp constants +var ( + zerof gc.Node + two63f gc.Node + two64f gc.Node + bignodes_did bool +) + +func bignodes() { + if bignodes_did { + return + } + bignodes_did = true + + gc.Nodconst(&zerof, gc.Types[gc.TINT64], 0) + zerof.Convconst(&zerof, gc.Types[gc.TFLOAT64]) + + var i big.Int + i.SetInt64(1) + i.Lsh(&i, 63) + var bigi gc.Node + + gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0) + bigi.SetBigInt(&i) + bigi.Convconst(&two63f, gc.Types[gc.TFLOAT64]) + + gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0) + i.Lsh(&i, 1) + bigi.SetBigInt(&i) + bigi.Convconst(&two64f, gc.Types[gc.TFLOAT64]) +} + +func memname(n *gc.Node, t *gc.Type) { + gc.Tempname(n, t) + n.Sym = gc.Lookup("." + n.Sym.Name[1:]) // keep optimizer from registerizing + n.Orig.Sym = n.Sym +} + +func gmove(f *gc.Node, t *gc.Node) { + if gc.Debug['M'] != 0 { + fmt.Printf("gmove %v -> %v\n", f, t) + } + + ft := gc.Simsimtype(f.Type) + tt := gc.Simsimtype(t.Type) + cvt := t.Type + + if gc.Iscomplex[ft] || gc.Iscomplex[tt] { + gc.Complexmove(f, t) + return + } + + if gc.Isfloat[ft] || gc.Isfloat[tt] { + floatmove(f, t) + return + } + + // cannot have two integer memory operands; + // except 64-bit, which always copies via registers anyway. + var r1 gc.Node + var a int + if gc.Isint[ft] && gc.Isint[tt] && !gc.Is64(f.Type) && !gc.Is64(t.Type) && gc.Ismem(f) && gc.Ismem(t) { + goto hard + } + + // convert constant to desired type + if f.Op == gc.OLITERAL { + var con gc.Node + f.Convconst(&con, t.Type) + f = &con + ft = gc.Simsimtype(con.Type) + } + + // value -> value copy, only one memory operand. + // figure out the instruction to use. + // break out of switch for one-instruction gins. + // goto rdst for "destination must be register". + // goto hard for "convert to cvt type first". + // otherwise handle and return. + + switch uint32(ft)<<16 | uint32(tt) { + default: + // should not happen + gc.Fatal("gmove %v -> %v", f, t) + return + + /* + * integer copy and truncate + */ + case gc.TINT8<<16 | gc.TINT8, // same size + gc.TINT8<<16 | gc.TUINT8, + gc.TUINT8<<16 | gc.TINT8, + gc.TUINT8<<16 | gc.TUINT8: + a = x86.AMOVB + + case gc.TINT16<<16 | gc.TINT8, // truncate + gc.TUINT16<<16 | gc.TINT8, + gc.TINT32<<16 | gc.TINT8, + gc.TUINT32<<16 | gc.TINT8, + gc.TINT16<<16 | gc.TUINT8, + gc.TUINT16<<16 | gc.TUINT8, + gc.TINT32<<16 | gc.TUINT8, + gc.TUINT32<<16 | gc.TUINT8: + a = x86.AMOVB + + goto rsrc + + case gc.TINT64<<16 | gc.TINT8, // truncate low word + gc.TUINT64<<16 | gc.TINT8, + gc.TINT64<<16 | gc.TUINT8, + gc.TUINT64<<16 | gc.TUINT8: + var flo gc.Node + var fhi gc.Node + split64(f, &flo, &fhi) + + var r1 gc.Node + gc.Nodreg(&r1, t.Type, x86.REG_AX) + gmove(&flo, &r1) + gins(x86.AMOVB, &r1, t) + splitclean() + return + + case gc.TINT16<<16 | gc.TINT16, // same size + gc.TINT16<<16 | gc.TUINT16, + gc.TUINT16<<16 | gc.TINT16, + gc.TUINT16<<16 | gc.TUINT16: + a = x86.AMOVW + + case gc.TINT32<<16 | gc.TINT16, // truncate + gc.TUINT32<<16 | gc.TINT16, + gc.TINT32<<16 | gc.TUINT16, + gc.TUINT32<<16 | gc.TUINT16: + a = x86.AMOVW + + goto rsrc + + case gc.TINT64<<16 | gc.TINT16, // truncate low word + gc.TUINT64<<16 | gc.TINT16, + gc.TINT64<<16 | gc.TUINT16, + gc.TUINT64<<16 | gc.TUINT16: + var flo gc.Node + var fhi gc.Node + split64(f, &flo, &fhi) + + var r1 gc.Node + gc.Nodreg(&r1, t.Type, x86.REG_AX) + gmove(&flo, &r1) + gins(x86.AMOVW, &r1, t) + splitclean() + return + + case gc.TINT32<<16 | gc.TINT32, // same size + gc.TINT32<<16 | gc.TUINT32, + gc.TUINT32<<16 | gc.TINT32, + gc.TUINT32<<16 | gc.TUINT32: + a = x86.AMOVL + + case gc.TINT64<<16 | gc.TINT32, // truncate + gc.TUINT64<<16 | gc.TINT32, + gc.TINT64<<16 | gc.TUINT32, + gc.TUINT64<<16 | gc.TUINT32: + var fhi gc.Node + var flo gc.Node + split64(f, &flo, &fhi) + + var r1 gc.Node + gc.Nodreg(&r1, t.Type, x86.REG_AX) + gmove(&flo, &r1) + gins(x86.AMOVL, &r1, t) + splitclean() + return + + case gc.TINT64<<16 | gc.TINT64, // same size + gc.TINT64<<16 | gc.TUINT64, + gc.TUINT64<<16 | gc.TINT64, + gc.TUINT64<<16 | gc.TUINT64: + var fhi gc.Node + var flo gc.Node + split64(f, &flo, &fhi) + + var tlo gc.Node + var thi gc.Node + split64(t, &tlo, &thi) + if f.Op == gc.OLITERAL { + gins(x86.AMOVL, &flo, &tlo) + gins(x86.AMOVL, &fhi, &thi) + } else { + var r1 gc.Node + gc.Nodreg(&r1, gc.Types[gc.TUINT32], x86.REG_AX) + var r2 gc.Node + gc.Nodreg(&r2, gc.Types[gc.TUINT32], x86.REG_DX) + gins(x86.AMOVL, &flo, &r1) + gins(x86.AMOVL, &fhi, &r2) + gins(x86.AMOVL, &r1, &tlo) + gins(x86.AMOVL, &r2, &thi) + } + + splitclean() + splitclean() + return + + /* + * integer up-conversions + */ + case gc.TINT8<<16 | gc.TINT16, // sign extend int8 + gc.TINT8<<16 | gc.TUINT16: + a = x86.AMOVBWSX + + goto rdst + + case gc.TINT8<<16 | gc.TINT32, + gc.TINT8<<16 | gc.TUINT32: + a = x86.AMOVBLSX + goto rdst + + case gc.TINT8<<16 | gc.TINT64, // convert via int32 + gc.TINT8<<16 | gc.TUINT64: + cvt = gc.Types[gc.TINT32] + + goto hard + + case gc.TUINT8<<16 | gc.TINT16, // zero extend uint8 + gc.TUINT8<<16 | gc.TUINT16: + a = x86.AMOVBWZX + + goto rdst + + case gc.TUINT8<<16 | gc.TINT32, + gc.TUINT8<<16 | gc.TUINT32: + a = x86.AMOVBLZX + goto rdst + + case gc.TUINT8<<16 | gc.TINT64, // convert via uint32 + gc.TUINT8<<16 | gc.TUINT64: + cvt = gc.Types[gc.TUINT32] + + goto hard + + case gc.TINT16<<16 | gc.TINT32, // sign extend int16 + gc.TINT16<<16 | gc.TUINT32: + a = x86.AMOVWLSX + + goto rdst + + case gc.TINT16<<16 | gc.TINT64, // convert via int32 + gc.TINT16<<16 | gc.TUINT64: + cvt = gc.Types[gc.TINT32] + + goto hard + + case gc.TUINT16<<16 | gc.TINT32, // zero extend uint16 + gc.TUINT16<<16 | gc.TUINT32: + a = x86.AMOVWLZX + + goto rdst + + case gc.TUINT16<<16 | gc.TINT64, // convert via uint32 + gc.TUINT16<<16 | gc.TUINT64: + cvt = gc.Types[gc.TUINT32] + + goto hard + + case gc.TINT32<<16 | gc.TINT64, // sign extend int32 + gc.TINT32<<16 | gc.TUINT64: + var thi gc.Node + var tlo gc.Node + split64(t, &tlo, &thi) + + var flo gc.Node + gc.Nodreg(&flo, tlo.Type, x86.REG_AX) + var fhi gc.Node + gc.Nodreg(&fhi, thi.Type, x86.REG_DX) + gmove(f, &flo) + gins(x86.ACDQ, nil, nil) + gins(x86.AMOVL, &flo, &tlo) + gins(x86.AMOVL, &fhi, &thi) + splitclean() + return + + case gc.TUINT32<<16 | gc.TINT64, // zero extend uint32 + gc.TUINT32<<16 | gc.TUINT64: + var tlo gc.Node + var thi gc.Node + split64(t, &tlo, &thi) + + gmove(f, &tlo) + gins(x86.AMOVL, ncon(0), &thi) + splitclean() + return + } + + gins(a, f, t) + return + + // requires register source +rsrc: + gc.Regalloc(&r1, f.Type, t) + + gmove(f, &r1) + gins(a, &r1, t) + gc.Regfree(&r1) + return + + // requires register destination +rdst: + { + gc.Regalloc(&r1, t.Type, t) + + gins(a, f, &r1) + gmove(&r1, t) + gc.Regfree(&r1) + return + } + + // requires register intermediate +hard: + gc.Regalloc(&r1, cvt, t) + + gmove(f, &r1) + gmove(&r1, t) + gc.Regfree(&r1) + return +} + +func floatmove(f *gc.Node, t *gc.Node) { + var r1 gc.Node + + ft := gc.Simsimtype(f.Type) + tt := gc.Simsimtype(t.Type) + cvt := t.Type + + // cannot have two floating point memory operands. + if gc.Isfloat[ft] && gc.Isfloat[tt] && gc.Ismem(f) && gc.Ismem(t) { + goto hard + } + + // convert constant to desired type + if f.Op == gc.OLITERAL { + var con gc.Node + f.Convconst(&con, t.Type) + f = &con + ft = gc.Simsimtype(con.Type) + + // some constants can't move directly to memory. + if gc.Ismem(t) { + // float constants come from memory. + if gc.Isfloat[tt] { + goto hard + } + } + } + + // value -> value copy, only one memory operand. + // figure out the instruction to use. + // break out of switch for one-instruction gins. + // goto rdst for "destination must be register". + // goto hard for "convert to cvt type first". + // otherwise handle and return. + + switch uint32(ft)<<16 | uint32(tt) { + default: + if gc.Thearch.Use387 { + floatmove_387(f, t) + } else { + floatmove_sse(f, t) + } + return + + // float to very long integer. + case gc.TFLOAT32<<16 | gc.TINT64, + gc.TFLOAT64<<16 | gc.TINT64: + if f.Op == gc.OREGISTER { + cvt = f.Type + goto hardmem + } + + var r1 gc.Node + gc.Nodreg(&r1, gc.Types[ft], x86.REG_F0) + if ft == gc.TFLOAT32 { + gins(x86.AFMOVF, f, &r1) + } else { + gins(x86.AFMOVD, f, &r1) + } + + // set round to zero mode during conversion + var t1 gc.Node + memname(&t1, gc.Types[gc.TUINT16]) + + var t2 gc.Node + memname(&t2, gc.Types[gc.TUINT16]) + gins(x86.AFSTCW, nil, &t1) + gins(x86.AMOVW, ncon(0xf7f), &t2) + gins(x86.AFLDCW, &t2, nil) + if tt == gc.TINT16 { + gins(x86.AFMOVWP, &r1, t) + } else if tt == gc.TINT32 { + gins(x86.AFMOVLP, &r1, t) + } else { + gins(x86.AFMOVVP, &r1, t) + } + gins(x86.AFLDCW, &t1, nil) + return + + case gc.TFLOAT32<<16 | gc.TUINT64, + gc.TFLOAT64<<16 | gc.TUINT64: + if !gc.Ismem(f) { + cvt = f.Type + goto hardmem + } + + bignodes() + var f0 gc.Node + gc.Nodreg(&f0, gc.Types[ft], x86.REG_F0) + var f1 gc.Node + gc.Nodreg(&f1, gc.Types[ft], x86.REG_F0+1) + var ax gc.Node + gc.Nodreg(&ax, gc.Types[gc.TUINT16], x86.REG_AX) + + if ft == gc.TFLOAT32 { + gins(x86.AFMOVF, f, &f0) + } else { + gins(x86.AFMOVD, f, &f0) + } + + // if 0 > v { answer = 0 } + gins(x86.AFMOVD, &zerof, &f0) + + gins(x86.AFUCOMIP, &f0, &f1) + p1 := gc.Gbranch(optoas(gc.OGT, gc.Types[tt]), nil, 0) + + // if 1<<64 <= v { answer = 0 too } + gins(x86.AFMOVD, &two64f, &f0) + + gins(x86.AFUCOMIP, &f0, &f1) + p2 := gc.Gbranch(optoas(gc.OGT, gc.Types[tt]), nil, 0) + gc.Patch(p1, gc.Pc) + gins(x86.AFMOVVP, &f0, t) // don't care about t, but will pop the stack + var thi gc.Node + var tlo gc.Node + split64(t, &tlo, &thi) + gins(x86.AMOVL, ncon(0), &tlo) + gins(x86.AMOVL, ncon(0), &thi) + splitclean() + p1 = gc.Gbranch(obj.AJMP, nil, 0) + gc.Patch(p2, gc.Pc) + + // in range; algorithm is: + // if small enough, use native float64 -> int64 conversion. + // otherwise, subtract 2^63, convert, and add it back. + + // set round to zero mode during conversion + var t1 gc.Node + memname(&t1, gc.Types[gc.TUINT16]) + + var t2 gc.Node + memname(&t2, gc.Types[gc.TUINT16]) + gins(x86.AFSTCW, nil, &t1) + gins(x86.AMOVW, ncon(0xf7f), &t2) + gins(x86.AFLDCW, &t2, nil) + + // actual work + gins(x86.AFMOVD, &two63f, &f0) + + gins(x86.AFUCOMIP, &f0, &f1) + p2 = gc.Gbranch(optoas(gc.OLE, gc.Types[tt]), nil, 0) + gins(x86.AFMOVVP, &f0, t) + p3 := gc.Gbranch(obj.AJMP, nil, 0) + gc.Patch(p2, gc.Pc) + gins(x86.AFMOVD, &two63f, &f0) + gins(x86.AFSUBDP, &f0, &f1) + gins(x86.AFMOVVP, &f0, t) + split64(t, &tlo, &thi) + gins(x86.AXORL, ncon(0x80000000), &thi) // + 2^63 + gc.Patch(p3, gc.Pc) + splitclean() + + // restore rounding mode + gins(x86.AFLDCW, &t1, nil) + + gc.Patch(p1, gc.Pc) + return + + /* + * integer to float + */ + case gc.TINT64<<16 | gc.TFLOAT32, + gc.TINT64<<16 | gc.TFLOAT64: + if t.Op == gc.OREGISTER { + goto hardmem + } + var f0 gc.Node + gc.Nodreg(&f0, t.Type, x86.REG_F0) + gins(x86.AFMOVV, f, &f0) + if tt == gc.TFLOAT32 { + gins(x86.AFMOVFP, &f0, t) + } else { + gins(x86.AFMOVDP, &f0, t) + } + return + + // algorithm is: + // if small enough, use native int64 -> float64 conversion. + // otherwise, halve (rounding to odd?), convert, and double. + case gc.TUINT64<<16 | gc.TFLOAT32, + gc.TUINT64<<16 | gc.TFLOAT64: + var ax gc.Node + gc.Nodreg(&ax, gc.Types[gc.TUINT32], x86.REG_AX) + + var dx gc.Node + gc.Nodreg(&dx, gc.Types[gc.TUINT32], x86.REG_DX) + var cx gc.Node + gc.Nodreg(&cx, gc.Types[gc.TUINT32], x86.REG_CX) + var t1 gc.Node + gc.Tempname(&t1, f.Type) + var tlo gc.Node + var thi gc.Node + split64(&t1, &tlo, &thi) + gmove(f, &t1) + gins(x86.ACMPL, &thi, ncon(0)) + p1 := gc.Gbranch(x86.AJLT, nil, 0) + + // native + var r1 gc.Node + gc.Nodreg(&r1, gc.Types[tt], x86.REG_F0) + + gins(x86.AFMOVV, &t1, &r1) + if tt == gc.TFLOAT32 { + gins(x86.AFMOVFP, &r1, t) + } else { + gins(x86.AFMOVDP, &r1, t) + } + p2 := gc.Gbranch(obj.AJMP, nil, 0) + + // simulated + gc.Patch(p1, gc.Pc) + + gmove(&tlo, &ax) + gmove(&thi, &dx) + p1 = gins(x86.ASHRL, ncon(1), &ax) + p1.From.Index = x86.REG_DX // double-width shift DX -> AX + p1.From.Scale = 0 + gins(x86.AMOVL, ncon(0), &cx) + gins(x86.ASETCC, nil, &cx) + gins(x86.AORL, &cx, &ax) + gins(x86.ASHRL, ncon(1), &dx) + gmove(&dx, &thi) + gmove(&ax, &tlo) + gc.Nodreg(&r1, gc.Types[tt], x86.REG_F0) + var r2 gc.Node + gc.Nodreg(&r2, gc.Types[tt], x86.REG_F0+1) + gins(x86.AFMOVV, &t1, &r1) + gins(x86.AFMOVD, &r1, &r1) + gins(x86.AFADDDP, &r1, &r2) + if tt == gc.TFLOAT32 { + gins(x86.AFMOVFP, &r1, t) + } else { + gins(x86.AFMOVDP, &r1, t) + } + gc.Patch(p2, gc.Pc) + splitclean() + return + } + + // requires register intermediate +hard: + gc.Regalloc(&r1, cvt, t) + + gmove(f, &r1) + gmove(&r1, t) + gc.Regfree(&r1) + return + + // requires memory intermediate +hardmem: + gc.Tempname(&r1, cvt) + + gmove(f, &r1) + gmove(&r1, t) + return +} + +func floatmove_387(f *gc.Node, t *gc.Node) { + var r1 gc.Node + var a int + + ft := gc.Simsimtype(f.Type) + tt := gc.Simsimtype(t.Type) + cvt := t.Type + + switch uint32(ft)<<16 | uint32(tt) { + default: + goto fatal + + /* + * float to integer + */ + case gc.TFLOAT32<<16 | gc.TINT16, + gc.TFLOAT32<<16 | gc.TINT32, + gc.TFLOAT32<<16 | gc.TINT64, + gc.TFLOAT64<<16 | gc.TINT16, + gc.TFLOAT64<<16 | gc.TINT32, + gc.TFLOAT64<<16 | gc.TINT64: + if t.Op == gc.OREGISTER { + goto hardmem + } + var r1 gc.Node + gc.Nodreg(&r1, gc.Types[ft], x86.REG_F0) + if f.Op != gc.OREGISTER { + if ft == gc.TFLOAT32 { + gins(x86.AFMOVF, f, &r1) + } else { + gins(x86.AFMOVD, f, &r1) + } + } + + // set round to zero mode during conversion + var t1 gc.Node + memname(&t1, gc.Types[gc.TUINT16]) + + var t2 gc.Node + memname(&t2, gc.Types[gc.TUINT16]) + gins(x86.AFSTCW, nil, &t1) + gins(x86.AMOVW, ncon(0xf7f), &t2) + gins(x86.AFLDCW, &t2, nil) + if tt == gc.TINT16 { + gins(x86.AFMOVWP, &r1, t) + } else if tt == gc.TINT32 { + gins(x86.AFMOVLP, &r1, t) + } else { + gins(x86.AFMOVVP, &r1, t) + } + gins(x86.AFLDCW, &t1, nil) + return + + // convert via int32. + case gc.TFLOAT32<<16 | gc.TINT8, + gc.TFLOAT32<<16 | gc.TUINT16, + gc.TFLOAT32<<16 | gc.TUINT8, + gc.TFLOAT64<<16 | gc.TINT8, + gc.TFLOAT64<<16 | gc.TUINT16, + gc.TFLOAT64<<16 | gc.TUINT8: + var t1 gc.Node + gc.Tempname(&t1, gc.Types[gc.TINT32]) + + gmove(f, &t1) + switch tt { + default: + gc.Fatal("gmove %v", t) + + case gc.TINT8: + gins(x86.ACMPL, &t1, ncon(-0x80&(1<<32-1))) + p1 := gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TINT32]), nil, -1) + gins(x86.ACMPL, &t1, ncon(0x7f)) + p2 := gc.Gbranch(optoas(gc.OGT, gc.Types[gc.TINT32]), nil, -1) + p3 := gc.Gbranch(obj.AJMP, nil, 0) + gc.Patch(p1, gc.Pc) + gc.Patch(p2, gc.Pc) + gmove(ncon(-0x80&(1<<32-1)), &t1) + gc.Patch(p3, gc.Pc) + gmove(&t1, t) + + case gc.TUINT8: + gins(x86.ATESTL, ncon(0xffffff00), &t1) + p1 := gc.Gbranch(x86.AJEQ, nil, +1) + gins(x86.AMOVL, ncon(0), &t1) + gc.Patch(p1, gc.Pc) + gmove(&t1, t) + + case gc.TUINT16: + gins(x86.ATESTL, ncon(0xffff0000), &t1) + p1 := gc.Gbranch(x86.AJEQ, nil, +1) + gins(x86.AMOVL, ncon(0), &t1) + gc.Patch(p1, gc.Pc) + gmove(&t1, t) + } + + return + + // convert via int64. + case gc.TFLOAT32<<16 | gc.TUINT32, + gc.TFLOAT64<<16 | gc.TUINT32: + cvt = gc.Types[gc.TINT64] + + goto hardmem + + /* + * integer to float + */ + case gc.TINT16<<16 | gc.TFLOAT32, + gc.TINT16<<16 | gc.TFLOAT64, + gc.TINT32<<16 | gc.TFLOAT32, + gc.TINT32<<16 | gc.TFLOAT64, + gc.TINT64<<16 | gc.TFLOAT32, + gc.TINT64<<16 | gc.TFLOAT64: + if t.Op != gc.OREGISTER { + goto hard + } + if f.Op == gc.OREGISTER { + cvt = f.Type + goto hardmem + } + + switch ft { + case gc.TINT16: + a = x86.AFMOVW + + case gc.TINT32: + a = x86.AFMOVL + + default: + a = x86.AFMOVV + } + + // convert via int32 memory + case gc.TINT8<<16 | gc.TFLOAT32, + gc.TINT8<<16 | gc.TFLOAT64, + gc.TUINT16<<16 | gc.TFLOAT32, + gc.TUINT16<<16 | gc.TFLOAT64, + gc.TUINT8<<16 | gc.TFLOAT32, + gc.TUINT8<<16 | gc.TFLOAT64: + cvt = gc.Types[gc.TINT32] + + goto hardmem + + // convert via int64 memory + case gc.TUINT32<<16 | gc.TFLOAT32, + gc.TUINT32<<16 | gc.TFLOAT64: + cvt = gc.Types[gc.TINT64] + + goto hardmem + + // The way the code generator uses floating-point + // registers, a move from F0 to F0 is intended as a no-op. + // On the x86, it's not: it pushes a second copy of F0 + // on the floating point stack. So toss it away here. + // Also, F0 is the *only* register we ever evaluate + // into, so we should only see register/register as F0/F0. + /* + * float to float + */ + case gc.TFLOAT32<<16 | gc.TFLOAT32, + gc.TFLOAT64<<16 | gc.TFLOAT64: + if gc.Ismem(f) && gc.Ismem(t) { + goto hard + } + if f.Op == gc.OREGISTER && t.Op == gc.OREGISTER { + if f.Reg != x86.REG_F0 || t.Reg != x86.REG_F0 { + goto fatal + } + return + } + + a = x86.AFMOVF + if ft == gc.TFLOAT64 { + a = x86.AFMOVD + } + if gc.Ismem(t) { + if f.Op != gc.OREGISTER || f.Reg != x86.REG_F0 { + gc.Fatal("gmove %v", f) + } + a = x86.AFMOVFP + if ft == gc.TFLOAT64 { + a = x86.AFMOVDP + } + } + + case gc.TFLOAT32<<16 | gc.TFLOAT64: + if gc.Ismem(f) && gc.Ismem(t) { + goto hard + } + if f.Op == gc.OREGISTER && t.Op == gc.OREGISTER { + if f.Reg != x86.REG_F0 || t.Reg != x86.REG_F0 { + goto fatal + } + return + } + + if f.Op == gc.OREGISTER { + gins(x86.AFMOVDP, f, t) + } else { + gins(x86.AFMOVF, f, t) + } + return + + case gc.TFLOAT64<<16 | gc.TFLOAT32: + if gc.Ismem(f) && gc.Ismem(t) { + goto hard + } + if f.Op == gc.OREGISTER && t.Op == gc.OREGISTER { + var r1 gc.Node + gc.Tempname(&r1, gc.Types[gc.TFLOAT32]) + gins(x86.AFMOVFP, f, &r1) + gins(x86.AFMOVF, &r1, t) + return + } + + if f.Op == gc.OREGISTER { + gins(x86.AFMOVFP, f, t) + } else { + gins(x86.AFMOVD, f, t) + } + return + } + + gins(a, f, t) + return + + // requires register intermediate +hard: + gc.Regalloc(&r1, cvt, t) + + gmove(f, &r1) + gmove(&r1, t) + gc.Regfree(&r1) + return + + // requires memory intermediate +hardmem: + gc.Tempname(&r1, cvt) + + gmove(f, &r1) + gmove(&r1, t) + return + + // should not happen +fatal: + gc.Fatal("gmove %v -> %v", gc.Nconv(f, obj.FmtLong), gc.Nconv(t, obj.FmtLong)) + + return +} + +func floatmove_sse(f *gc.Node, t *gc.Node) { + var r1 gc.Node + var cvt *gc.Type + var a int + + ft := gc.Simsimtype(f.Type) + tt := gc.Simsimtype(t.Type) + + switch uint32(ft)<<16 | uint32(tt) { + // should not happen + default: + gc.Fatal("gmove %v -> %v", f, t) + + return + + // convert via int32. + /* + * float to integer + */ + case gc.TFLOAT32<<16 | gc.TINT16, + gc.TFLOAT32<<16 | gc.TINT8, + gc.TFLOAT32<<16 | gc.TUINT16, + gc.TFLOAT32<<16 | gc.TUINT8, + gc.TFLOAT64<<16 | gc.TINT16, + gc.TFLOAT64<<16 | gc.TINT8, + gc.TFLOAT64<<16 | gc.TUINT16, + gc.TFLOAT64<<16 | gc.TUINT8: + cvt = gc.Types[gc.TINT32] + + goto hard + + // convert via int64. + case gc.TFLOAT32<<16 | gc.TUINT32, + gc.TFLOAT64<<16 | gc.TUINT32: + cvt = gc.Types[gc.TINT64] + + goto hardmem + + case gc.TFLOAT32<<16 | gc.TINT32: + a = x86.ACVTTSS2SL + goto rdst + + case gc.TFLOAT64<<16 | gc.TINT32: + a = x86.ACVTTSD2SL + goto rdst + + // convert via int32 memory + /* + * integer to float + */ + case gc.TINT8<<16 | gc.TFLOAT32, + gc.TINT8<<16 | gc.TFLOAT64, + gc.TINT16<<16 | gc.TFLOAT32, + gc.TINT16<<16 | gc.TFLOAT64, + gc.TUINT16<<16 | gc.TFLOAT32, + gc.TUINT16<<16 | gc.TFLOAT64, + gc.TUINT8<<16 | gc.TFLOAT32, + gc.TUINT8<<16 | gc.TFLOAT64: + cvt = gc.Types[gc.TINT32] + + goto hard + + // convert via int64 memory + case gc.TUINT32<<16 | gc.TFLOAT32, + gc.TUINT32<<16 | gc.TFLOAT64: + cvt = gc.Types[gc.TINT64] + + goto hardmem + + case gc.TINT32<<16 | gc.TFLOAT32: + a = x86.ACVTSL2SS + goto rdst + + case gc.TINT32<<16 | gc.TFLOAT64: + a = x86.ACVTSL2SD + goto rdst + + /* + * float to float + */ + case gc.TFLOAT32<<16 | gc.TFLOAT32: + a = x86.AMOVSS + + case gc.TFLOAT64<<16 | gc.TFLOAT64: + a = x86.AMOVSD + + case gc.TFLOAT32<<16 | gc.TFLOAT64: + a = x86.ACVTSS2SD + goto rdst + + case gc.TFLOAT64<<16 | gc.TFLOAT32: + a = x86.ACVTSD2SS + goto rdst + } + + gins(a, f, t) + return + + // requires register intermediate +hard: + gc.Regalloc(&r1, cvt, t) + + gmove(f, &r1) + gmove(&r1, t) + gc.Regfree(&r1) + return + + // requires memory intermediate +hardmem: + gc.Tempname(&r1, cvt) + + gmove(f, &r1) + gmove(&r1, t) + return + + // requires register destination +rdst: + gc.Regalloc(&r1, t.Type, t) + + gins(a, f, &r1) + gmove(&r1, t) + gc.Regfree(&r1) + return +} + +func samaddr(f *gc.Node, t *gc.Node) bool { + if f.Op != t.Op { + return false + } + + switch f.Op { + case gc.OREGISTER: + if f.Reg != t.Reg { + break + } + return true + } + + return false +} + +/* + * generate one instruction: + * as f, t + */ +func gins(as int, f *gc.Node, t *gc.Node) *obj.Prog { + if as == x86.AFMOVF && f != nil && f.Op == gc.OREGISTER && t != nil && t.Op == gc.OREGISTER { + gc.Fatal("gins MOVF reg, reg") + } + if as == x86.ACVTSD2SS && f != nil && f.Op == gc.OLITERAL { + gc.Fatal("gins CVTSD2SS const") + } + if as == x86.AMOVSD && t != nil && t.Op == gc.OREGISTER && t.Reg == x86.REG_F0 { + gc.Fatal("gins MOVSD into F0") + } + + if as == x86.AMOVL && f != nil && f.Op == gc.OADDR && f.Left.Op == gc.ONAME && f.Left.Class != gc.PEXTERN && f.Left.Class != gc.PFUNC { + // Turn MOVL $xxx(FP/SP) into LEAL xxx. + // These should be equivalent but most of the backend + // only expects to see LEAL, because that's what we had + // historically generated. Various hidden assumptions are baked in by now. + as = x86.ALEAL + f = f.Left + } + + switch as { + case x86.AMOVB, + x86.AMOVW, + x86.AMOVL: + if f != nil && t != nil && samaddr(f, t) { + return nil + } + + case x86.ALEAL: + if f != nil && gc.Isconst(f, gc.CTNIL) { + gc.Fatal("gins LEAL nil %v", f.Type) + } + } + + p := gc.Prog(as) + gc.Naddr(&p.From, f) + gc.Naddr(&p.To, t) + + if gc.Debug['g'] != 0 { + fmt.Printf("%v\n", p) + } + + w := 0 + switch as { + case x86.AMOVB: + w = 1 + + case x86.AMOVW: + w = 2 + + case x86.AMOVL: + w = 4 + } + + if true && w != 0 && f != nil && (p.From.Width > int64(w) || p.To.Width > int64(w)) { + gc.Dump("bad width from:", f) + gc.Dump("bad width to:", t) + gc.Fatal("bad width: %v (%d, %d)\n", p, p.From.Width, p.To.Width) + } + + if p.To.Type == obj.TYPE_ADDR && w > 0 { + gc.Fatal("bad use of addr: %v", p) + } + + return p +} + +func ginsnop() { + var reg gc.Node + gc.Nodreg(®, gc.Types[gc.TINT], x86.REG_AX) + gins(x86.AXCHGL, ®, ®) +} + +func dotaddable(n *gc.Node, n1 *gc.Node) bool { + if n.Op != gc.ODOT { + return false + } + + var oary [10]int64 + var nn *gc.Node + o := gc.Dotoffset(n, oary[:], &nn) + if nn != nil && nn.Addable && o == 1 && oary[0] >= 0 { + *n1 = *nn + n1.Type = n.Type + n1.Xoffset += oary[0] + return true + } + + return false +} + +func sudoclean() { +} + +func sudoaddable(as int, n *gc.Node, a *obj.Addr) bool { + *a = obj.Addr{} + return false +} diff --git a/src/cmd/compile/internal/x86/peep.go b/src/cmd/compile/internal/x86/peep.go new file mode 100644 index 0000000000..8b50eab077 --- /dev/null +++ b/src/cmd/compile/internal/x86/peep.go @@ -0,0 +1,814 @@ +// Derived from Inferno utils/6c/peep.c +// http://code.google.com/p/inferno-os/source/browse/utils/6c/peep.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package x86 + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/x86" + "fmt" +) + +const ( + REGEXT = 0 + exregoffset = x86.REG_DI +) + +var gactive uint32 + +// do we need the carry bit +func needc(p *obj.Prog) bool { + for p != nil { + if p.Info.Flags&gc.UseCarry != 0 { + return true + } + if p.Info.Flags&(gc.SetCarry|gc.KillCarry) != 0 { + return false + } + p = p.Link + } + + return false +} + +func rnops(r *gc.Flow) *gc.Flow { + if r != nil { + var p *obj.Prog + var r1 *gc.Flow + for { + p = r.Prog + if p.As != obj.ANOP || p.From.Type != obj.TYPE_NONE || p.To.Type != obj.TYPE_NONE { + break + } + r1 = gc.Uniqs(r) + if r1 == nil { + break + } + r = r1 + } + } + + return r +} + +func peep(firstp *obj.Prog) { + g := gc.Flowstart(firstp, nil) + if g == nil { + return + } + gactive = 0 + + // byte, word arithmetic elimination. + elimshortmov(g) + + // constant propagation + // find MOV $con,R followed by + // another MOV $con,R without + // setting R in the interim + var p *obj.Prog + for r := g.Start; r != nil; r = r.Link { + p = r.Prog + switch p.As { + case x86.ALEAL: + if regtyp(&p.To) { + if p.From.Sym != nil { + if p.From.Index == x86.REG_NONE { + conprop(r) + } + } + } + + case x86.AMOVB, + x86.AMOVW, + x86.AMOVL, + x86.AMOVSS, + x86.AMOVSD: + if regtyp(&p.To) { + if p.From.Type == obj.TYPE_CONST || p.From.Type == obj.TYPE_FCONST { + conprop(r) + } + } + } + } + + var r1 *gc.Flow + var p1 *obj.Prog + var r *gc.Flow + var t int +loop1: + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + gc.Dumpit("loop1", g.Start, 0) + } + + t = 0 + for r = g.Start; r != nil; r = r.Link { + p = r.Prog + switch p.As { + case x86.AMOVL, + x86.AMOVSS, + x86.AMOVSD: + if regtyp(&p.To) { + if regtyp(&p.From) { + if copyprop(g, r) { + excise(r) + t++ + } else if subprop(r) && copyprop(g, r) { + excise(r) + t++ + } + } + } + + case x86.AMOVBLZX, + x86.AMOVWLZX, + x86.AMOVBLSX, + x86.AMOVWLSX: + if regtyp(&p.To) { + r1 = rnops(gc.Uniqs(r)) + if r1 != nil { + p1 = r1.Prog + if p.As == p1.As && p.To.Type == p1.From.Type && p.To.Reg == p1.From.Reg { + p1.As = x86.AMOVL + t++ + } + } + } + + case x86.AADDL, + x86.AADDW: + if p.From.Type != obj.TYPE_CONST || needc(p.Link) { + break + } + if p.From.Offset == -1 { + if p.As == x86.AADDL { + p.As = x86.ADECL + } else { + p.As = x86.ADECW + } + p.From = obj.Addr{} + break + } + + if p.From.Offset == 1 { + if p.As == x86.AADDL { + p.As = x86.AINCL + } else { + p.As = x86.AINCW + } + p.From = obj.Addr{} + break + } + + case x86.ASUBL, + x86.ASUBW: + if p.From.Type != obj.TYPE_CONST || needc(p.Link) { + break + } + if p.From.Offset == -1 { + if p.As == x86.ASUBL { + p.As = x86.AINCL + } else { + p.As = x86.AINCW + } + p.From = obj.Addr{} + break + } + + if p.From.Offset == 1 { + if p.As == x86.ASUBL { + p.As = x86.ADECL + } else { + p.As = x86.ADECW + } + p.From = obj.Addr{} + break + } + } + } + + if t != 0 { + goto loop1 + } + + // MOVSD removal. + // We never use packed registers, so a MOVSD between registers + // can be replaced by MOVAPD, which moves the pair of float64s + // instead of just the lower one. We only use the lower one, but + // the processor can do better if we do moves using both. + for r := g.Start; r != nil; r = r.Link { + p = r.Prog + if p.As == x86.AMOVSD { + if regtyp(&p.From) { + if regtyp(&p.To) { + p.As = x86.AMOVAPD + } + } + } + } + + gc.Flowend(g) +} + +func excise(r *gc.Flow) { + p := r.Prog + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + fmt.Printf("%v ===delete===\n", p) + } + + obj.Nopout(p) + + gc.Ostats.Ndelmov++ +} + +func regtyp(a *obj.Addr) bool { + return a.Type == obj.TYPE_REG && (x86.REG_AX <= a.Reg && a.Reg <= x86.REG_DI || x86.REG_X0 <= a.Reg && a.Reg <= x86.REG_X7) +} + +// movb elimination. +// movb is simulated by the linker +// when a register other than ax, bx, cx, dx +// is used, so rewrite to other instructions +// when possible. a movb into a register +// can smash the entire 64-bit register without +// causing any trouble. +func elimshortmov(g *gc.Graph) { + var p *obj.Prog + + for r := g.Start; r != nil; r = r.Link { + p = r.Prog + if regtyp(&p.To) { + switch p.As { + case x86.AINCB, + x86.AINCW: + p.As = x86.AINCL + + case x86.ADECB, + x86.ADECW: + p.As = x86.ADECL + + case x86.ANEGB, + x86.ANEGW: + p.As = x86.ANEGL + + case x86.ANOTB, + x86.ANOTW: + p.As = x86.ANOTL + } + + if regtyp(&p.From) || p.From.Type == obj.TYPE_CONST { + // move or artihmetic into partial register. + // from another register or constant can be movl. + // we don't switch to 32-bit arithmetic if it can + // change how the carry bit is set (and the carry bit is needed). + switch p.As { + case x86.AMOVB, + x86.AMOVW: + p.As = x86.AMOVL + + case x86.AADDB, + x86.AADDW: + if !needc(p.Link) { + p.As = x86.AADDL + } + + case x86.ASUBB, + x86.ASUBW: + if !needc(p.Link) { + p.As = x86.ASUBL + } + + case x86.AMULB, + x86.AMULW: + p.As = x86.AMULL + + case x86.AIMULB, + x86.AIMULW: + p.As = x86.AIMULL + + case x86.AANDB, + x86.AANDW: + p.As = x86.AANDL + + case x86.AORB, + x86.AORW: + p.As = x86.AORL + + case x86.AXORB, + x86.AXORW: + p.As = x86.AXORL + + case x86.ASHLB, + x86.ASHLW: + p.As = x86.ASHLL + } + } else { + // explicit zero extension + switch p.As { + case x86.AMOVB: + p.As = x86.AMOVBLZX + + case x86.AMOVW: + p.As = x86.AMOVWLZX + } + } + } + } +} + +/* + * the idea is to substitute + * one register for another + * from one MOV to another + * MOV a, R0 + * ADD b, R0 / no use of R1 + * MOV R0, R1 + * would be converted to + * MOV a, R1 + * ADD b, R1 + * MOV R1, R0 + * hopefully, then the former or latter MOV + * will be eliminated by copy propagation. + */ +func subprop(r0 *gc.Flow) bool { + p := r0.Prog + v1 := &p.From + if !regtyp(v1) { + return false + } + v2 := &p.To + if !regtyp(v2) { + return false + } + for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { + if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { + fmt.Printf("\t? %v\n", r.Prog) + } + if gc.Uniqs(r) == nil { + break + } + p = r.Prog + if p.As == obj.AVARDEF || p.As == obj.AVARKILL { + continue + } + if p.Info.Flags&gc.Call != 0 { + return false + } + + if p.Info.Reguse|p.Info.Regset != 0 { + return false + } + + if (p.Info.Flags&gc.Move != 0) && (p.Info.Flags&(gc.SizeL|gc.SizeQ|gc.SizeF|gc.SizeD) != 0) && p.To.Type == v1.Type && p.To.Reg == v1.Reg { + copysub(&p.To, v1, v2, 1) + if gc.Debug['P'] != 0 { + fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog) + if p.From.Type == v2.Type && p.From.Reg == v2.Reg { + fmt.Printf(" excise") + } + fmt.Printf("\n") + } + + for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { + p = r.Prog + copysub(&p.From, v1, v2, 1) + copysub(&p.To, v1, v2, 1) + if gc.Debug['P'] != 0 { + fmt.Printf("%v\n", r.Prog) + } + } + + t := int(v1.Reg) + v1.Reg = v2.Reg + v2.Reg = int16(t) + if gc.Debug['P'] != 0 { + fmt.Printf("%v last\n", r.Prog) + } + return true + } + + if copyau(&p.From, v2) || copyau(&p.To, v2) { + break + } + if copysub(&p.From, v1, v2, 0) != 0 || copysub(&p.To, v1, v2, 0) != 0 { + break + } + } + + return false +} + +/* + * The idea is to remove redundant copies. + * v1->v2 F=0 + * (use v2 s/v2/v1/)* + * set v1 F=1 + * use v2 return fail + * ----------------- + * v1->v2 F=0 + * (use v2 s/v2/v1/)* + * set v1 F=1 + * set v2 return success + */ +func copyprop(g *gc.Graph, r0 *gc.Flow) bool { + p := r0.Prog + v1 := &p.From + v2 := &p.To + if copyas(v1, v2) { + return true + } + gactive++ + return copy1(v1, v2, r0.S1, 0) +} + +func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f int) bool { + if uint32(r.Active) == gactive { + if gc.Debug['P'] != 0 { + fmt.Printf("act set; return 1\n") + } + return true + } + + r.Active = int32(gactive) + if gc.Debug['P'] != 0 { + fmt.Printf("copy %v->%v f=%d\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), f) + } + var t int + var p *obj.Prog + for ; r != nil; r = r.S1 { + p = r.Prog + if gc.Debug['P'] != 0 { + fmt.Printf("%v", p) + } + if f == 0 && gc.Uniqp(r) == nil { + f = 1 + if gc.Debug['P'] != 0 { + fmt.Printf("; merge; f=%d", f) + } + } + + t = copyu(p, v2, nil) + switch t { + case 2: /* rar, can't split */ + if gc.Debug['P'] != 0 { + fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2)) + } + return false + + case 3: /* set */ + if gc.Debug['P'] != 0 { + fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2)) + } + return true + + case 1, /* used, substitute */ + 4: /* use and set */ + if f != 0 { + if gc.Debug['P'] == 0 { + return false + } + if t == 4 { + fmt.Printf("; %v used+set and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f) + } else { + fmt.Printf("; %v used and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f) + } + return false + } + + if copyu(p, v2, v1) != 0 { + if gc.Debug['P'] != 0 { + fmt.Printf("; sub fail; return 0\n") + } + return false + } + + if gc.Debug['P'] != 0 { + fmt.Printf("; sub %v/%v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1)) + } + if t == 4 { + if gc.Debug['P'] != 0 { + fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2)) + } + return true + } + } + + if f == 0 { + t = copyu(p, v1, nil) + if f == 0 && (t == 2 || t == 3 || t == 4) { + f = 1 + if gc.Debug['P'] != 0 { + fmt.Printf("; %v set and !f; f=%d", gc.Ctxt.Dconv(v1), f) + } + } + } + + if gc.Debug['P'] != 0 { + fmt.Printf("\n") + } + if r.S2 != nil { + if !copy1(v1, v2, r.S2, f) { + return false + } + } + } + + return true +} + +/* + * return + * 1 if v only used (and substitute), + * 2 if read-alter-rewrite + * 3 if set + * 4 if set and used + * 0 otherwise (not touched) + */ +func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int { + switch p.As { + case obj.AJMP: + if s != nil { + if copysub(&p.To, v, s, 1) != 0 { + return 1 + } + return 0 + } + + if copyau(&p.To, v) { + return 1 + } + return 0 + + case obj.ARET: + if s != nil { + return 1 + } + return 3 + + case obj.ACALL: + if REGEXT != 0 /*TypeKind(100016)*/ && v.Type == obj.TYPE_REG && v.Reg <= REGEXT && v.Reg > exregoffset { + return 2 + } + if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG { + return 2 + } + if v.Type == p.From.Type && v.Reg == p.From.Reg { + return 2 + } + + if s != nil { + if copysub(&p.To, v, s, 1) != 0 { + return 1 + } + return 0 + } + + if copyau(&p.To, v) { + return 4 + } + return 3 + + case obj.ATEXT: + if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG { + return 3 + } + return 0 + } + + if p.As == obj.AVARDEF || p.As == obj.AVARKILL { + return 0 + } + + if (p.Info.Reguse|p.Info.Regset)&RtoB(int(v.Reg)) != 0 { + return 2 + } + + if p.Info.Flags&gc.LeftAddr != 0 { + if copyas(&p.From, v) { + return 2 + } + } + + if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightRead|gc.RightWrite { + if copyas(&p.To, v) { + return 2 + } + } + + if p.Info.Flags&gc.RightWrite != 0 { + if copyas(&p.To, v) { + if s != nil { + return copysub(&p.From, v, s, 1) + } + if copyau(&p.From, v) { + return 4 + } + return 3 + } + } + + if p.Info.Flags&(gc.LeftAddr|gc.LeftRead|gc.LeftWrite|gc.RightAddr|gc.RightRead|gc.RightWrite) != 0 { + if s != nil { + if copysub(&p.From, v, s, 1) != 0 { + return 1 + } + return copysub(&p.To, v, s, 1) + } + + if copyau(&p.From, v) { + return 1 + } + if copyau(&p.To, v) { + return 1 + } + } + + return 0 +} + +/* + * direct reference, + * could be set/use depending on + * semantics + */ +func copyas(a *obj.Addr, v *obj.Addr) bool { + if x86.REG_AL <= a.Reg && a.Reg <= x86.REG_BL { + gc.Fatal("use of byte register") + } + if x86.REG_AL <= v.Reg && v.Reg <= x86.REG_BL { + gc.Fatal("use of byte register") + } + + if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg { + return false + } + if regtyp(v) { + return true + } + if (v.Type == obj.TYPE_MEM || v.Type == obj.TYPE_ADDR) && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) { + if v.Offset == a.Offset { + return true + } + } + return false +} + +func sameaddr(a *obj.Addr, v *obj.Addr) bool { + if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg { + return false + } + if regtyp(v) { + return true + } + if (v.Type == obj.TYPE_MEM || v.Type == obj.TYPE_ADDR) && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) { + if v.Offset == a.Offset { + return true + } + } + return false +} + +/* + * either direct or indirect + */ +func copyau(a *obj.Addr, v *obj.Addr) bool { + if copyas(a, v) { + return true + } + if regtyp(v) { + if (a.Type == obj.TYPE_MEM || a.Type == obj.TYPE_ADDR) && a.Reg == v.Reg { + return true + } + if a.Index == v.Reg { + return true + } + } + + return false +} + +/* + * substitute s for v in a + * return failure to substitute + */ +func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f int) int { + if copyas(a, v) { + reg := int(s.Reg) + if reg >= x86.REG_AX && reg <= x86.REG_DI || reg >= x86.REG_X0 && reg <= x86.REG_X7 { + if f != 0 { + a.Reg = int16(reg) + } + } + + return 0 + } + + if regtyp(v) { + reg := int(v.Reg) + if (a.Type == obj.TYPE_MEM || a.Type == obj.TYPE_ADDR) && int(a.Reg) == reg { + if (s.Reg == x86.REG_BP) && a.Index != obj.TYPE_NONE { + return 1 /* can't use BP-base with index */ + } + if f != 0 { + a.Reg = s.Reg + } + } + + // return 0; + if int(a.Index) == reg { + if f != 0 { + a.Index = s.Reg + } + return 0 + } + + return 0 + } + + return 0 +} + +func conprop(r0 *gc.Flow) { + var p *obj.Prog + var t int + + p0 := r0.Prog + v0 := &p0.To + r := r0 + +loop: + r = gc.Uniqs(r) + if r == nil || r == r0 { + return + } + if gc.Uniqp(r) == nil { + return + } + + p = r.Prog + t = copyu(p, v0, nil) + switch t { + case 0, // miss + 1: // use + goto loop + + case 2, // rar + 4: // use and set + break + + case 3: // set + if p.As == p0.As { + if p.From.Type == p0.From.Type { + if p.From.Reg == p0.From.Reg { + if p.From.Node == p0.From.Node { + if p.From.Offset == p0.From.Offset { + if p.From.Scale == p0.From.Scale { + if p.From.Type == obj.TYPE_FCONST && p.From.Val.(float64) == p0.From.Val.(float64) { + if p.From.Index == p0.From.Index { + excise(r) + goto loop + } + } + } + } + } + } + } + } + } +} + +func smallindir(a *obj.Addr, reg *obj.Addr) bool { + return regtyp(reg) && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && a.Index == x86.REG_NONE && 0 <= a.Offset && a.Offset < 4096 +} + +func stackaddr(a *obj.Addr) bool { + return a.Type == obj.TYPE_REG && a.Reg == x86.REG_SP +} diff --git a/src/cmd/compile/internal/x86/prog.go b/src/cmd/compile/internal/x86/prog.go new file mode 100644 index 0000000000..f96a1aa945 --- /dev/null +++ b/src/cmd/compile/internal/x86/prog.go @@ -0,0 +1,292 @@ +// Copyright 2013 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 x86 + +import ( + "cmd/compile/internal/gc" + "cmd/internal/obj" + "cmd/internal/obj/x86" +) + +var ( + AX = RtoB(x86.REG_AX) + BX = RtoB(x86.REG_BX) + CX = RtoB(x86.REG_CX) + DX = RtoB(x86.REG_DX) + DI = RtoB(x86.REG_DI) + SI = RtoB(x86.REG_SI) + LeftRdwr uint32 = gc.LeftRead | gc.LeftWrite + RightRdwr uint32 = gc.RightRead | gc.RightWrite +) + +// This table gives the basic information about instruction +// generated by the compiler and processed in the optimizer. +// See opt.h for bit definitions. +// +// Instructions not generated need not be listed. +// As an exception to that rule, we typically write down all the +// size variants of an operation even if we just use a subset. +// +// The table is formatted for 8-space tabs. +var progtable = [x86.ALAST]obj.ProgInfo{ + obj.ATYPE: {gc.Pseudo | gc.Skip, 0, 0, 0}, + obj.ATEXT: {gc.Pseudo, 0, 0, 0}, + obj.AFUNCDATA: {gc.Pseudo, 0, 0, 0}, + obj.APCDATA: {gc.Pseudo, 0, 0, 0}, + obj.AUNDEF: {gc.Break, 0, 0, 0}, + obj.AUSEFIELD: {gc.OK, 0, 0, 0}, + obj.ACHECKNIL: {gc.LeftRead, 0, 0, 0}, + obj.AVARDEF: {gc.Pseudo | gc.RightWrite, 0, 0, 0}, + obj.AVARKILL: {gc.Pseudo | gc.RightWrite, 0, 0, 0}, + + // NOP is an internal no-op that also stands + // for USED and SET annotations, not the Intel opcode. + obj.ANOP: {gc.LeftRead | gc.RightWrite, 0, 0, 0}, + x86.AADCL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry | gc.UseCarry, 0, 0, 0}, + x86.AADCW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry | gc.UseCarry, 0, 0, 0}, + x86.AADDB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.AADDL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.AADDW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.AADDSD: {gc.SizeD | gc.LeftRead | RightRdwr, 0, 0, 0}, + x86.AADDSS: {gc.SizeF | gc.LeftRead | RightRdwr, 0, 0, 0}, + x86.AANDB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.AANDL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.AANDW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + obj.ACALL: {gc.RightAddr | gc.Call | gc.KillCarry, 0, 0, 0}, + x86.ACDQ: {gc.OK, AX, AX | DX, 0}, + x86.ACWD: {gc.OK, AX, AX | DX, 0}, + x86.ACLD: {gc.OK, 0, 0, 0}, + x86.ASTD: {gc.OK, 0, 0, 0}, + x86.ACMPB: {gc.SizeB | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, + x86.ACMPL: {gc.SizeL | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, + x86.ACMPW: {gc.SizeW | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, + x86.ACOMISD: {gc.SizeD | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, + x86.ACOMISS: {gc.SizeF | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, + x86.ACVTSD2SL: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.ACVTSD2SS: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.ACVTSL2SD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.ACVTSL2SS: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.ACVTSS2SD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.ACVTSS2SL: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.ACVTTSD2SL: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.ACVTTSS2SL: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.ADECB: {gc.SizeB | RightRdwr, 0, 0, 0}, + x86.ADECL: {gc.SizeL | RightRdwr, 0, 0, 0}, + x86.ADECW: {gc.SizeW | RightRdwr, 0, 0, 0}, + x86.ADIVB: {gc.SizeB | gc.LeftRead | gc.SetCarry, AX, AX, 0}, + x86.ADIVL: {gc.SizeL | gc.LeftRead | gc.SetCarry, AX | DX, AX | DX, 0}, + x86.ADIVW: {gc.SizeW | gc.LeftRead | gc.SetCarry, AX | DX, AX | DX, 0}, + x86.ADIVSD: {gc.SizeD | gc.LeftRead | RightRdwr, 0, 0, 0}, + x86.ADIVSS: {gc.SizeF | gc.LeftRead | RightRdwr, 0, 0, 0}, + x86.AFLDCW: {gc.SizeW | gc.LeftAddr, 0, 0, 0}, + x86.AFSTCW: {gc.SizeW | gc.RightAddr, 0, 0, 0}, + x86.AFSTSW: {gc.SizeW | gc.RightAddr | gc.RightWrite, 0, 0, 0}, + x86.AFADDD: {gc.SizeD | gc.LeftAddr | RightRdwr, 0, 0, 0}, + x86.AFADDDP: {gc.SizeD | gc.LeftAddr | RightRdwr, 0, 0, 0}, + x86.AFADDF: {gc.SizeF | gc.LeftAddr | RightRdwr, 0, 0, 0}, + x86.AFCOMD: {gc.SizeD | gc.LeftAddr | gc.RightRead, 0, 0, 0}, + x86.AFCOMDP: {gc.SizeD | gc.LeftAddr | gc.RightRead, 0, 0, 0}, + x86.AFCOMDPP: {gc.SizeD | gc.LeftAddr | gc.RightRead, 0, 0, 0}, + x86.AFCOMF: {gc.SizeF | gc.LeftAddr | gc.RightRead, 0, 0, 0}, + x86.AFCOMFP: {gc.SizeF | gc.LeftAddr | gc.RightRead, 0, 0, 0}, + x86.AFUCOMIP: {gc.SizeF | gc.LeftAddr | gc.RightRead, 0, 0, 0}, + x86.AFCHS: {gc.SizeD | RightRdwr, 0, 0, 0}, // also SizeF + + x86.AFDIVDP: {gc.SizeD | gc.LeftAddr | RightRdwr, 0, 0, 0}, + x86.AFDIVF: {gc.SizeF | gc.LeftAddr | RightRdwr, 0, 0, 0}, + x86.AFDIVD: {gc.SizeD | gc.LeftAddr | RightRdwr, 0, 0, 0}, + x86.AFDIVRDP: {gc.SizeD | gc.LeftAddr | RightRdwr, 0, 0, 0}, + x86.AFDIVRF: {gc.SizeF | gc.LeftAddr | RightRdwr, 0, 0, 0}, + x86.AFDIVRD: {gc.SizeD | gc.LeftAddr | RightRdwr, 0, 0, 0}, + x86.AFXCHD: {gc.SizeD | LeftRdwr | RightRdwr, 0, 0, 0}, + x86.AFSUBD: {gc.SizeD | gc.LeftAddr | RightRdwr, 0, 0, 0}, + x86.AFSUBDP: {gc.SizeD | gc.LeftAddr | RightRdwr, 0, 0, 0}, + x86.AFSUBF: {gc.SizeF | gc.LeftAddr | RightRdwr, 0, 0, 0}, + x86.AFSUBRD: {gc.SizeD | gc.LeftAddr | RightRdwr, 0, 0, 0}, + x86.AFSUBRDP: {gc.SizeD | gc.LeftAddr | RightRdwr, 0, 0, 0}, + x86.AFSUBRF: {gc.SizeF | gc.LeftAddr | RightRdwr, 0, 0, 0}, + x86.AFMOVD: {gc.SizeD | gc.LeftAddr | gc.RightWrite, 0, 0, 0}, + x86.AFMOVF: {gc.SizeF | gc.LeftAddr | gc.RightWrite, 0, 0, 0}, + x86.AFMOVL: {gc.SizeL | gc.LeftAddr | gc.RightWrite, 0, 0, 0}, + x86.AFMOVW: {gc.SizeW | gc.LeftAddr | gc.RightWrite, 0, 0, 0}, + x86.AFMOVV: {gc.SizeQ | gc.LeftAddr | gc.RightWrite, 0, 0, 0}, + + // These instructions are marked as RightAddr + // so that the register optimizer does not try to replace the + // memory references with integer register references. + // But they do not use the previous value at the address, so + // we also mark them RightWrite. + x86.AFMOVDP: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.RightAddr, 0, 0, 0}, + x86.AFMOVFP: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.RightAddr, 0, 0, 0}, + x86.AFMOVLP: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.RightAddr, 0, 0, 0}, + x86.AFMOVWP: {gc.SizeW | gc.LeftRead | gc.RightWrite | gc.RightAddr, 0, 0, 0}, + x86.AFMOVVP: {gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.RightAddr, 0, 0, 0}, + x86.AFMULD: {gc.SizeD | gc.LeftAddr | RightRdwr, 0, 0, 0}, + x86.AFMULDP: {gc.SizeD | gc.LeftAddr | RightRdwr, 0, 0, 0}, + x86.AFMULF: {gc.SizeF | gc.LeftAddr | RightRdwr, 0, 0, 0}, + x86.AIDIVB: {gc.SizeB | gc.LeftRead | gc.SetCarry, AX, AX, 0}, + x86.AIDIVL: {gc.SizeL | gc.LeftRead | gc.SetCarry, AX | DX, AX | DX, 0}, + x86.AIDIVW: {gc.SizeW | gc.LeftRead | gc.SetCarry, AX | DX, AX | DX, 0}, + x86.AIMULB: {gc.SizeB | gc.LeftRead | gc.SetCarry, AX, AX, 0}, + x86.AIMULL: {gc.SizeL | gc.LeftRead | gc.ImulAXDX | gc.SetCarry, 0, 0, 0}, + x86.AIMULW: {gc.SizeW | gc.LeftRead | gc.ImulAXDX | gc.SetCarry, 0, 0, 0}, + x86.AINCB: {gc.SizeB | RightRdwr, 0, 0, 0}, + x86.AINCL: {gc.SizeL | RightRdwr, 0, 0, 0}, + x86.AINCW: {gc.SizeW | RightRdwr, 0, 0, 0}, + x86.AJCC: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + x86.AJCS: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + x86.AJEQ: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + x86.AJGE: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + x86.AJGT: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + x86.AJHI: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + x86.AJLE: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + x86.AJLS: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + x86.AJLT: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + x86.AJMI: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + x86.AJNE: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + x86.AJOC: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + x86.AJOS: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + x86.AJPC: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + x86.AJPL: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + x86.AJPS: {gc.Cjmp | gc.UseCarry, 0, 0, 0}, + obj.AJMP: {gc.Jump | gc.Break | gc.KillCarry, 0, 0, 0}, + x86.ALEAL: {gc.LeftAddr | gc.RightWrite, 0, 0, 0}, + x86.AMOVBLSX: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.AMOVBLZX: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.AMOVBWSX: {gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.AMOVBWZX: {gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.AMOVWLSX: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.AMOVWLZX: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv, 0, 0, 0}, + x86.AMOVB: {gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, + x86.AMOVL: {gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, + x86.AMOVW: {gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, + x86.AMOVSB: {gc.OK, DI | SI, DI | SI, 0}, + x86.AMOVSL: {gc.OK, DI | SI, DI | SI, 0}, + x86.AMOVSW: {gc.OK, DI | SI, DI | SI, 0}, + obj.ADUFFCOPY: {gc.OK, DI | SI, DI | SI | CX, 0}, + x86.AMOVSD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, + x86.AMOVSS: {gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, + + // We use MOVAPD as a faster synonym for MOVSD. + x86.AMOVAPD: {gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Move, 0, 0, 0}, + x86.AMULB: {gc.SizeB | gc.LeftRead | gc.SetCarry, AX, AX, 0}, + x86.AMULL: {gc.SizeL | gc.LeftRead | gc.SetCarry, AX, AX | DX, 0}, + x86.AMULW: {gc.SizeW | gc.LeftRead | gc.SetCarry, AX, AX | DX, 0}, + x86.AMULSD: {gc.SizeD | gc.LeftRead | RightRdwr, 0, 0, 0}, + x86.AMULSS: {gc.SizeF | gc.LeftRead | RightRdwr, 0, 0, 0}, + x86.ANEGB: {gc.SizeB | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.ANEGL: {gc.SizeL | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.ANEGW: {gc.SizeW | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.ANOTB: {gc.SizeB | RightRdwr, 0, 0, 0}, + x86.ANOTL: {gc.SizeL | RightRdwr, 0, 0, 0}, + x86.ANOTW: {gc.SizeW | RightRdwr, 0, 0, 0}, + x86.AORB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.AORL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.AORW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.APOPL: {gc.SizeL | gc.RightWrite, 0, 0, 0}, + x86.APUSHL: {gc.SizeL | gc.LeftRead, 0, 0, 0}, + x86.ARCLB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry, 0, 0, 0}, + x86.ARCLL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry, 0, 0, 0}, + x86.ARCLW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry, 0, 0, 0}, + x86.ARCRB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry, 0, 0, 0}, + x86.ARCRL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry, 0, 0, 0}, + x86.ARCRW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry, 0, 0, 0}, + x86.AREP: {gc.OK, CX, CX, 0}, + x86.AREPN: {gc.OK, CX, CX, 0}, + obj.ARET: {gc.Break | gc.KillCarry, 0, 0, 0}, + x86.AROLB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.AROLL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.AROLW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ARORB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ARORL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ARORW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ASAHF: {gc.OK, AX, AX, 0}, + x86.ASALB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ASALL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ASALW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ASARB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ASARL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ASARW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ASBBB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.SetCarry | gc.UseCarry, 0, 0, 0}, + x86.ASBBL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry | gc.UseCarry, 0, 0, 0}, + x86.ASBBW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry | gc.UseCarry, 0, 0, 0}, + x86.ASETCC: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, + x86.ASETCS: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, + x86.ASETEQ: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, + x86.ASETGE: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, + x86.ASETGT: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, + x86.ASETHI: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, + x86.ASETLE: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, + x86.ASETLS: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, + x86.ASETLT: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, + x86.ASETMI: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, + x86.ASETNE: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, + x86.ASETOC: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, + x86.ASETOS: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, + x86.ASETPC: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, + x86.ASETPL: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, + x86.ASETPS: {gc.SizeB | RightRdwr | gc.UseCarry, 0, 0, 0}, + x86.ASHLB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ASHLL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ASHLW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ASHRB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ASHRL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ASHRW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry, 0, 0, 0}, + x86.ASTOSB: {gc.OK, AX | DI, DI, 0}, + x86.ASTOSL: {gc.OK, AX | DI, DI, 0}, + x86.ASTOSW: {gc.OK, AX | DI, DI, 0}, + obj.ADUFFZERO: {gc.OK, AX | DI, DI, 0}, + x86.ASUBB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.ASUBL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.ASUBW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.ASUBSD: {gc.SizeD | gc.LeftRead | RightRdwr, 0, 0, 0}, + x86.ASUBSS: {gc.SizeF | gc.LeftRead | RightRdwr, 0, 0, 0}, + x86.ATESTB: {gc.SizeB | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, + x86.ATESTL: {gc.SizeL | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, + x86.ATESTW: {gc.SizeW | gc.LeftRead | gc.RightRead | gc.SetCarry, 0, 0, 0}, + x86.AUCOMISD: {gc.SizeD | gc.LeftRead | gc.RightRead, 0, 0, 0}, + x86.AUCOMISS: {gc.SizeF | gc.LeftRead | gc.RightRead, 0, 0, 0}, + x86.AXCHGB: {gc.SizeB | LeftRdwr | RightRdwr, 0, 0, 0}, + x86.AXCHGL: {gc.SizeL | LeftRdwr | RightRdwr, 0, 0, 0}, + x86.AXCHGW: {gc.SizeW | LeftRdwr | RightRdwr, 0, 0, 0}, + x86.AXORB: {gc.SizeB | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.AXORL: {gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, + x86.AXORW: {gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry, 0, 0, 0}, +} + +func proginfo(p *obj.Prog) { + info := &p.Info + *info = progtable[p.As] + if info.Flags == 0 { + gc.Fatal("unknown instruction %v", p) + } + + if (info.Flags&gc.ShiftCX != 0) && p.From.Type != obj.TYPE_CONST { + info.Reguse |= CX + } + + if info.Flags&gc.ImulAXDX != 0 { + if p.To.Type == obj.TYPE_NONE { + info.Reguse |= AX + info.Regset |= AX | DX + } else { + info.Flags |= RightRdwr + } + } + + // Addressing makes some registers used. + if p.From.Type == obj.TYPE_MEM && p.From.Name == obj.NAME_NONE { + info.Regindex |= RtoB(int(p.From.Reg)) + } + if p.From.Index != x86.REG_NONE { + info.Regindex |= RtoB(int(p.From.Index)) + } + if p.To.Type == obj.TYPE_MEM && p.To.Name == obj.NAME_NONE { + info.Regindex |= RtoB(int(p.To.Reg)) + } + if p.To.Index != x86.REG_NONE { + info.Regindex |= RtoB(int(p.To.Index)) + } +} diff --git a/src/cmd/compile/internal/x86/reg.go b/src/cmd/compile/internal/x86/reg.go new file mode 100644 index 0000000000..8c97171e47 --- /dev/null +++ b/src/cmd/compile/internal/x86/reg.go @@ -0,0 +1,112 @@ +// Derived from Inferno utils/6c/reg.c +// http://code.google.com/p/inferno-os/source/browse/utils/6c/reg.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package x86 + +import "cmd/internal/obj/x86" +import "cmd/compile/internal/gc" + +const ( + NREGVAR = 16 /* 8 integer + 8 floating */ +) + +var reg [x86.MAXREG]uint8 + +var regname = []string{ + ".ax", + ".cx", + ".dx", + ".bx", + ".sp", + ".bp", + ".si", + ".di", + ".x0", + ".x1", + ".x2", + ".x3", + ".x4", + ".x5", + ".x6", + ".x7", +} + +func regnames(n *int) []string { + *n = NREGVAR + return regname +} + +func excludedregs() uint64 { + return RtoB(x86.REG_SP) +} + +func doregbits(r int) uint64 { + b := uint64(0) + if r >= x86.REG_AX && r <= x86.REG_DI { + b |= RtoB(r) + } else if r >= x86.REG_AL && r <= x86.REG_BL { + b |= RtoB(r - x86.REG_AL + x86.REG_AX) + } else if r >= x86.REG_AH && r <= x86.REG_BH { + b |= RtoB(r - x86.REG_AH + x86.REG_AX) + } else if r >= x86.REG_X0 && r <= x86.REG_X0+7 { + b |= FtoB(r) + } + return b +} + +func RtoB(r int) uint64 { + if r < x86.REG_AX || r > x86.REG_DI { + return 0 + } + return 1 << uint(r-x86.REG_AX) +} + +func BtoR(b uint64) int { + b &= 0xff + if b == 0 { + return 0 + } + return gc.Bitno(b) + x86.REG_AX +} + +func FtoB(f int) uint64 { + if f < x86.REG_X0 || f > x86.REG_X7 { + return 0 + } + return 1 << uint(f-x86.REG_X0+8) +} + +func BtoF(b uint64) int { + b &= 0xFF00 + if b == 0 { + return 0 + } + return gc.Bitno(b) - 8 + x86.REG_X0 +} 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/internal/gc/align.go b/src/cmd/internal/gc/align.go deleted file mode 100644 index 789e59bfd0..0000000000 --- a/src/cmd/internal/gc/align.go +++ /dev/null @@ -1,706 +0,0 @@ -// Copyright 2009 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 gc - -import "cmd/internal/obj" - -/* - * machine size and rounding - * alignment is dictated around - * the size of a pointer, set in betypeinit - * (see ../6g/galign.c). - */ -var defercalc int - -func Rnd(o int64, r int64) int64 { - if r < 1 || r > 8 || r&(r-1) != 0 { - Fatal("rnd %d", r) - } - return (o + r - 1) &^ (r - 1) -} - -func offmod(t *Type) { - o := int32(0) - for f := t.Type; f != nil; f = f.Down { - if f.Etype != TFIELD { - Fatal("offmod: not TFIELD: %v", Tconv(f, obj.FmtLong)) - } - f.Width = int64(o) - o += int32(Widthptr) - if int64(o) >= Thearch.MAXWIDTH { - Yyerror("interface too large") - o = int32(Widthptr) - } - } -} - -func widstruct(errtype *Type, t *Type, o int64, flag int) int64 { - starto := o - maxalign := int32(flag) - if maxalign < 1 { - maxalign = 1 - } - lastzero := int64(0) - var w int64 - for f := t.Type; f != nil; f = f.Down { - if f.Etype != TFIELD { - Fatal("widstruct: not TFIELD: %v", Tconv(f, obj.FmtLong)) - } - if f.Type == nil { - // broken field, just skip it so that other valid fields - // get a width. - continue - } - - dowidth(f.Type) - if int32(f.Type.Align) > maxalign { - maxalign = int32(f.Type.Align) - } - if f.Type.Width < 0 { - Fatal("invalid width %d", f.Type.Width) - } - w = f.Type.Width - if f.Type.Align > 0 { - o = Rnd(o, int64(f.Type.Align)) - } - f.Width = o // really offset for TFIELD - if f.Nname != nil { - // this same stackparam logic is in addrescapes - // 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 - f.Nname.Xoffset = 0 - } else { - f.Nname.Xoffset = o - } - } - - if w == 0 { - lastzero = o - } - o += w - if o >= Thearch.MAXWIDTH { - Yyerror("type %v too large", Tconv(errtype, obj.FmtLong)) - o = 8 // small but nonzero - } - } - - // For nonzero-sized structs which end in a zero-sized thing, we add - // an extra byte of padding to the type. This padding ensures that - // taking the address of the zero-sized thing can't manufacture a - // pointer to the next object in the heap. See issue 9401. - if flag == 1 && o > starto && o == lastzero { - o++ - } - - // final width is rounded - if flag != 0 { - o = Rnd(o, int64(maxalign)) - } - t.Align = uint8(maxalign) - - // type width only includes back to first field's offset - t.Width = o - starto - - return o -} - -func dowidth(t *Type) { - if Widthptr == 0 { - Fatal("dowidth without betypeinit") - } - - if t == nil { - return - } - - if t.Width > 0 { - return - } - - if t.Width == -2 { - lno := int(lineno) - lineno = int32(t.Lineno) - if t.Broke == 0 { - t.Broke = 1 - Yyerror("invalid recursive type %v", t) - } - - t.Width = 0 - lineno = int32(lno) - return - } - - // break infinite recursion if the broken recursive type - // is referenced again - if t.Broke != 0 && t.Width == 0 { - return - } - - // defer checkwidth calls until after we're done - defercalc++ - - lno := int(lineno) - lineno = int32(t.Lineno) - t.Width = -2 - t.Align = 0 - - et := int32(t.Etype) - switch et { - case TFUNC, TCHAN, TMAP, TSTRING: - break - - /* simtype == 0 during bootstrap */ - default: - if Simtype[t.Etype] != 0 { - et = int32(Simtype[t.Etype]) - } - } - - w := int64(0) - switch et { - default: - Fatal("dowidth: unknown type: %v", t) - - /* compiler-specific stuff */ - case TINT8, TUINT8, TBOOL: - // bool is int8 - w = 1 - - case TINT16, TUINT16: - w = 2 - - case TINT32, TUINT32, TFLOAT32: - w = 4 - - case TINT64, TUINT64, TFLOAT64, TCOMPLEX64: - w = 8 - t.Align = uint8(Widthreg) - - case TCOMPLEX128: - w = 16 - t.Align = uint8(Widthreg) - - case TPTR32: - w = 4 - checkwidth(t.Type) - - case TPTR64: - w = 8 - checkwidth(t.Type) - - case TUNSAFEPTR: - w = int64(Widthptr) - - case TINTER: // implemented as 2 pointers - w = 2 * int64(Widthptr) - - t.Align = uint8(Widthptr) - offmod(t) - - case TCHAN: // implemented as pointer - w = int64(Widthptr) - - checkwidth(t.Type) - - // make fake type to check later to - // trigger channel argument check. - t1 := typ(TCHANARGS) - - t1.Type = t - checkwidth(t1) - - case TCHANARGS: - t1 := t.Type - dowidth(t.Type) // just in case - if t1.Type.Width >= 1<<16 { - Yyerror("channel element type too large (>64kB)") - } - t.Width = 1 - - case TMAP: // implemented as pointer - w = int64(Widthptr) - - checkwidth(t.Type) - checkwidth(t.Down) - - case TFORW: // should have been filled in - if t.Broke == 0 { - Yyerror("invalid recursive type %v", t) - } - w = 1 // anything will do - - // dummy type; should be replaced before use. - case TANY: - if Debug['A'] == 0 { - Fatal("dowidth any") - } - w = 1 // anything will do - - case TSTRING: - if sizeof_String == 0 { - Fatal("early dowidth string") - } - w = int64(sizeof_String) - t.Align = uint8(Widthptr) - - case TARRAY: - if t.Type == nil { - break - } - if t.Bound >= 0 { - dowidth(t.Type) - if t.Type.Width != 0 { - cap := (uint64(Thearch.MAXWIDTH) - 1) / uint64(t.Type.Width) - if uint64(t.Bound) > cap { - Yyerror("type %v larger than address space", Tconv(t, obj.FmtLong)) - } - } - - w = t.Bound * t.Type.Width - t.Align = t.Type.Align - } else if t.Bound == -1 { - w = int64(sizeof_Array) - checkwidth(t.Type) - t.Align = uint8(Widthptr) - } else if t.Bound == -100 { - if t.Broke == 0 { - Yyerror("use of [...] array outside of array literal") - t.Broke = 1 - } - } else { - Fatal("dowidth %v", t) // probably [...]T - } - - case TSTRUCT: - if t.Funarg != 0 { - Fatal("dowidth fn struct %v", t) - } - w = widstruct(t, t, 0, 1) - - // make fake type to check later to - // trigger function argument computation. - case TFUNC: - t1 := typ(TFUNCARGS) - - t1.Type = t - checkwidth(t1) - - // width of func type is pointer - w = int64(Widthptr) - - // function is 3 cated structures; - // compute their widths as side-effect. - case TFUNCARGS: - t1 := t.Type - - w = widstruct(t.Type, *getthis(t1), 0, 0) - w = widstruct(t.Type, *getinarg(t1), w, Widthreg) - w = widstruct(t.Type, *Getoutarg(t1), w, Widthreg) - t1.Argwid = w - if w%int64(Widthreg) != 0 { - Warn("bad type %v %d\n", t1, w) - } - t.Align = 1 - } - - if Widthptr == 4 && w != int64(int32(w)) { - Yyerror("type %v too large", t) - } - - t.Width = w - if t.Align == 0 { - if w > 8 || w&(w-1) != 0 { - Fatal("invalid alignment for %v", t) - } - t.Align = uint8(w) - } - - lineno = int32(lno) - - if defercalc == 1 { - resumecheckwidth() - } else { - defercalc-- - } -} - -/* - * when a type's width should be known, we call checkwidth - * to compute it. during a declaration like - * - * type T *struct { next T } - * - * it is necessary to defer the calculation of the struct width - * until after T has been initialized to be a pointer to that struct. - * similarly, during import processing structs may be used - * before their definition. in those situations, calling - * defercheckwidth() stops width calculations until - * resumecheckwidth() is called, at which point all the - * checkwidths that were deferred are executed. - * dowidth should only be called when the type's size - * is needed immediately. checkwidth makes sure the - * size is evaluated eventually. - */ -type TypeList struct { - t *Type - next *TypeList -} - -var tlfree *TypeList - -var tlq *TypeList - -func checkwidth(t *Type) { - if t == nil { - return - } - - // function arg structs should not be checked - // outside of the enclosing function. - if t.Funarg != 0 { - Fatal("checkwidth %v", t) - } - - if defercalc == 0 { - dowidth(t) - return - } - - if t.Deferwidth != 0 { - return - } - t.Deferwidth = 1 - - l := tlfree - if l != nil { - tlfree = l.next - } else { - l = new(TypeList) - } - - l.t = t - l.next = tlq - tlq = l -} - -func defercheckwidth() { - // we get out of sync on syntax errors, so don't be pedantic. - if defercalc != 0 && nerrors == 0 { - Fatal("defercheckwidth") - } - defercalc = 1 -} - -func resumecheckwidth() { - if defercalc == 0 { - Fatal("resumecheckwidth") - } - for l := tlq; l != nil; l = tlq { - l.t.Deferwidth = 0 - tlq = l.next - dowidth(l.t) - l.next = tlfree - tlfree = l - } - - defercalc = 0 -} - -var itable *Type // distinguished *byte - -func typeinit() { - if Widthptr == 0 { - Fatal("typeinit before betypeinit") - } - - for i := 0; i < NTYPE; i++ { - Simtype[i] = uint8(i) - } - - Types[TPTR32] = typ(TPTR32) - dowidth(Types[TPTR32]) - - Types[TPTR64] = typ(TPTR64) - dowidth(Types[TPTR64]) - - t := typ(TUNSAFEPTR) - Types[TUNSAFEPTR] = t - t.Sym = Pkglookup("Pointer", unsafepkg) - t.Sym.Def = typenod(t) - - dowidth(Types[TUNSAFEPTR]) - - Tptr = TPTR32 - if Widthptr == 8 { - Tptr = TPTR64 - } - - for i := TINT8; i <= TUINT64; i++ { - Isint[i] = true - } - Isint[TINT] = true - Isint[TUINT] = true - Isint[TUINTPTR] = true - - Isfloat[TFLOAT32] = true - Isfloat[TFLOAT64] = true - - Iscomplex[TCOMPLEX64] = true - Iscomplex[TCOMPLEX128] = true - - Isptr[TPTR32] = true - Isptr[TPTR64] = true - - isforw[TFORW] = true - - Issigned[TINT] = true - Issigned[TINT8] = true - Issigned[TINT16] = true - Issigned[TINT32] = true - Issigned[TINT64] = true - - /* - * initialize okfor - */ - for i := 0; i < NTYPE; i++ { - if Isint[i] || i == TIDEAL { - okforeq[i] = true - okforcmp[i] = true - okforarith[i] = true - okforadd[i] = true - okforand[i] = true - okforconst[i] = true - issimple[i] = true - Minintval[i] = new(Mpint) - Maxintval[i] = new(Mpint) - } - - if Isfloat[i] { - okforeq[i] = true - okforcmp[i] = true - okforadd[i] = true - okforarith[i] = true - okforconst[i] = true - issimple[i] = true - minfltval[i] = newMpflt() - maxfltval[i] = newMpflt() - } - - if Iscomplex[i] { - okforeq[i] = true - okforadd[i] = true - okforarith[i] = true - okforconst[i] = true - issimple[i] = true - } - } - - issimple[TBOOL] = true - - okforadd[TSTRING] = true - - okforbool[TBOOL] = true - - okforcap[TARRAY] = true - okforcap[TCHAN] = true - - okforconst[TBOOL] = true - okforconst[TSTRING] = true - - okforlen[TARRAY] = true - okforlen[TCHAN] = true - okforlen[TMAP] = true - okforlen[TSTRING] = true - - okforeq[TPTR32] = true - okforeq[TPTR64] = true - okforeq[TUNSAFEPTR] = true - okforeq[TINTER] = true - okforeq[TCHAN] = true - okforeq[TSTRING] = true - okforeq[TBOOL] = true - okforeq[TMAP] = true // nil only; refined in typecheck - okforeq[TFUNC] = true // nil only; refined in typecheck - okforeq[TARRAY] = true // nil slice only; refined in typecheck - okforeq[TSTRUCT] = true // it's complicated; refined in typecheck - - okforcmp[TSTRING] = true - - var i int - for i = 0; i < len(okfor); i++ { - okfor[i] = okfornone[:] - } - - // binary - okfor[OADD] = okforadd[:] - - okfor[OAND] = okforand[:] - okfor[OANDAND] = okforbool[:] - okfor[OANDNOT] = okforand[:] - okfor[ODIV] = okforarith[:] - okfor[OEQ] = okforeq[:] - okfor[OGE] = okforcmp[:] - okfor[OGT] = okforcmp[:] - okfor[OLE] = okforcmp[:] - okfor[OLT] = okforcmp[:] - okfor[OMOD] = okforand[:] - okfor[OMUL] = okforarith[:] - okfor[ONE] = okforeq[:] - okfor[OOR] = okforand[:] - okfor[OOROR] = okforbool[:] - okfor[OSUB] = okforarith[:] - okfor[OXOR] = okforand[:] - okfor[OLSH] = okforand[:] - okfor[ORSH] = okforand[:] - - // unary - okfor[OCOM] = okforand[:] - - okfor[OMINUS] = okforarith[:] - okfor[ONOT] = okforbool[:] - okfor[OPLUS] = okforarith[:] - - // special - okfor[OCAP] = okforcap[:] - - okfor[OLEN] = okforlen[:] - - // comparison - iscmp[OLT] = true - - iscmp[OGT] = true - iscmp[OGE] = true - iscmp[OLE] = true - iscmp[OEQ] = true - iscmp[ONE] = true - - mpatofix(Maxintval[TINT8], "0x7f") - mpatofix(Minintval[TINT8], "-0x80") - mpatofix(Maxintval[TINT16], "0x7fff") - mpatofix(Minintval[TINT16], "-0x8000") - mpatofix(Maxintval[TINT32], "0x7fffffff") - mpatofix(Minintval[TINT32], "-0x80000000") - mpatofix(Maxintval[TINT64], "0x7fffffffffffffff") - mpatofix(Minintval[TINT64], "-0x8000000000000000") - - mpatofix(Maxintval[TUINT8], "0xff") - mpatofix(Maxintval[TUINT16], "0xffff") - mpatofix(Maxintval[TUINT32], "0xffffffff") - mpatofix(Maxintval[TUINT64], "0xffffffffffffffff") - - /* f is valid float if min < f < max. (min and max are not themselves valid.) */ - mpatoflt(maxfltval[TFLOAT32], "33554431p103") /* 2^24-1 p (127-23) + 1/2 ulp*/ - mpatoflt(minfltval[TFLOAT32], "-33554431p103") - mpatoflt(maxfltval[TFLOAT64], "18014398509481983p970") /* 2^53-1 p (1023-52) + 1/2 ulp */ - mpatoflt(minfltval[TFLOAT64], "-18014398509481983p970") - - maxfltval[TCOMPLEX64] = maxfltval[TFLOAT32] - minfltval[TCOMPLEX64] = minfltval[TFLOAT32] - maxfltval[TCOMPLEX128] = maxfltval[TFLOAT64] - minfltval[TCOMPLEX128] = minfltval[TFLOAT64] - - /* for walk to use in error messages */ - Types[TFUNC] = functype(nil, nil, nil) - - /* types used in front end */ - // types[TNIL] got set early in lexinit - Types[TIDEAL] = typ(TIDEAL) - - Types[TINTER] = typ(TINTER) - - /* simple aliases */ - Simtype[TMAP] = uint8(Tptr) - - Simtype[TCHAN] = uint8(Tptr) - Simtype[TFUNC] = uint8(Tptr) - Simtype[TUNSAFEPTR] = uint8(Tptr) - - /* pick up the backend thearch.typedefs */ - var s1 *Sym - var etype int - var sameas int - var s *Sym - for i = range Thearch.Typedefs { - s = Lookup(Thearch.Typedefs[i].Name) - s1 = Pkglookup(Thearch.Typedefs[i].Name, builtinpkg) - - etype = Thearch.Typedefs[i].Etype - if etype < 0 || etype >= len(Types) { - Fatal("typeinit: %s bad etype", s.Name) - } - sameas = Thearch.Typedefs[i].Sameas - if sameas < 0 || sameas >= len(Types) { - Fatal("typeinit: %s bad sameas", s.Name) - } - Simtype[etype] = uint8(sameas) - minfltval[etype] = minfltval[sameas] - maxfltval[etype] = maxfltval[sameas] - Minintval[etype] = Minintval[sameas] - Maxintval[etype] = Maxintval[sameas] - - t = Types[etype] - if t != nil { - Fatal("typeinit: %s already defined", s.Name) - } - - t = typ(etype) - t.Sym = s1 - - dowidth(t) - Types[etype] = t - s1.Def = typenod(t) - } - - Array_array = int(Rnd(0, int64(Widthptr))) - Array_nel = int(Rnd(int64(Array_array)+int64(Widthptr), int64(Widthint))) - Array_cap = int(Rnd(int64(Array_nel)+int64(Widthint), int64(Widthint))) - sizeof_Array = int(Rnd(int64(Array_cap)+int64(Widthint), int64(Widthptr))) - - // string is same as slice wo the cap - sizeof_String = int(Rnd(int64(Array_nel)+int64(Widthint), int64(Widthptr))) - - dowidth(Types[TSTRING]) - dowidth(idealstring) - - itable = typ(Tptr) - itable.Type = Types[TUINT8] -} - -/* - * compute total size of f's in/out arguments. - */ -func Argsize(t *Type) int { - var save Iter - var x int64 - - w := int64(0) - - fp := Structfirst(&save, Getoutarg(t)) - for fp != nil { - x = fp.Width + fp.Type.Width - if x > w { - w = x - } - fp = structnext(&save) - } - - fp = funcfirst(&save, t) - for fp != nil { - x = fp.Width + fp.Type.Width - if x > w { - w = x - } - fp = funcnext(&save) - } - - w = (w + int64(Widthptr) - 1) &^ (int64(Widthptr) - 1) - if int64(int(w)) != w { - Fatal("argsize too big") - } - return int(w) -} diff --git a/src/cmd/internal/gc/big/accuracy_string.go b/src/cmd/internal/gc/big/accuracy_string.go deleted file mode 100644 index 24ef7f1077..0000000000 --- a/src/cmd/internal/gc/big/accuracy_string.go +++ /dev/null @@ -1,17 +0,0 @@ -// generated by stringer -type=Accuracy; DO NOT EDIT - -package big - -import "fmt" - -const _Accuracy_name = "BelowExactAbove" - -var _Accuracy_index = [...]uint8{0, 5, 10, 15} - -func (i Accuracy) String() string { - i -= -1 - if i < 0 || i+1 >= Accuracy(len(_Accuracy_index)) { - return fmt.Sprintf("Accuracy(%d)", i+-1) - } - return _Accuracy_name[_Accuracy_index[i]:_Accuracy_index[i+1]] -} diff --git a/src/cmd/internal/gc/big/arith.go b/src/cmd/internal/gc/big/arith.go deleted file mode 100644 index 328c85c4f7..0000000000 --- a/src/cmd/internal/gc/big/arith.go +++ /dev/null @@ -1,291 +0,0 @@ -// Copyright 2009 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. - -// This file provides Go implementations of elementary multi-precision -// arithmetic operations on word vectors. Needed for platforms without -// assembly implementations of these routines. - -package big - -// A Word represents a single digit of a multi-precision unsigned integer. -type Word uintptr - -const ( - // Compute the size _S of a Word in bytes. - _m = ^Word(0) - _logS = _m>>8&1 + _m>>16&1 + _m>>32&1 - _S = 1 << _logS - - _W = _S << 3 // word size in bits - _B = 1 << _W // digit base - _M = _B - 1 // digit mask - - _W2 = _W / 2 // half word size in bits - _B2 = 1 << _W2 // half digit base - _M2 = _B2 - 1 // half digit mask -) - -// ---------------------------------------------------------------------------- -// Elementary operations on words -// -// These operations are used by the vector operations below. - -// z1<<_W + z0 = x+y+c, with c == 0 or 1 -func addWW_g(x, y, c Word) (z1, z0 Word) { - yc := y + c - z0 = x + yc - if z0 < x || yc < y { - z1 = 1 - } - return -} - -// z1<<_W + z0 = x-y-c, with c == 0 or 1 -func subWW_g(x, y, c Word) (z1, z0 Word) { - yc := y + c - z0 = x - yc - if z0 > x || yc < y { - z1 = 1 - } - return -} - -// z1<<_W + z0 = x*y -// Adapted from Warren, Hacker's Delight, p. 132. -func mulWW_g(x, y Word) (z1, z0 Word) { - x0 := x & _M2 - x1 := x >> _W2 - y0 := y & _M2 - y1 := y >> _W2 - w0 := x0 * y0 - t := x1*y0 + w0>>_W2 - w1 := t & _M2 - w2 := t >> _W2 - w1 += x0 * y1 - z1 = x1*y1 + w2 + w1>>_W2 - z0 = x * y - return -} - -// z1<<_W + z0 = x*y + c -func mulAddWWW_g(x, y, c Word) (z1, z0 Word) { - z1, zz0 := mulWW_g(x, y) - if z0 = zz0 + c; z0 < zz0 { - z1++ - } - return -} - -// Length of x in bits. -func bitLen_g(x Word) (n int) { - for ; x >= 0x8000; x >>= 16 { - n += 16 - } - if x >= 0x80 { - x >>= 8 - n += 8 - } - if x >= 0x8 { - x >>= 4 - n += 4 - } - if x >= 0x2 { - x >>= 2 - n += 2 - } - if x >= 0x1 { - n++ - } - return -} - -// log2 computes the integer binary logarithm of x. -// The result is the integer n for which 2^n <= x < 2^(n+1). -// If x == 0, the result is -1. -func log2(x Word) int { - return bitLen(x) - 1 -} - -// Number of leading zeros in x. -func leadingZeros(x Word) uint { - return uint(_W - bitLen(x)) -} - -// q = (u1<<_W + u0 - r)/y -// Adapted from Warren, Hacker's Delight, p. 152. -func divWW_g(u1, u0, v Word) (q, r Word) { - if u1 >= v { - return 1<<_W - 1, 1<<_W - 1 - } - - s := leadingZeros(v) - v <<= s - - vn1 := v >> _W2 - vn0 := v & _M2 - un32 := u1<>(_W-s) - un10 := u0 << s - un1 := un10 >> _W2 - un0 := un10 & _M2 - q1 := un32 / vn1 - rhat := un32 - q1*vn1 - - for q1 >= _B2 || q1*vn0 > _B2*rhat+un1 { - q1-- - rhat += vn1 - if rhat >= _B2 { - break - } - } - - un21 := un32*_B2 + un1 - q1*v - q0 := un21 / vn1 - rhat = un21 - q0*vn1 - - for q0 >= _B2 || q0*vn0 > _B2*rhat+un0 { - q0-- - rhat += vn1 - if rhat >= _B2 { - break - } - } - - return q1*_B2 + q0, (un21*_B2 + un0 - q0*v) >> s -} - -// Keep for performance debugging. -// Using addWW_g is likely slower. -const use_addWW_g = false - -// The resulting carry c is either 0 or 1. -func addVV_g(z, x, y []Word) (c Word) { - if use_addWW_g { - for i := range z { - c, z[i] = addWW_g(x[i], y[i], c) - } - return - } - - for i, xi := range x[:len(z)] { - yi := y[i] - zi := xi + yi + c - z[i] = zi - // see "Hacker's Delight", section 2-12 (overflow detection) - c = (xi&yi | (xi|yi)&^zi) >> (_W - 1) - } - return -} - -// The resulting carry c is either 0 or 1. -func subVV_g(z, x, y []Word) (c Word) { - if use_addWW_g { - for i := range z { - c, z[i] = subWW_g(x[i], y[i], c) - } - return - } - - for i, xi := range x[:len(z)] { - yi := y[i] - zi := xi - yi - c - z[i] = zi - // see "Hacker's Delight", section 2-12 (overflow detection) - c = (yi&^xi | (yi|^xi)&zi) >> (_W - 1) - } - 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 { - c = y - for i := range z { - c, z[i] = addWW_g(x[i], c, 0) - } - return - } - - c = y - for i, xi := range x[:len(z)] { - zi := xi + c - z[i] = zi - c = xi &^ zi >> (_W - 1) - } - return -} - -func subVW_g(z, x []Word, y Word) (c Word) { - if use_addWW_g { - c = y - for i := range z { - c, z[i] = subWW_g(x[i], c, 0) - } - return - } - - c = y - for i, xi := range x[:len(z)] { - zi := xi - c - z[i] = zi - c = (zi &^ xi) >> (_W - 1) - } - return -} - -func shlVU_g(z, x []Word, s uint) (c Word) { - if n := len(z); n > 0 { - ŝ := _W - s - w1 := x[n-1] - c = w1 >> ŝ - for i := n - 1; i > 0; i-- { - w := w1 - w1 = x[i-1] - z[i] = w<>ŝ - } - z[0] = w1 << s - } - return -} - -func shrVU_g(z, x []Word, s uint) (c Word) { - if n := len(z); n > 0 { - ŝ := _W - s - w1 := x[0] - c = w1 << ŝ - for i := 0; i < n-1; i++ { - w := w1 - w1 = x[i+1] - z[i] = w>>s | w1<<ŝ - } - z[n-1] = w1 >> s - } - return -} - -func mulAddVWW_g(z, x []Word, y, r Word) (c Word) { - c = r - for i := range z { - c, z[i] = mulAddWWW_g(x[i], y, c) - } - return -} - -// TODO(gri) Remove use of addWW_g here and then we can remove addWW_g and subWW_g. -func addMulVVW_g(z, x []Word, y Word) (c Word) { - for i := range z { - z1, z0 := mulAddWWW_g(x[i], y, z[i]) - c, z[i] = addWW_g(z0, c, 0) - c += z1 - } - return -} - -func divWVW_g(z []Word, xn Word, x []Word, y Word) (r Word) { - r = xn - for i := len(z) - 1; i >= 0; i-- { - z[i], r = divWW_g(r, x[i], y) - } - return -} diff --git a/src/cmd/internal/gc/big/arith_decl.go b/src/cmd/internal/gc/big/arith_decl.go deleted file mode 100644 index fe13577203..0000000000 --- a/src/cmd/internal/gc/big/arith_decl.go +++ /dev/null @@ -1,53 +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 big - -func mulWW(x, y Word) (z1, z0 Word) { - return mulWW_g(x, y) -} - -func divWW(x1, x0, y Word) (q, r Word) { - return divWW_g(x1, x0, y) -} - -func addVV(z, x, y []Word) (c Word) { - return addVV_g(z, x, y) -} - -func subVV(z, x, y []Word) (c Word) { - return subVV_g(z, x, y) -} - -func addVW(z, x []Word, y Word) (c Word) { - return addVW_g(z, x, y) -} - -func subVW(z, x []Word, y Word) (c Word) { - return subVW_g(z, x, y) -} - -func shlVU(z, x []Word, s uint) (c Word) { - return shlVU_g(z, x, s) -} - -func shrVU(z, x []Word, s uint) (c Word) { - return shrVU_g(z, x, s) -} - -func mulAddVWW(z, x []Word, y, r Word) (c Word) { - return mulAddVWW_g(z, x, y, r) -} - -func addMulVVW(z, x []Word, y Word) (c Word) { - return addMulVVW_g(z, x, y) -} - -func divWVW(z []Word, xn Word, x []Word, y Word) (r Word) { - return divWVW_g(z, xn, x, y) -} - -func bitLen(x Word) (n int) { - return bitLen_g(x) -} diff --git a/src/cmd/internal/gc/big/arith_test.go b/src/cmd/internal/gc/big/arith_test.go deleted file mode 100644 index cd92dd7173..0000000000 --- a/src/cmd/internal/gc/big/arith_test.go +++ /dev/null @@ -1,456 +0,0 @@ -// Copyright 2009 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 big - -import ( - "math/rand" - "testing" -) - -type funWW func(x, y, c Word) (z1, z0 Word) -type argWW struct { - x, y, c, z1, z0 Word -} - -var sumWW = []argWW{ - {0, 0, 0, 0, 0}, - {0, 1, 0, 0, 1}, - {0, 0, 1, 0, 1}, - {0, 1, 1, 0, 2}, - {12345, 67890, 0, 0, 80235}, - {12345, 67890, 1, 0, 80236}, - {_M, 1, 0, 1, 0}, - {_M, 0, 1, 1, 0}, - {_M, 1, 1, 1, 1}, - {_M, _M, 0, 1, _M - 1}, - {_M, _M, 1, 1, _M}, -} - -func testFunWW(t *testing.T, msg string, f funWW, a argWW) { - z1, z0 := f(a.x, a.y, a.c) - if z1 != a.z1 || z0 != a.z0 { - t.Errorf("%s%+v\n\tgot z1:z0 = %#x:%#x; want %#x:%#x", msg, a, z1, z0, a.z1, a.z0) - } -} - -func TestFunWW(t *testing.T) { - for _, a := range sumWW { - arg := a - testFunWW(t, "addWW_g", addWW_g, arg) - - arg = argWW{a.y, a.x, a.c, a.z1, a.z0} - testFunWW(t, "addWW_g symmetric", addWW_g, arg) - - arg = argWW{a.z0, a.x, a.c, a.z1, a.y} - testFunWW(t, "subWW_g", subWW_g, arg) - - arg = argWW{a.z0, a.y, a.c, a.z1, a.x} - testFunWW(t, "subWW_g symmetric", subWW_g, arg) - } -} - -type funVV func(z, x, y []Word) (c Word) -type argVV struct { - z, x, y nat - c Word -} - -var sumVV = []argVV{ - {}, - {nat{0}, nat{0}, nat{0}, 0}, - {nat{1}, nat{1}, nat{0}, 0}, - {nat{0}, nat{_M}, nat{1}, 1}, - {nat{80235}, nat{12345}, nat{67890}, 0}, - {nat{_M - 1}, nat{_M}, nat{_M}, 1}, - {nat{0, 0, 0, 0}, nat{_M, _M, _M, _M}, nat{1, 0, 0, 0}, 1}, - {nat{0, 0, 0, _M}, nat{_M, _M, _M, _M - 1}, nat{1, 0, 0, 0}, 0}, - {nat{0, 0, 0, 0}, nat{_M, 0, _M, 0}, nat{1, _M, 0, _M}, 1}, -} - -func testFunVV(t *testing.T, msg string, f funVV, a argVV) { - z := make(nat, len(a.z)) - c := f(z, a.x, a.y) - for i, zi := range z { - if zi != a.z[i] { - t.Errorf("%s%+v\n\tgot z[%d] = %#x; want %#x", msg, a, i, zi, a.z[i]) - break - } - } - if c != a.c { - t.Errorf("%s%+v\n\tgot c = %#x; want %#x", msg, a, c, a.c) - } -} - -func TestFunVV(t *testing.T) { - for _, a := range sumVV { - arg := a - testFunVV(t, "addVV_g", addVV_g, arg) - testFunVV(t, "addVV", addVV, arg) - - arg = argVV{a.z, a.y, a.x, a.c} - testFunVV(t, "addVV_g symmetric", addVV_g, arg) - testFunVV(t, "addVV symmetric", addVV, arg) - - arg = argVV{a.x, a.z, a.y, a.c} - testFunVV(t, "subVV_g", subVV_g, arg) - testFunVV(t, "subVV", subVV, arg) - - arg = argVV{a.y, a.z, a.x, a.c} - testFunVV(t, "subVV_g symmetric", subVV_g, arg) - testFunVV(t, "subVV symmetric", subVV, arg) - } -} - -// Always the same seed for reproducible results. -var rnd = rand.New(rand.NewSource(0)) - -func rndW() Word { - return Word(rnd.Int63()<<1 | rnd.Int63n(2)) -} - -func rndV(n int) []Word { - v := make([]Word, n) - for i := range v { - v[i] = rndW() - } - return v -} - -func benchmarkFunVV(b *testing.B, f funVV, n int) { - x := rndV(n) - y := rndV(n) - z := make([]Word, n) - b.SetBytes(int64(n * _W)) - b.ResetTimer() - for i := 0; i < b.N; i++ { - f(z, x, y) - } -} - -func BenchmarkAddVV_1(b *testing.B) { benchmarkFunVV(b, addVV, 1) } -func BenchmarkAddVV_2(b *testing.B) { benchmarkFunVV(b, addVV, 2) } -func BenchmarkAddVV_3(b *testing.B) { benchmarkFunVV(b, addVV, 3) } -func BenchmarkAddVV_4(b *testing.B) { benchmarkFunVV(b, addVV, 4) } -func BenchmarkAddVV_5(b *testing.B) { benchmarkFunVV(b, addVV, 5) } -func BenchmarkAddVV_1e1(b *testing.B) { benchmarkFunVV(b, addVV, 1e1) } -func BenchmarkAddVV_1e2(b *testing.B) { benchmarkFunVV(b, addVV, 1e2) } -func BenchmarkAddVV_1e3(b *testing.B) { benchmarkFunVV(b, addVV, 1e3) } -func BenchmarkAddVV_1e4(b *testing.B) { benchmarkFunVV(b, addVV, 1e4) } -func BenchmarkAddVV_1e5(b *testing.B) { benchmarkFunVV(b, addVV, 1e5) } - -type funVW func(z, x []Word, y Word) (c Word) -type argVW struct { - z, x nat - y Word - c Word -} - -var sumVW = []argVW{ - {}, - {nil, nil, 2, 2}, - {nat{0}, nat{0}, 0, 0}, - {nat{1}, nat{0}, 1, 0}, - {nat{1}, nat{1}, 0, 0}, - {nat{0}, nat{_M}, 1, 1}, - {nat{0, 0, 0, 0}, nat{_M, _M, _M, _M}, 1, 1}, -} - -var prodVW = []argVW{ - {}, - {nat{0}, nat{0}, 0, 0}, - {nat{0}, nat{_M}, 0, 0}, - {nat{0}, nat{0}, _M, 0}, - {nat{1}, nat{1}, 1, 0}, - {nat{22793}, nat{991}, 23, 0}, - {nat{0, 0, 0, 22793}, nat{0, 0, 0, 991}, 23, 0}, - {nat{0, 0, 0, 0}, nat{7893475, 7395495, 798547395, 68943}, 0, 0}, - {nat{0, 0, 0, 0}, nat{0, 0, 0, 0}, 894375984, 0}, - {nat{_M << 1 & _M}, nat{_M}, 1 << 1, _M >> (_W - 1)}, - {nat{_M << 7 & _M}, nat{_M}, 1 << 7, _M >> (_W - 7)}, - {nat{_M << 7 & _M, _M, _M, _M}, nat{_M, _M, _M, _M}, 1 << 7, _M >> (_W - 7)}, -} - -var lshVW = []argVW{ - {}, - {nat{0}, nat{0}, 0, 0}, - {nat{0}, nat{0}, 1, 0}, - {nat{0}, nat{0}, 20, 0}, - - {nat{_M}, nat{_M}, 0, 0}, - {nat{_M << 1 & _M}, nat{_M}, 1, 1}, - {nat{_M << 20 & _M}, nat{_M}, 20, _M >> (_W - 20)}, - - {nat{_M, _M, _M}, nat{_M, _M, _M}, 0, 0}, - {nat{_M << 1 & _M, _M, _M}, nat{_M, _M, _M}, 1, 1}, - {nat{_M << 20 & _M, _M, _M}, nat{_M, _M, _M}, 20, _M >> (_W - 20)}, -} - -var rshVW = []argVW{ - {}, - {nat{0}, nat{0}, 0, 0}, - {nat{0}, nat{0}, 1, 0}, - {nat{0}, nat{0}, 20, 0}, - - {nat{_M}, nat{_M}, 0, 0}, - {nat{_M >> 1}, nat{_M}, 1, _M << (_W - 1) & _M}, - {nat{_M >> 20}, nat{_M}, 20, _M << (_W - 20) & _M}, - - {nat{_M, _M, _M}, nat{_M, _M, _M}, 0, 0}, - {nat{_M, _M, _M >> 1}, nat{_M, _M, _M}, 1, _M << (_W - 1) & _M}, - {nat{_M, _M, _M >> 20}, nat{_M, _M, _M}, 20, _M << (_W - 20) & _M}, -} - -func testFunVW(t *testing.T, msg string, f funVW, a argVW) { - z := make(nat, len(a.z)) - c := f(z, a.x, a.y) - for i, zi := range z { - if zi != a.z[i] { - t.Errorf("%s%+v\n\tgot z[%d] = %#x; want %#x", msg, a, i, zi, a.z[i]) - break - } - } - if c != a.c { - t.Errorf("%s%+v\n\tgot c = %#x; want %#x", msg, a, c, a.c) - } -} - -func makeFunVW(f func(z, x []Word, s uint) (c Word)) funVW { - return func(z, x []Word, s Word) (c Word) { - return f(z, x, uint(s)) - } -} - -func TestFunVW(t *testing.T) { - for _, a := range sumVW { - arg := a - testFunVW(t, "addVW_g", addVW_g, arg) - testFunVW(t, "addVW", addVW, arg) - - arg = argVW{a.x, a.z, a.y, a.c} - testFunVW(t, "subVW_g", subVW_g, arg) - testFunVW(t, "subVW", subVW, arg) - } - - shlVW_g := makeFunVW(shlVU_g) - shlVW := makeFunVW(shlVU) - for _, a := range lshVW { - arg := a - testFunVW(t, "shlVU_g", shlVW_g, arg) - testFunVW(t, "shlVU", shlVW, arg) - } - - shrVW_g := makeFunVW(shrVU_g) - shrVW := makeFunVW(shrVU) - for _, a := range rshVW { - arg := a - testFunVW(t, "shrVU_g", shrVW_g, arg) - testFunVW(t, "shrVU", shrVW, arg) - } -} - -func benchmarkFunVW(b *testing.B, f funVW, n int) { - x := rndV(n) - y := rndW() - z := make([]Word, n) - b.SetBytes(int64(n * _S)) - b.ResetTimer() - for i := 0; i < b.N; i++ { - f(z, x, y) - } -} - -func BenchmarkAddVW_1(b *testing.B) { benchmarkFunVW(b, addVW, 1) } -func BenchmarkAddVW_2(b *testing.B) { benchmarkFunVW(b, addVW, 2) } -func BenchmarkAddVW_3(b *testing.B) { benchmarkFunVW(b, addVW, 3) } -func BenchmarkAddVW_4(b *testing.B) { benchmarkFunVW(b, addVW, 4) } -func BenchmarkAddVW_5(b *testing.B) { benchmarkFunVW(b, addVW, 5) } -func BenchmarkAddVW_1e1(b *testing.B) { benchmarkFunVW(b, addVW, 1e1) } -func BenchmarkAddVW_1e2(b *testing.B) { benchmarkFunVW(b, addVW, 1e2) } -func BenchmarkAddVW_1e3(b *testing.B) { benchmarkFunVW(b, addVW, 1e3) } -func BenchmarkAddVW_1e4(b *testing.B) { benchmarkFunVW(b, addVW, 1e4) } -func BenchmarkAddVW_1e5(b *testing.B) { benchmarkFunVW(b, addVW, 1e5) } - -type funVWW func(z, x []Word, y, r Word) (c Word) -type argVWW struct { - z, x nat - y, r Word - c Word -} - -var prodVWW = []argVWW{ - {}, - {nat{0}, nat{0}, 0, 0, 0}, - {nat{991}, nat{0}, 0, 991, 0}, - {nat{0}, nat{_M}, 0, 0, 0}, - {nat{991}, nat{_M}, 0, 991, 0}, - {nat{0}, nat{0}, _M, 0, 0}, - {nat{991}, nat{0}, _M, 991, 0}, - {nat{1}, nat{1}, 1, 0, 0}, - {nat{992}, nat{1}, 1, 991, 0}, - {nat{22793}, nat{991}, 23, 0, 0}, - {nat{22800}, nat{991}, 23, 7, 0}, - {nat{0, 0, 0, 22793}, nat{0, 0, 0, 991}, 23, 0, 0}, - {nat{7, 0, 0, 22793}, nat{0, 0, 0, 991}, 23, 7, 0}, - {nat{0, 0, 0, 0}, nat{7893475, 7395495, 798547395, 68943}, 0, 0, 0}, - {nat{991, 0, 0, 0}, nat{7893475, 7395495, 798547395, 68943}, 0, 991, 0}, - {nat{0, 0, 0, 0}, nat{0, 0, 0, 0}, 894375984, 0, 0}, - {nat{991, 0, 0, 0}, nat{0, 0, 0, 0}, 894375984, 991, 0}, - {nat{_M << 1 & _M}, nat{_M}, 1 << 1, 0, _M >> (_W - 1)}, - {nat{_M<<1&_M + 1}, nat{_M}, 1 << 1, 1, _M >> (_W - 1)}, - {nat{_M << 7 & _M}, nat{_M}, 1 << 7, 0, _M >> (_W - 7)}, - {nat{_M<<7&_M + 1<<6}, nat{_M}, 1 << 7, 1 << 6, _M >> (_W - 7)}, - {nat{_M << 7 & _M, _M, _M, _M}, nat{_M, _M, _M, _M}, 1 << 7, 0, _M >> (_W - 7)}, - {nat{_M<<7&_M + 1<<6, _M, _M, _M}, nat{_M, _M, _M, _M}, 1 << 7, 1 << 6, _M >> (_W - 7)}, -} - -func testFunVWW(t *testing.T, msg string, f funVWW, a argVWW) { - z := make(nat, len(a.z)) - c := f(z, a.x, a.y, a.r) - for i, zi := range z { - if zi != a.z[i] { - t.Errorf("%s%+v\n\tgot z[%d] = %#x; want %#x", msg, a, i, zi, a.z[i]) - break - } - } - if c != a.c { - t.Errorf("%s%+v\n\tgot c = %#x; want %#x", msg, a, c, a.c) - } -} - -// TODO(gri) mulAddVWW and divWVW are symmetric operations but -// their signature is not symmetric. Try to unify. - -type funWVW func(z []Word, xn Word, x []Word, y Word) (r Word) -type argWVW struct { - z nat - xn Word - x nat - y Word - r Word -} - -func testFunWVW(t *testing.T, msg string, f funWVW, a argWVW) { - z := make(nat, len(a.z)) - r := f(z, a.xn, a.x, a.y) - for i, zi := range z { - if zi != a.z[i] { - t.Errorf("%s%+v\n\tgot z[%d] = %#x; want %#x", msg, a, i, zi, a.z[i]) - break - } - } - if r != a.r { - t.Errorf("%s%+v\n\tgot r = %#x; want %#x", msg, a, r, a.r) - } -} - -func TestFunVWW(t *testing.T) { - for _, a := range prodVWW { - arg := a - testFunVWW(t, "mulAddVWW_g", mulAddVWW_g, arg) - testFunVWW(t, "mulAddVWW", mulAddVWW, arg) - - if a.y != 0 && a.r < a.y { - arg := argWVW{a.x, a.c, a.z, a.y, a.r} - testFunWVW(t, "divWVW_g", divWVW_g, arg) - testFunWVW(t, "divWVW", divWVW, arg) - } - } -} - -var mulWWTests = []struct { - x, y Word - q, r Word -}{ - {_M, _M, _M - 1, 1}, - // 32 bit only: {0xc47dfa8c, 50911, 0x98a4, 0x998587f4}, -} - -func TestMulWW(t *testing.T) { - for i, test := range mulWWTests { - q, r := mulWW_g(test.x, test.y) - if q != test.q || r != test.r { - t.Errorf("#%d got (%x, %x) want (%x, %x)", i, q, r, test.q, test.r) - } - } -} - -var mulAddWWWTests = []struct { - x, y, c Word - q, r Word -}{ - // TODO(agl): These will only work on 64-bit platforms. - // {15064310297182388543, 0xe7df04d2d35d5d80, 13537600649892366549, 13644450054494335067, 10832252001440893781}, - // {15064310297182388543, 0xdab2f18048baa68d, 13644450054494335067, 12869334219691522700, 14233854684711418382}, - {_M, _M, 0, _M - 1, 1}, - {_M, _M, _M, _M, 0}, -} - -func TestMulAddWWW(t *testing.T) { - for i, test := range mulAddWWWTests { - q, r := mulAddWWW_g(test.x, test.y, test.c) - if q != test.q || r != test.r { - t.Errorf("#%d got (%x, %x) want (%x, %x)", i, q, r, test.q, test.r) - } - } -} - -func benchmarkAddMulVVW(b *testing.B, n int) { - x := rndV(n) - y := rndW() - z := make([]Word, n) - b.SetBytes(int64(n * _W)) - b.ResetTimer() - for i := 0; i < b.N; i++ { - addMulVVW(z, x, y) - } -} - -func BenchmarkAddMulVVW_1(b *testing.B) { benchmarkAddMulVVW(b, 1) } -func BenchmarkAddMulVVW_2(b *testing.B) { benchmarkAddMulVVW(b, 2) } -func BenchmarkAddMulVVW_3(b *testing.B) { benchmarkAddMulVVW(b, 3) } -func BenchmarkAddMulVVW_4(b *testing.B) { benchmarkAddMulVVW(b, 4) } -func BenchmarkAddMulVVW_5(b *testing.B) { benchmarkAddMulVVW(b, 5) } -func BenchmarkAddMulVVW_1e1(b *testing.B) { benchmarkAddMulVVW(b, 1e1) } -func BenchmarkAddMulVVW_1e2(b *testing.B) { benchmarkAddMulVVW(b, 1e2) } -func BenchmarkAddMulVVW_1e3(b *testing.B) { benchmarkAddMulVVW(b, 1e3) } -func BenchmarkAddMulVVW_1e4(b *testing.B) { benchmarkAddMulVVW(b, 1e4) } -func BenchmarkAddMulVVW_1e5(b *testing.B) { benchmarkAddMulVVW(b, 1e5) } - -func testWordBitLen(t *testing.T, fname string, f func(Word) int) { - for i := 0; i <= _W; i++ { - x := Word(1) << uint(i-1) // i == 0 => x == 0 - n := f(x) - if n != i { - t.Errorf("got %d; want %d for %s(%#x)", n, i, fname, x) - } - } -} - -func TestWordBitLen(t *testing.T) { - testWordBitLen(t, "bitLen", bitLen) - testWordBitLen(t, "bitLen_g", bitLen_g) -} - -// runs b.N iterations of bitLen called on a Word containing (1 << nbits)-1. -func benchmarkBitLenN(b *testing.B, nbits uint) { - testword := Word((uint64(1) << nbits) - 1) - for i := 0; i < b.N; i++ { - bitLen(testword) - } -} - -// Individual bitLen tests. Numbers chosen to examine both sides -// of powers-of-two boundaries. -func BenchmarkBitLen0(b *testing.B) { benchmarkBitLenN(b, 0) } -func BenchmarkBitLen1(b *testing.B) { benchmarkBitLenN(b, 1) } -func BenchmarkBitLen2(b *testing.B) { benchmarkBitLenN(b, 2) } -func BenchmarkBitLen3(b *testing.B) { benchmarkBitLenN(b, 3) } -func BenchmarkBitLen4(b *testing.B) { benchmarkBitLenN(b, 4) } -func BenchmarkBitLen5(b *testing.B) { benchmarkBitLenN(b, 5) } -func BenchmarkBitLen8(b *testing.B) { benchmarkBitLenN(b, 8) } -func BenchmarkBitLen9(b *testing.B) { benchmarkBitLenN(b, 9) } -func BenchmarkBitLen16(b *testing.B) { benchmarkBitLenN(b, 16) } -func BenchmarkBitLen17(b *testing.B) { benchmarkBitLenN(b, 17) } -func BenchmarkBitLen31(b *testing.B) { benchmarkBitLenN(b, 31) } diff --git a/src/cmd/internal/gc/big/bits_test.go b/src/cmd/internal/gc/big/bits_test.go deleted file mode 100644 index 3ce24222d7..0000000000 --- a/src/cmd/internal/gc/big/bits_test.go +++ /dev/null @@ -1,224 +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. - -// This file implements the Bits type used for testing Float operations -// via an independent (albeit slower) representations for floating-point -// numbers. - -package big - -import ( - "fmt" - "sort" - "testing" -) - -// A Bits value b represents a finite floating-point number x of the form -// -// x = 2**b[0] + 2**b[1] + ... 2**b[len(b)-1] -// -// The order of slice elements is not significant. Negative elements may be -// used to form fractions. A Bits value is normalized if each b[i] occurs at -// most once. For instance Bits{0, 0, 1} is not normalized but represents the -// same floating-point number as Bits{2}, which is normalized. The zero (nil) -// value of Bits is a ready to use Bits value and represents the value 0. -type Bits []int - -func (x Bits) add(y Bits) Bits { - return append(x, y...) -} - -func (x Bits) mul(y Bits) Bits { - var p Bits - for _, x := range x { - for _, y := range y { - p = append(p, x+y) - } - } - return p -} - -func TestMulBits(t *testing.T) { - for _, test := range []struct { - x, y, want Bits - }{ - {nil, nil, nil}, - {Bits{}, Bits{}, nil}, - {Bits{0}, Bits{0}, Bits{0}}, - {Bits{0}, Bits{1}, Bits{1}}, - {Bits{1}, Bits{1, 2, 3}, Bits{2, 3, 4}}, - {Bits{-1}, Bits{1}, Bits{0}}, - {Bits{-10, -1, 0, 1, 10}, Bits{1, 2, 3}, Bits{-9, -8, -7, 0, 1, 2, 1, 2, 3, 2, 3, 4, 11, 12, 13}}, - } { - got := fmt.Sprintf("%v", test.x.mul(test.y)) - want := fmt.Sprintf("%v", test.want) - if got != want { - t.Errorf("%v * %v = %s; want %s", test.x, test.y, got, want) - } - - } -} - -// norm returns the normalized bits for x: It removes multiple equal entries -// by treating them as an addition (e.g., Bits{5, 5} => Bits{6}), and it sorts -// the result list for reproducible results. -func (x Bits) norm() Bits { - m := make(map[int]bool) - for _, b := range x { - for m[b] { - m[b] = false - b++ - } - m[b] = true - } - var z Bits - for b, set := range m { - if set { - z = append(z, b) - } - } - sort.Ints([]int(z)) - return z -} - -func TestNormBits(t *testing.T) { - for _, test := range []struct { - x, want Bits - }{ - {nil, nil}, - {Bits{}, Bits{}}, - {Bits{0}, Bits{0}}, - {Bits{0, 0}, Bits{1}}, - {Bits{3, 1, 1}, Bits{2, 3}}, - {Bits{10, 9, 8, 7, 6, 6}, Bits{11}}, - } { - got := fmt.Sprintf("%v", test.x.norm()) - want := fmt.Sprintf("%v", test.want) - if got != want { - t.Errorf("normBits(%v) = %s; want %s", test.x, got, want) - } - - } -} - -// round returns the Float value corresponding to x after rounding x -// to prec bits according to mode. -func (x Bits) round(prec uint, mode RoundingMode) *Float { - x = x.norm() - - // determine range - var min, max int - for i, b := range x { - if i == 0 || b < min { - min = b - } - if i == 0 || b > max { - max = b - } - } - prec0 := uint(max + 1 - min) - if prec >= prec0 { - return x.Float() - } - // prec < prec0 - - // determine bit 0, rounding, and sticky bit, and result bits z - var bit0, rbit, sbit uint - var z Bits - r := max - int(prec) - for _, b := range x { - switch { - case b == r: - rbit = 1 - case b < r: - sbit = 1 - default: - // b > r - if b == r+1 { - bit0 = 1 - } - z = append(z, b) - } - } - - // round - f := z.Float() // rounded to zero - if mode == ToNearestAway { - panic("not yet implemented") - } - if mode == ToNearestEven && rbit == 1 && (sbit == 1 || sbit == 0 && bit0 != 0) || mode == AwayFromZero { - // round away from zero - f.SetMode(ToZero).SetPrec(prec) - f.Add(f, Bits{int(r) + 1}.Float()) - } - return f -} - -// Float returns the *Float z of the smallest possible precision such that -// z = sum(2**bits[i]), with i = range bits. If multiple bits[i] are equal, -// they are added: Bits{0, 1, 0}.Float() == 2**0 + 2**1 + 2**0 = 4. -func (bits Bits) Float() *Float { - // handle 0 - if len(bits) == 0 { - return new(Float) - } - // len(bits) > 0 - - // determine lsb exponent - var min int - for i, b := range bits { - if i == 0 || b < min { - min = b - } - } - - // create bit pattern - x := NewInt(0) - for _, b := range bits { - badj := b - min - // propagate carry if necessary - for x.Bit(badj) != 0 { - x.SetBit(x, badj, 0) - badj++ - } - x.SetBit(x, badj, 1) - } - - // create corresponding float - z := new(Float).SetInt(x) // normalized - if e := int64(z.exp) + int64(min); MinExp <= e && e <= MaxExp { - z.exp = int32(e) - } else { - // this should never happen for our test cases - panic("exponent out of range") - } - return z -} - -func TestFromBits(t *testing.T) { - for _, test := range []struct { - bits Bits - want string - }{ - // 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"}, - - // 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 */}, - } { - f := test.bits.Float() - if got := f.Format('p', 0); got != test.want { - t.Errorf("setBits(%v) = %s; want %s", test.bits, got, test.want) - } - } -} diff --git a/src/cmd/internal/gc/big/calibrate_test.go b/src/cmd/internal/gc/big/calibrate_test.go deleted file mode 100644 index f69ffbf5cf..0000000000 --- a/src/cmd/internal/gc/big/calibrate_test.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2009 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. - -// This file prints execution times for the Mul benchmark -// given different Karatsuba thresholds. The result may be -// used to manually fine-tune the threshold constant. The -// results are somewhat fragile; use repeated runs to get -// a clear picture. - -// Usage: go test -run=TestCalibrate -calibrate - -package big - -import ( - "flag" - "fmt" - "testing" - "time" -) - -var calibrate = flag.Bool("calibrate", false, "run calibration test") - -func karatsubaLoad(b *testing.B) { - BenchmarkMul(b) -} - -// measureKaratsuba returns the time to run a Karatsuba-relevant benchmark -// given Karatsuba threshold th. -func measureKaratsuba(th int) time.Duration { - th, karatsubaThreshold = karatsubaThreshold, th - res := testing.Benchmark(karatsubaLoad) - karatsubaThreshold = th - return time.Duration(res.NsPerOp()) -} - -func computeThresholds() { - fmt.Printf("Multiplication times for varying Karatsuba thresholds\n") - fmt.Printf("(run repeatedly for good results)\n") - - // determine Tk, the work load execution time using basic multiplication - Tb := measureKaratsuba(1e9) // th == 1e9 => Karatsuba multiplication disabled - fmt.Printf("Tb = %10s\n", Tb) - - // thresholds - th := 4 - th1 := -1 - th2 := -1 - - var deltaOld time.Duration - for count := -1; count != 0 && th < 128; count-- { - // determine Tk, the work load execution time using Karatsuba multiplication - Tk := measureKaratsuba(th) - - // improvement over Tb - delta := (Tb - Tk) * 100 / Tb - - fmt.Printf("th = %3d Tk = %10s %4d%%", th, Tk, delta) - - // determine break-even point - if Tk < Tb && th1 < 0 { - th1 = th - fmt.Print(" break-even point") - } - - // determine diminishing return - if 0 < delta && delta < deltaOld && th2 < 0 { - th2 = th - fmt.Print(" diminishing return") - } - deltaOld = delta - - fmt.Println() - - // trigger counter - if th1 >= 0 && th2 >= 0 && count < 0 { - count = 10 // this many extra measurements after we got both thresholds - } - - th++ - } -} - -func TestCalibrate(t *testing.T) { - if *calibrate { - computeThresholds() - } -} diff --git a/src/cmd/internal/gc/big/decimal.go b/src/cmd/internal/gc/big/decimal.go deleted file mode 100644 index 3d024dce68..0000000000 --- a/src/cmd/internal/gc/big/decimal.go +++ /dev/null @@ -1,258 +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. - -// This file implements multi-precision decimal numbers. -// The implementation is for float to decimal conversion only; -// not general purpose use. -// The only operations are precise conversion from binary to -// decimal and rounding. -// -// The key observation and some code (shr) is borrowed from -// strconv/decimal.go: conversion of binary fractional values can be done -// precisely in multi-precision decimal because 2 divides 10 (required for -// >> of mantissa); but conversion of decimal floating-point values cannot -// be done precisely in binary representation. -// -// In contrast to strconv/decimal.go, only right shift is implemented in -// decimal format - left shift can be done precisely in binary format. - -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. -type decimal struct { - mant []byte // mantissa ASCII digits, big-endian - exp int // exponent, valid if len(mant) > 0 -} - -// Maximum shift amount that can be done in one pass without overflow. -// A Word has _W bits and (1<= 0), or m >> -shift (for shift < 0). -func (x *decimal) init(m nat, shift int) { - // special case 0 - if len(m) == 0 { - x.mant = x.mant[:0] - return - } - - // Optimization: If we need to shift right, first remove any trailing - // zero bits from m to reduce shift amount that needs to be done in - // decimal format (since that is likely slower). - if shift < 0 { - ntz := m.trailingZeroBits() - s := uint(-shift) - if s >= ntz { - s = ntz // shift at most ntz bits - } - m = nat(nil).shr(m, s) - shift += int(s) - } - - // Do any shift left in binary representation. - if shift > 0 { - m = nat(nil).shl(m, uint(shift)) - shift = 0 - } - - // Convert mantissa into decimal representation. - s := m.decimalString() // TODO(gri) avoid string conversion here - n := len(s) - x.exp = n - // Trim trailing zeros; instead the exponent is tracking - // the decimal point independent of the number of digits. - for n > 0 && s[n-1] == '0' { - n-- - } - x.mant = append(x.mant[:0], s[:n]...) - - // Do any (remaining) shift right in decimal representation. - if shift < 0 { - for shift < -maxShift { - shr(x, maxShift) - shift += maxShift - } - shr(x, uint(-shift)) - } -} - -// Possibly optimization: The current implementation of nat.string takes -// a charset argument. When a right shift is needed, we could provide -// "\x00\x01...\x09" instead of "012..9" (as in nat.decimalString) and -// avoid the repeated +'0' and -'0' operations in decimal.shr (and do a -// single +'0' pass at the end). - -// shr implements x >> s, for s <= maxShift. -func shr(x *decimal, s uint) { - // Division by 1<>s == 0 && r < len(x.mant) { - ch := Word(x.mant[r]) - r++ - n = n*10 + ch - '0' - } - if n == 0 { - // x == 0; shouldn't get here, but handle anyway - x.mant = x.mant[:0] - return - } - for n>>s == 0 { - r++ - n *= 10 - } - x.exp += 1 - r - - // read a digit, write a digit - w := 0 // write index - for r < len(x.mant) { - ch := Word(x.mant[r]) - r++ - d := n >> s - n -= d << s - x.mant[w] = byte(d + '0') - w++ - n = n*10 + ch - '0' - } - - // write extra digits that still fit - for n > 0 && w < len(x.mant) { - d := n >> s - n -= d << s - x.mant[w] = byte(d + '0') - w++ - n = n * 10 - } - x.mant = x.mant[:w] // the number may be shorter (e.g. 1024 >> 10) - - // append additional digits that didn't fit - for n > 0 { - d := n >> s - n -= d << s - x.mant = append(x.mant, byte(d+'0')) - n = n * 10 - } - - trim(x) -} - -func (x *decimal) String() string { - if len(x.mant) == 0 { - return "0" - } - - var buf []byte - switch { - case x.exp <= 0: - // 0.00ddd - buf = append(buf, "0."...) - buf = appendZeros(buf, -x.exp) - buf = append(buf, x.mant...) - - case /* 0 < */ x.exp < len(x.mant): - // dd.ddd - buf = append(buf, x.mant[:x.exp]...) - buf = append(buf, '.') - buf = append(buf, x.mant[x.exp:]...) - - default: // len(x.mant) <= x.exp - // ddd00 - buf = append(buf, x.mant...) - buf = appendZeros(buf, x.exp-len(x.mant)) - } - - return string(buf) -} - -// appendZeros appends n 0 digits to buf and returns buf. -func appendZeros(buf []byte, n int) []byte { - for ; n > 0; n-- { - buf = append(buf, '0') - } - return buf -} - -// shouldRoundUp reports if x should be rounded up -// if shortened to n digits. n must be a valid index -// for x.mant. -func shouldRoundUp(x *decimal, n int) bool { - if x.mant[n] == '5' && n+1 == len(x.mant) { - // exactly halfway - round to even - return n > 0 && (x.mant[n-1]-'0')&1 != 0 - } - // not halfway - digit tells all (x.mant has no trailing zeros) - return x.mant[n] >= '5' -} - -// round sets x to (at most) n mantissa digits by rounding it -// to the nearest even value with n (or fever) mantissa digits. -// If n < 0, x remains unchanged. -func (x *decimal) round(n int) { - if n < 0 || n >= len(x.mant) { - return // nothing to do - } - - if shouldRoundUp(x, n) { - x.roundUp(n) - } else { - x.roundDown(n) - } -} - -func (x *decimal) roundUp(n int) { - if n < 0 || n >= len(x.mant) { - return // nothing to do - } - // 0 <= n < len(x.mant) - - // find first digit < '9' - for n > 0 && x.mant[n-1] >= '9' { - n-- - } - - if n == 0 { - // all digits are '9's => round up to '1' and update exponent - x.mant[0] = '1' // ok since len(x.mant) > n - x.mant = x.mant[:1] - x.exp++ - return - } - - // n > 0 && x.mant[n-1] < '9' - x.mant[n-1]++ - x.mant = x.mant[:n] - // x already trimmed -} - -func (x *decimal) roundDown(n int) { - if n < 0 || n >= len(x.mant) { - return // nothing to do - } - x.mant = x.mant[:n] - trim(x) -} - -// trim cuts off any trailing zeros from x's mantissa; -// they are meaningless for the value of x. -func trim(x *decimal) { - i := len(x.mant) - for i > 0 && x.mant[i-1] == '0' { - i-- - } - x.mant = x.mant[:i] -} diff --git a/src/cmd/internal/gc/big/decimal_test.go b/src/cmd/internal/gc/big/decimal_test.go deleted file mode 100644 index 81e022a47d..0000000000 --- a/src/cmd/internal/gc/big/decimal_test.go +++ /dev/null @@ -1,106 +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 big - -import "testing" - -func TestDecimalString(t *testing.T) { - for _, test := range []struct { - x decimal - want string - }{ - {want: "0"}, - {decimal{nil, 1000}, "0"}, // exponent of 0 is ignored - {decimal{[]byte("12345"), 0}, "0.12345"}, - {decimal{[]byte("12345"), -3}, "0.00012345"}, - {decimal{[]byte("12345"), +3}, "123.45"}, - {decimal{[]byte("12345"), +10}, "1234500000"}, - } { - if got := test.x.String(); got != test.want { - t.Errorf("%v == %s; want %s", test.x, got, test.want) - } - } -} - -func TestDecimalInit(t *testing.T) { - for _, test := range []struct { - x Word - shift int - want string - }{ - {0, 0, "0"}, - {0, -100, "0"}, - {0, 100, "0"}, - {1, 0, "1"}, - {1, 10, "1024"}, - {1, 100, "1267650600228229401496703205376"}, - {1, -100, "0.0000000000000000000000000000007888609052210118054117285652827862296732064351090230047702789306640625"}, - {12345678, 8, "3160493568"}, - {12345678, -8, "48225.3046875"}, - {195312, 9, "99999744"}, - {1953125, 9, "1000000000"}, - } { - var d decimal - d.init(nat{test.x}.norm(), test.shift) - if got := d.String(); got != test.want { - t.Errorf("%d << %d == %s; want %s", test.x, test.shift, got, test.want) - } - } -} - -func TestDecimalRounding(t *testing.T) { - for _, test := range []struct { - x uint64 - n int - down, even, up string - }{ - {0, 0, "0", "0", "0"}, - {0, 1, "0", "0", "0"}, - - {1, 0, "0", "0", "10"}, - {5, 0, "0", "0", "10"}, - {9, 0, "0", "10", "10"}, - - {15, 1, "10", "20", "20"}, - {45, 1, "40", "40", "50"}, - {95, 1, "90", "100", "100"}, - - {12344999, 4, "12340000", "12340000", "12350000"}, - {12345000, 4, "12340000", "12340000", "12350000"}, - {12345001, 4, "12340000", "12350000", "12350000"}, - {23454999, 4, "23450000", "23450000", "23460000"}, - {23455000, 4, "23450000", "23460000", "23460000"}, - {23455001, 4, "23450000", "23460000", "23460000"}, - - {99994999, 4, "99990000", "99990000", "100000000"}, - {99995000, 4, "99990000", "100000000", "100000000"}, - {99999999, 4, "99990000", "100000000", "100000000"}, - - {12994999, 4, "12990000", "12990000", "13000000"}, - {12995000, 4, "12990000", "13000000", "13000000"}, - {12999999, 4, "12990000", "13000000", "13000000"}, - } { - x := nat(nil).setUint64(test.x) - - var d decimal - d.init(x, 0) - d.roundDown(test.n) - if got := d.String(); got != test.down { - t.Errorf("roundDown(%d, %d) = %s; want %s", test.x, test.n, got, test.down) - } - - d.init(x, 0) - d.round(test.n) - if got := d.String(); got != test.even { - t.Errorf("round(%d, %d) = %s; want %s", test.x, test.n, got, test.even) - } - - d.init(x, 0) - d.roundUp(test.n) - if got := d.String(); got != test.up { - t.Errorf("roundUp(%d, %d) = %s; want %s", test.x, test.n, got, test.up) - } - } -} diff --git a/src/cmd/internal/gc/big/example_test.go b/src/cmd/internal/gc/big/example_test.go deleted file mode 100644 index 078be47f95..0000000000 --- a/src/cmd/internal/gc/big/example_test.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2012 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 big_test - -import ( - "fmt" - "log" - "math/big" -) - -func ExampleRat_SetString() { - r := new(big.Rat) - r.SetString("355/113") - fmt.Println(r.FloatString(3)) - // Output: 3.142 -} - -func ExampleInt_SetString() { - i := new(big.Int) - i.SetString("644", 8) // octal - fmt.Println(i) - // Output: 420 -} - -func ExampleRat_Scan() { - // The Scan function is rarely used directly; - // the fmt package recognizes it as an implementation of fmt.Scanner. - r := new(big.Rat) - _, err := fmt.Sscan("1.5000", r) - if err != nil { - log.Println("error scanning value:", err) - } else { - fmt.Println(r) - } - // Output: 3/2 -} - -func ExampleInt_Scan() { - // The Scan function is rarely used directly; - // the fmt package recognizes it as an implementation of fmt.Scanner. - i := new(big.Int) - _, err := fmt.Sscan("18446744073709551617", i) - if err != nil { - log.Println("error scanning value:", err) - } else { - fmt.Println(i) - } - // Output: 18446744073709551617 -} diff --git a/src/cmd/internal/gc/big/float.go b/src/cmd/internal/gc/big/float.go deleted file mode 100644 index ed55e8e513..0000000000 --- a/src/cmd/internal/gc/big/float.go +++ /dev/null @@ -1,1681 +0,0 @@ -// Copyright 2014 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. - -// This file implements multi-precision floating-point numbers. -// Like in the GNU MPFR library (http://www.mpfr.org/), operands -// can be of mixed precision. Unlike MPFR, the rounding mode is -// not specified with each operation, but with each operand. The -// rounding mode of the result operand determines the rounding -// mode of an operation. This is a from-scratch implementation. - -package big - -import ( - "fmt" - "math" -) - -const debugFloat = true // enable for debugging - -// A nonzero finite Float represents a multi-precision floating point number -// -// sign × mantissa × 2**exponent -// -// with 0.5 <= mantissa < 1.0, and MinExp <= exponent <= MaxExp. -// A Float may also be zero (+0, -0) or infinite (+Inf, -Inf). -// All Floats are ordered, and the ordering of two Floats x and y -// is defined by x.Cmp(y). -// -// Each Float value also has a precision, rounding mode, and accuracy. -// The precision is the maximum number of mantissa bits available to -// represent the value. The rounding mode specifies how a result should -// be rounded to fit into the mantissa bits, and accuracy describes the -// rounding error with respect to the exact result. -// -// Unless specified otherwise, all operations (including setters) that -// specify a *Float variable for the result (usually via the receiver -// with the exception of MantExp), round the numeric result according -// to the precision and rounding mode of the result variable. -// -// If the provided result precision is 0 (see below), it is set to the -// precision of the argument with the largest precision value before any -// rounding takes place, and the rounding mode remains unchanged. Thus, -// uninitialized Floats provided as result arguments will have their -// precision set to a reasonable value determined by the operands and -// their mode is the zero value for RoundingMode (ToNearestEven). -// -// By setting the desired precision to 24 or 53 and using matching rounding -// mode (typically ToNearestEven), Float operations produce the same results -// as the corresponding float32 or float64 IEEE-754 arithmetic for operands -// that correspond to normal (i.e., not denormal) float32 or float64 numbers. -// Exponent underflow and overflow lead to a 0 or an Infinity for different -// values than IEEE-754 because Float exponents have a much larger range. -// -// The zero (uninitialized) value for a Float is ready to use and represents -// the number +0.0 exactly, with precision 0 and rounding mode ToNearestEven. -// -type Float struct { - prec uint32 - mode RoundingMode - acc Accuracy - form form - neg bool - mant nat - exp int32 -} - -// Float operations that would lead to a NaN under IEEE-754 rules cause -// a run-time panic of ErrNaN type. -type ErrNaN struct { - msg string -} - -// 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. -func NewFloat(x float64) *Float { - if math.IsNaN(x) { - panic(ErrNaN{"NewFloat(NaN)"}) - } - return new(Float).SetFloat64(x) -} - -// Exponent and precision limits. -const ( - MaxExp = math.MaxInt32 // largest supported exponent - MinExp = math.MinInt32 // smallest supported exponent - MaxPrec = math.MaxUint32 // largest (theoretically) supported precision; likely memory-limited -) - -// Internal representation: The mantissa bits x.mant of a nonzero finite -// Float x are stored in a nat slice long enough to hold up to x.prec bits; -// the slice may (but doesn't have to) be shorter if the mantissa contains -// trailing 0 bits. x.mant is normalized if the msb of x.mant == 1 (i.e., -// the msb is shifted all the way "to the left"). Thus, if the mantissa has -// trailing 0 bits or x.prec is not a multiple of the the Word size _W, -// x.mant[0] has trailing zero bits. The msb of the mantissa corresponds -// to the value 0.5; the exponent x.exp shifts the binary point as needed. -// -// A zero or non-finite Float x ignores x.mant and x.exp. -// -// x form neg mant exp -// ---------------------------------------------------------- -// ±0 zero sign - - -// 0 < |x| < +Inf finite sign mantissa exponent -// ±Inf inf sign - - - -// A form value describes the internal representation. -type form byte - -// The form value order is relevant - do not change! -const ( - zero form = iota - finite - inf -) - -// RoundingMode determines how a Float value is rounded to the -// desired precision. Rounding may change the Float value; the -// rounding error is described by the Float's Accuracy. -type RoundingMode byte - -// The following rounding modes are supported. -const ( - ToNearestEven RoundingMode = iota // == IEEE 754-2008 roundTiesToEven - ToNearestAway // == IEEE 754-2008 roundTiesToAway - ToZero // == IEEE 754-2008 roundTowardZero - AwayFromZero // no IEEE 754-2008 equivalent - ToNegativeInf // == IEEE 754-2008 roundTowardNegative - ToPositiveInf // == IEEE 754-2008 roundTowardPositive -) - -//go:generate stringer -type=RoundingMode - -// Accuracy describes the rounding error produced by the most recent -// operation that generated a Float value, relative to the exact value. -type Accuracy int8 - -// Constants describing the Accuracy of a Float. -const ( - Below Accuracy = -1 - Exact Accuracy = 0 - Above Accuracy = +1 -) - -//go:generate stringer -type=Accuracy - -// SetPrec sets z's precision to prec and returns the (possibly) rounded -// value of z. Rounding occurs according to z's rounding mode if the mantissa -// cannot be represented in prec bits without loss of precision. -// SetPrec(0) maps all finite values to ±0; infinite values remain unchanged. -// If prec > MaxPrec, it is set to MaxPrec. -func (z *Float) SetPrec(prec uint) *Float { - z.acc = Exact // optimistically assume no rounding is needed - - // special case - if prec == 0 { - z.prec = 0 - if z.form == finite { - // truncate z to 0 - z.acc = makeAcc(z.neg) - z.form = zero - } - return z - } - - // general case - if prec > MaxPrec { - prec = MaxPrec - } - old := z.prec - z.prec = uint32(prec) - if z.prec < old { - z.round(0) - } - return z -} - -func makeAcc(above bool) Accuracy { - if above { - return Above - } - return Below -} - -// SetMode sets z's rounding mode to mode and returns an exact z. -// z remains unchanged otherwise. -// z.SetMode(z.Mode()) is a cheap way to set z's accuracy to Exact. -func (z *Float) SetMode(mode RoundingMode) *Float { - z.mode = mode - z.acc = Exact - return z -} - -// Prec returns the mantissa precision of x in bits. -// The result may be 0 for |x| == 0 and |x| == Inf. -func (x *Float) Prec() uint { - return uint(x.prec) -} - -// MinPrec returns the minimum precision required to represent x exactly -// (i.e., the smallest prec before x.SetPrec(prec) would start rounding x). -// The result is 0 for |x| == 0 and |x| == Inf. -func (x *Float) MinPrec() uint { - if x.form != finite { - return 0 - } - return uint(len(x.mant))*_W - x.mant.trailingZeroBits() -} - -// Mode returns the rounding mode of x. -func (x *Float) Mode() RoundingMode { - return x.mode -} - -// Acc returns the accuracy of x produced by the most recent operation. -func (x *Float) Acc() Accuracy { - return x.acc -} - -// Sign returns: -// -// -1 if x < 0 -// 0 if x is ±0 -// +1 if x > 0 -// -func (x *Float) Sign() int { - if debugFloat { - x.validate() - } - if x.form == zero { - return 0 - } - if x.neg { - return -1 - } - return 1 -} - -// MantExp breaks x into its mantissa and exponent components -// and returns the exponent. If a non-nil mant argument is -// provided its value is set to the mantissa of x, with the -// same precision and rounding mode as x. The components -// satisfy x == mant × 2**exp, with 0.5 <= |mant| < 1.0. -// Calling MantExp with a nil argument is an efficient way to -// get the exponent of the receiver. -// -// Special cases are: -// -// ( ±0).MantExp(mant) = 0, with mant set to ±0 -// (±Inf).MantExp(mant) = 0, with mant set to ±Inf -// -// x and mant may be the same in which case x is set to its -// mantissa value. -func (x *Float) MantExp(mant *Float) (exp int) { - if debugFloat { - x.validate() - } - if x.form == finite { - exp = int(x.exp) - } - if mant != nil { - mant.Copy(x) - if mant.form == finite { - mant.exp = 0 - } - } - return -} - -func (z *Float) setExpAndRound(exp int64, sbit uint) { - if exp < MinExp { - // underflow - z.acc = makeAcc(z.neg) - z.form = zero - return - } - - if exp > MaxExp { - // overflow - z.acc = makeAcc(!z.neg) - z.form = inf - return - } - - z.form = finite - z.exp = int32(exp) - z.round(sbit) -} - -// SetMantExp sets z to mant × 2**exp and and returns z. -// The result z has the same precision and rounding mode -// as mant. SetMantExp is an inverse of MantExp but does -// not require 0.5 <= |mant| < 1.0. Specifically: -// -// mant := new(Float) -// new(Float).SetMantExp(mant, x.SetMantExp(mant)).Cmp(x).Eql() is true -// -// Special cases are: -// -// z.SetMantExp( ±0, exp) = ±0 -// z.SetMantExp(±Inf, exp) = ±Inf -// -// z and mant may be the same in which case z's exponent -// is set to exp. -func (z *Float) SetMantExp(mant *Float, exp int) *Float { - if debugFloat { - z.validate() - mant.validate() - } - z.Copy(mant) - if z.form != finite { - return z - } - z.setExpAndRound(int64(z.exp)+int64(exp), 0) - return z -} - -// Signbit returns true if x is negative or negative zero. -func (x *Float) Signbit() bool { - return x.neg -} - -// IsInf reports whether x is +Inf or -Inf. -func (x *Float) IsInf() bool { - return x.form == inf -} - -// IsInt reports whether x is an integer. -// ±Inf values are not integers. -func (x *Float) IsInt() bool { - if debugFloat { - x.validate() - } - // special cases - if x.form != finite { - return x.form == zero - } - // x.form == finite - if x.exp <= 0 { - return false - } - // x.exp > 0 - return x.prec <= uint32(x.exp) || x.MinPrec() <= uint(x.exp) // not enough bits for fractional mantissa -} - -// debugging support -func (x *Float) validate() { - if !debugFloat { - // avoid performance bugs - panic("validate called but debugFloat is not set") - } - if x.form != finite { - return - } - m := len(x.mant) - if m == 0 { - panic("nonzero finite number with empty mantissa") - } - const msb = 1 << (_W - 1) - if x.mant[m-1]&msb == 0 { - panic(fmt.Sprintf("msb not set in last word %#x of %s", x.mant[m-1], x.Format('p', 0))) - } - if x.prec == 0 { - panic("zero precision finite number") - } -} - -// round rounds z according to z.mode to z.prec bits and sets z.acc accordingly. -// sbit must be 0 or 1 and summarizes any "sticky bit" information one might -// have before calling round. z's mantissa must be normalized (with the msb set) -// or empty. -// -// CAUTION: The rounding modes ToNegativeInf, ToPositiveInf are affected by the -// sign of z. For correct rounding, the sign of z must be set correctly before -// calling round. -func (z *Float) round(sbit uint) { - if debugFloat { - z.validate() - if z.form > finite { - panic(fmt.Sprintf("round called for non-finite value %s", z)) - } - } - // z.form <= finite - - z.acc = Exact - if z.form == zero { - return - } - // z.form == finite && len(z.mant) > 0 - // m > 0 implies z.prec > 0 (checked by validate) - - m := uint32(len(z.mant)) // present mantissa length in words - bits := m * _W // present mantissa bits - if bits <= z.prec { - // mantissa fits => nothing to do - return - } - // bits > z.prec - - n := (z.prec + (_W - 1)) / _W // mantissa length in words for desired precision - - // Rounding is based on two bits: the rounding bit (rbit) and the - // sticky bit (sbit). The rbit is the bit immediately before the - // z.prec leading mantissa bits (the "0.5"). The sbit is set if any - // of the bits before the rbit are set (the "0.25", "0.125", etc.): - // - // rbit sbit => "fractional part" - // - // 0 0 == 0 - // 0 1 > 0 , < 0.5 - // 1 0 == 0.5 - // 1 1 > 0.5, < 1.0 - - // bits > z.prec: mantissa too large => round - r := uint(bits - z.prec - 1) // rounding bit position; r >= 0 - rbit := z.mant.bit(r) // rounding bit - if sbit == 0 { - sbit = z.mant.sticky(r) - } - if debugFloat && sbit&^1 != 0 { - panic(fmt.Sprintf("invalid sbit %#x", sbit)) - } - - // convert ToXInf rounding modes - mode := z.mode - switch mode { - case ToNegativeInf: - mode = ToZero - if z.neg { - mode = AwayFromZero - } - case ToPositiveInf: - mode = AwayFromZero - if z.neg { - mode = ToZero - } - } - - // cut off extra words - if m > n { - copy(z.mant, z.mant[m-n:]) // move n last words to front - z.mant = z.mant[:n] - } - - // determine number of trailing zero bits t - t := n*_W - z.prec // 0 <= t < _W - lsb := Word(1) << t - - // make rounding decision - // TODO(gri) This can be simplified (see Bits.round in bits_test.go). - switch mode { - case ToZero: - // nothing to do - case ToNearestEven, ToNearestAway: - if rbit == 0 { - // rounding bits == 0b0x - mode = ToZero - } else if sbit == 1 { - // rounding bits == 0b11 - mode = AwayFromZero - } - case AwayFromZero: - if rbit|sbit == 0 { - mode = ToZero - } - default: - // ToXInf modes have been converted to ToZero or AwayFromZero - panic("unreachable") - } - - // round and determine accuracy - switch mode { - case ToZero: - if rbit|sbit != 0 { - z.acc = Below - } - - case ToNearestEven, ToNearestAway: - if debugFloat && rbit != 1 { - panic("internal error in rounding") - } - if mode == ToNearestEven && sbit == 0 && z.mant[0]&lsb == 0 { - z.acc = Below - break - } - // mode == ToNearestAway || sbit == 1 || z.mant[0]&lsb != 0 - fallthrough - - case AwayFromZero: - // add 1 to mantissa - if addVW(z.mant, z.mant, lsb) != 0 { - // overflow => shift mantissa right by 1 and add msb - shrVU(z.mant, z.mant, 1) - z.mant[n-1] |= 1 << (_W - 1) - // adjust exponent - if z.exp < MaxExp { - z.exp++ - } else { - // exponent overflow - z.acc = makeAcc(!z.neg) - z.form = inf - return - } - } - z.acc = Above - } - - // zero out trailing bits in least-significant word - z.mant[0] &^= lsb - 1 - - // update accuracy - if z.acc != Exact && z.neg { - z.acc = -z.acc - } - - if debugFloat { - z.validate() - } - - 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 - } - z.acc = Exact - z.neg = neg - if x == 0 { - z.form = zero - return z - } - // x != 0 - z.form = finite - s := nlz64(x) - z.mant = z.mant.setUint64(x << s) - z.exp = int32(64 - s) // always fits - if z.prec < 64 { - z.round(0) - } - return z -} - -// SetUint64 sets z to the (possibly rounded) value of x and returns z. -// If z's precision is 0, it is changed to 64 (and rounding will have -// no effect). -func (z *Float) SetUint64(x uint64) *Float { - return z.setBits64(false, x) -} - -// SetInt64 sets z to the (possibly rounded) value of x and returns z. -// If z's precision is 0, it is changed to 64 (and rounding will have -// no effect). -func (z *Float) SetInt64(x int64) *Float { - u := x - if u < 0 { - u = -u - } - // We cannot simply call z.SetUint64(uint64(u)) and change - // the sign afterwards because the sign affects rounding. - return z.setBits64(x < 0, uint64(u)) -} - -// SetFloat64 sets z to the (possibly rounded) value of x and returns z. -// If z's precision is 0, it is changed to 53 (and rounding will have -// no effect). SetFloat64 panics with ErrNaN if x is a NaN. -func (z *Float) SetFloat64(x float64) *Float { - if z.prec == 0 { - z.prec = 53 - } - if math.IsNaN(x) { - panic(ErrNaN{"Float.SetFloat64(NaN)"}) - } - z.acc = Exact - z.neg = math.Signbit(x) // handle -0, -Inf correctly - if x == 0 { - z.form = zero - return z - } - if math.IsInf(x, 0) { - z.form = inf - return z - } - // normalized x != 0 - z.form = finite - fmant, exp := math.Frexp(x) // get normalized mantissa - z.mant = z.mant.setUint64(1<<63 | math.Float64bits(fmant)<<11) - z.exp = int32(exp) // always fits - if z.prec < 53 { - z.round(0) - } - return z -} - -// fnorm normalizes mantissa m by shifting it to the left -// such that the msb of the most-significant word (msw) is 1. -// It returns the shift amount. It assumes that len(m) != 0. -func fnorm(m nat) int64 { - if debugFloat && (len(m) == 0 || m[len(m)-1] == 0) { - panic("msw of mantissa is 0") - } - s := nlz(m[len(m)-1]) - if s > 0 { - c := shlVU(m, m, s) - if debugFloat && c != 0 { - panic("nlz or shlVU incorrect") - } - } - return int64(s) -} - -// SetInt sets z to the (possibly rounded) value of x and returns z. -// If z's precision is 0, it is changed to the larger of x.BitLen() -// or 64 (and rounding will have no effect). -func (z *Float) SetInt(x *Int) *Float { - // TODO(gri) can be more efficient if z.prec > 0 - // but small compared to the size of x, or if there - // are many trailing 0's. - bits := uint32(x.BitLen()) - if z.prec == 0 { - z.prec = umax32(bits, 64) - } - z.acc = Exact - z.neg = x.neg - if len(x.abs) == 0 { - z.form = zero - return z - } - // x != 0 - z.mant = z.mant.set(x.abs) - fnorm(z.mant) - z.setExpAndRound(int64(bits), 0) - return z -} - -// SetRat sets z to the (possibly rounded) value of x and returns z. -// If z's precision is 0, it is changed to the largest of a.BitLen(), -// b.BitLen(), or 64; with x = a/b. -func (z *Float) SetRat(x *Rat) *Float { - if x.IsInt() { - return z.SetInt(x.Num()) - } - var a, b Float - a.SetInt(x.Num()) - b.SetInt(x.Denom()) - if z.prec == 0 { - z.prec = umax32(a.prec, b.prec) - } - return z.Quo(&a, &b) -} - -// SetInf sets z to the infinite Float -Inf if signbit is -// set, or +Inf if signbit is not set, and returns z. The -// precision of z is unchanged and the result is always -// Exact. -func (z *Float) SetInf(signbit bool) *Float { - z.acc = Exact - z.form = inf - z.neg = signbit - return z -} - -// Set sets z to the (possibly rounded) value of x and returns z. -// If z's precision is 0, it is changed to the precision of x -// before setting z (and rounding will have no effect). -// Rounding is performed according to z's precision and rounding -// mode; and z's accuracy reports the result error relative to the -// exact (not rounded) result. -func (z *Float) Set(x *Float) *Float { - if debugFloat { - x.validate() - } - z.acc = Exact - if z != x { - z.form = x.form - z.neg = x.neg - if x.form == finite { - z.exp = x.exp - z.mant = z.mant.set(x.mant) - } - if z.prec == 0 { - z.prec = x.prec - } else if z.prec < x.prec { - z.round(0) - } - } - return z -} - -// Copy sets z to x, with the same precision, rounding mode, and -// accuracy as x, and returns z. x is not changed even if z and -// x are the same. -func (z *Float) Copy(x *Float) *Float { - if debugFloat { - x.validate() - } - if z != x { - z.prec = x.prec - z.mode = x.mode - z.acc = x.acc - z.form = x.form - z.neg = x.neg - if z.form == finite { - z.mant = z.mant.set(x.mant) - z.exp = x.exp - } - } - 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 { - return 0 - } - // i > 0 - v := uint64(x[i-1]) - if _W == 32 { - v <<= 32 - if i > 1 { - v |= uint64(x[i-2]) - } - } - return v -} - -// Uint64 returns the unsigned integer resulting from truncating x -// towards zero. If 0 <= x <= math.MaxUint64, the result is Exact -// if x is an integer and Below otherwise. -// The result is (0, Above) for x < 0, and (math.MaxUint64, Below) -// for x > math.MaxUint64. -func (x *Float) Uint64() (uint64, Accuracy) { - if debugFloat { - x.validate() - } - - switch x.form { - case finite: - if x.neg { - return 0, Above - } - // 0 < x < +Inf - if x.exp <= 0 { - // 0 < x < 1 - return 0, Below - } - // 1 <= x < Inf - if x.exp <= 64 { - // u = trunc(x) fits into a uint64 - u := high64(x.mant) >> (64 - uint32(x.exp)) - if x.MinPrec() <= 64 { - return u, Exact - } - return u, Below // x truncated - } - // x too large - return math.MaxUint64, Below - - case zero: - return 0, Exact - - case inf: - if x.neg { - return 0, Above - } - return math.MaxUint64, Below - } - - panic("unreachable") -} - -// Int64 returns the integer resulting from truncating x towards zero. -// If math.MinInt64 <= x <= math.MaxInt64, the result is Exact if x is -// an integer, and Above (x < 0) or Below (x > 0) otherwise. -// The result is (math.MinInt64, Above) for x < math.MinInt64, -// and (math.MaxInt64, Below) for x > math.MaxInt64. -func (x *Float) Int64() (int64, Accuracy) { - if debugFloat { - x.validate() - } - - switch x.form { - case finite: - // 0 < |x| < +Inf - acc := makeAcc(x.neg) - if x.exp <= 0 { - // 0 < |x| < 1 - return 0, acc - } - // x.exp > 0 - - // 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))) - if x.neg { - i = -i - } - if x.MinPrec() <= uint(x.exp) { - return i, Exact - } - return i, acc // x truncated - } - if x.neg { - // check for special case x == math.MinInt64 (i.e., x == -(0.5 << 64)) - if x.exp == 64 && x.MinPrec() == 1 { - acc = Exact - } - return math.MinInt64, acc - } - // x too large - return math.MaxInt64, Below - - case zero: - return 0, Exact - - case inf: - if x.neg { - return math.MinInt64, Above - } - return math.MaxInt64, Below - } - - 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. -// If x is too large to be represented by a float32 (|x| > math.MaxFloat32), -// the result is (+Inf, Above) or (-Inf, Below), depending on the sign of x. -func (x *Float) Float32() (float32, Accuracy) { - if debugFloat { - x.validate() - } - - switch x.form { - case finite: - // 0 < |x| < +Inf - - const ( - fbits = 32 // float size - mbits = 23 // mantissa size (excluding implicit msb) - ebits = fbits - mbits - 1 // 8 exponent size - bias = 1<<(ebits-1) - 1 // 127 exponent bias - dmin = 1 - bias - mbits // -149 smallest unbiased exponent (denormal) - emin = 1 - bias // -126 smallest unbiased exponent (normal) - 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) - - if x.exp < dmin+1 { - // underflow - if x.neg { - var z float32 - return -z, Above - } - return 0.0, Below - } - // x.exp >= dmin+1 - - 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.Set(x) - - // 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 - } - - if r.exp > emax+1 { - // 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) - } - - m := high32(r.mant) >> ebits & (1< math.MaxFloat64), -// the result is (+Inf, Above) or (-Inf, Below), depending on the sign of x. -func (x *Float) Float64() (float64, Accuracy) { - if debugFloat { - x.validate() - } - - switch x.form { - case finite: - // 0 < |x| < +Inf - - const ( - fbits = 64 // float size - mbits = 52 // mantissa size (excluding implicit msb) - ebits = fbits - mbits - 1 // 11 exponent size - bias = 1<<(ebits-1) - 1 // 1023 exponent bias - dmin = 1 - bias - mbits // -1074 smallest unbiased exponent (denormal) - emin = 1 - bias // -1022 smallest unbiased exponent (normal) - 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) - - if x.exp < dmin+1 { - // underflow - if x.neg { - var z float64 - return -z, Above - } - return 0.0, Below - } - // x.exp >= dmin+1 - - 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.Set(x) - - // 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 - } - - if r.exp > emax+1 { - // 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) - } - - m := high64(r.mant) >> ebits & (1< 0, and Above for x < 0. -// If a non-nil *Int argument z is provided, Int stores -// the result in z instead of allocating a new Int. -func (x *Float) Int(z *Int) (*Int, Accuracy) { - if debugFloat { - x.validate() - } - - if z == nil && x.form <= finite { - z = new(Int) - } - - switch x.form { - case finite: - // 0 < |x| < +Inf - acc := makeAcc(x.neg) - if x.exp <= 0 { - // 0 < |x| < 1 - return z.SetInt64(0), acc - } - // x.exp > 0 - - // 1 <= |x| < +Inf - // determine minimum required precision for x - allBits := uint(len(x.mant)) * _W - exp := uint(x.exp) - if x.MinPrec() <= exp { - acc = Exact - } - // shift mantissa as needed - if z == nil { - z = new(Int) - } - z.neg = x.neg - switch { - case exp > allBits: - z.abs = z.abs.shl(x.mant, exp-allBits) - default: - z.abs = z.abs.set(x.mant) - case exp < allBits: - z.abs = z.abs.shr(x.mant, allBits-exp) - } - return z, acc - - case zero: - return z.SetInt64(0), Exact - - case inf: - return nil, makeAcc(x.neg) - } - - panic("unreachable") -} - -// Rat returns the rational number corresponding to x; -// or nil if x is an infinity. -// The result is Exact is x is not an Inf. -// If a non-nil *Rat argument z is provided, Rat stores -// the result in z instead of allocating a new Rat. -func (x *Float) Rat(z *Rat) (*Rat, Accuracy) { - if debugFloat { - x.validate() - } - - if z == nil && x.form <= finite { - z = new(Rat) - } - - switch x.form { - case finite: - // 0 < |x| < +Inf - allBits := int32(len(x.mant)) * _W - // build up numerator and denominator - z.a.neg = x.neg - switch { - case x.exp > allBits: - z.a.abs = z.a.abs.shl(x.mant, uint(x.exp-allBits)) - z.b.abs = z.b.abs[:0] // == 1 (see Rat) - // z already in normal form - default: - z.a.abs = z.a.abs.set(x.mant) - z.b.abs = z.b.abs[:0] // == 1 (see Rat) - // z already in normal form - case x.exp < allBits: - z.a.abs = z.a.abs.set(x.mant) - t := z.b.abs.setUint64(1) - z.b.abs = t.shl(t, uint(allBits-x.exp)) - z.norm() - } - return z, Exact - - case zero: - return z.SetInt64(0), Exact - - case inf: - return nil, makeAcc(x.neg) - } - - panic("unreachable") -} - -// Abs sets z to the (possibly rounded) value |x| (the absolute value of x) -// and returns z. -func (z *Float) Abs(x *Float) *Float { - z.Set(x) - z.neg = false - return z -} - -// Neg sets z to the (possibly rounded) value of x with its sign negated, -// and returns z. -func (z *Float) Neg(x *Float) *Float { - z.Set(x) - z.neg = !z.neg - return z -} - -func validateBinaryOperands(x, y *Float) { - if !debugFloat { - // avoid performance bugs - panic("validateBinaryOperands called but debugFloat is not set") - } - if len(x.mant) == 0 { - panic("empty mantissa for x") - } - if len(y.mant) == 0 { - panic("empty mantissa for y") - } -} - -// z = x + y, ignoring signs of x and y for the addition -// but using the sign of z for rounding the result. -// x and y must have a non-empty mantissa and valid exponent. -func (z *Float) uadd(x, y *Float) { - // Note: This implementation requires 2 shifts most of the - // time. It is also inefficient if exponents or precisions - // differ by wide margins. The following article describes - // an efficient (but much more complicated) implementation - // compatible with the internal representation used here: - // - // Vincent Lefèvre: "The Generic Multiple-Precision Floating- - // Point Addition With Exact Rounding (as in the MPFR Library)" - // http://www.vinc17.net/research/papers/rnc6.pdf - - if debugFloat { - validateBinaryOperands(x, y) - } - - // compute exponents ex, ey for mantissa with "binary point" - // on the right (mantissa.0) - use int64 to avoid overflow - ex := int64(x.exp) - int64(len(x.mant))*_W - ey := int64(y.exp) - int64(len(y.mant))*_W - - // TODO(gri) having a combined add-and-shift primitive - // could make this code significantly faster - switch { - case ex < ey: - // cannot re-use z.mant w/o testing for aliasing - t := nat(nil).shl(y.mant, uint(ey-ex)) - z.mant = z.mant.add(x.mant, t) - default: - // ex == ey, no shift needed - z.mant = z.mant.add(x.mant, y.mant) - case ex > ey: - // cannot re-use z.mant w/o testing for aliasing - t := nat(nil).shl(x.mant, uint(ex-ey)) - z.mant = z.mant.add(t, y.mant) - ex = ey - } - // len(z.mant) > 0 - - z.setExpAndRound(ex+int64(len(z.mant))*_W-fnorm(z.mant), 0) -} - -// z = x - y for |x| > |y|, ignoring signs of x and y for the subtraction -// but using the sign of z for rounding the result. -// x and y must have a non-empty mantissa and valid exponent. -func (z *Float) usub(x, y *Float) { - // This code is symmetric to uadd. - // We have not factored the common code out because - // eventually uadd (and usub) should be optimized - // by special-casing, and the code will diverge. - - if debugFloat { - validateBinaryOperands(x, y) - } - - ex := int64(x.exp) - int64(len(x.mant))*_W - ey := int64(y.exp) - int64(len(y.mant))*_W - - switch { - case ex < ey: - // cannot re-use z.mant w/o testing for aliasing - t := nat(nil).shl(y.mant, uint(ey-ex)) - z.mant = t.sub(x.mant, t) - default: - // ex == ey, no shift needed - z.mant = z.mant.sub(x.mant, y.mant) - case ex > ey: - // cannot re-use z.mant w/o testing for aliasing - t := nat(nil).shl(x.mant, uint(ex-ey)) - z.mant = t.sub(t, y.mant) - ex = ey - } - - // operands may have cancelled each other out - if len(z.mant) == 0 { - z.acc = Exact - z.form = zero - z.neg = false - return - } - // len(z.mant) > 0 - - z.setExpAndRound(ex+int64(len(z.mant))*_W-fnorm(z.mant), 0) -} - -// z = x * y, ignoring signs of x and y for the multiplication -// but using the sign of z for rounding the result. -// x and y must have a non-empty mantissa and valid exponent. -func (z *Float) umul(x, y *Float) { - if debugFloat { - validateBinaryOperands(x, y) - } - - // Note: This is doing too much work if the precision - // of z is less than the sum of the precisions of x - // and y which is often the case (e.g., if all floats - // have the same precision). - // TODO(gri) Optimize this for the common case. - - e := int64(x.exp) + int64(y.exp) - z.mant = z.mant.mul(x.mant, y.mant) - - z.setExpAndRound(e-fnorm(z.mant), 0) -} - -// z = x / y, ignoring signs of x and y for the division -// but using the sign of z for rounding the result. -// x and y must have a non-empty mantissa and valid exponent. -func (z *Float) uquo(x, y *Float) { - if debugFloat { - validateBinaryOperands(x, y) - } - - // mantissa length in words for desired result precision + 1 - // (at least one extra bit so we get the rounding bit after - // the division) - n := int(z.prec/_W) + 1 - - // compute adjusted x.mant such that we get enough result precision - xadj := x.mant - if d := n - len(x.mant) + len(y.mant); d > 0 { - // d extra words needed => add d "0 digits" to x - xadj = make(nat, len(x.mant)+d) - copy(xadj[d:], x.mant) - } - // TODO(gri): If we have too many digits (d < 0), we should be able - // to shorten x for faster division. But we must be extra careful - // with rounding in that case. - - // Compute d before division since there may be aliasing of x.mant - // (via xadj) or y.mant with z.mant. - d := len(xadj) - len(y.mant) - - // divide - var r nat - z.mant, r = z.mant.div(nil, xadj, y.mant) - e := int64(x.exp) - int64(y.exp) - int64(d-len(z.mant))*_W - - // The result is long enough to include (at least) the rounding bit. - // If there's a non-zero remainder, the corresponding fractional part - // (if it were computed), would have a non-zero sticky bit (if it were - // zero, it couldn't have a non-zero remainder). - var sbit uint - if len(r) > 0 { - sbit = 1 - } - - z.setExpAndRound(e-fnorm(z.mant), sbit) -} - -// ucmp returns -1, 0, or +1, depending on whether -// |x| < |y|, |x| == |y|, or |x| > |y|. -// x and y must have a non-empty mantissa and valid exponent. -func (x *Float) ucmp(y *Float) int { - if debugFloat { - validateBinaryOperands(x, y) - } - - switch { - case x.exp < y.exp: - return -1 - case x.exp > y.exp: - return +1 - } - // x.exp == y.exp - - // compare mantissas - i := len(x.mant) - j := len(y.mant) - for i > 0 || j > 0 { - var xm, ym Word - if i > 0 { - i-- - xm = x.mant[i] - } - if j > 0 { - j-- - ym = y.mant[j] - } - switch { - case xm < ym: - return -1 - case xm > ym: - return +1 - } - } - - return 0 -} - -// Handling of sign bit as defined by IEEE 754-2008, section 6.3: -// -// When neither the inputs nor result are NaN, the sign of a product or -// quotient is the exclusive OR of the operands’ signs; the sign of a sum, -// or of a difference x−y regarded as a sum x+(−y), differs from at most -// one of the addends’ signs; and the sign of the result of conversions, -// the quantize operation, the roundToIntegral operations, and the -// roundToIntegralExact (see 5.3.1) is the sign of the first or only operand. -// These rules shall apply even when operands or results are zero or infinite. -// -// When the sum of two operands with opposite signs (or the difference of -// two operands with like signs) is exactly zero, the sign of that sum (or -// difference) shall be +0 in all rounding-direction attributes except -// roundTowardNegative; under that attribute, the sign of an exact zero -// sum (or difference) shall be −0. However, x+x = x−(−x) retains the same -// sign as x even when x is zero. -// -// See also: http://play.golang.org/p/RtH3UCt5IH - -// Add sets z to the rounded sum x+y and returns z. If z's precision is 0, -// it is changed to the larger of x's or y's precision before the operation. -// Rounding is performed according to z's precision and rounding mode; and -// z's accuracy reports the result error relative to the exact (not rounded) -// result. Add panics with ErrNaN if x and y are infinities with opposite -// signs. The value of z is undefined in that case. -// -// BUG(gri) When rounding ToNegativeInf, the sign of Float values rounded to 0 is incorrect. -func (z *Float) Add(x, y *Float) *Float { - if debugFloat { - x.validate() - y.validate() - } - - if z.prec == 0 { - z.prec = umax32(x.prec, y.prec) - } - - if x.form == finite && y.form == finite { - // x + y (commom case) - z.neg = x.neg - if x.neg == y.neg { - // x + y == x + y - // (-x) + (-y) == -(x + y) - z.uadd(x, y) - } else { - // x + (-y) == x - y == -(y - x) - // (-x) + y == y - x == -(x - y) - if x.ucmp(y) > 0 { - z.usub(x, y) - } else { - z.neg = !z.neg - z.usub(y, x) - } - } - return z - } - - if x.form == inf && y.form == inf && x.neg != y.neg { - // +Inf + -Inf - // -Inf + +Inf - // value of z is undefined but make sure it's valid - z.acc = Exact - z.form = zero - z.neg = false - panic(ErrNaN{"addition of infinities with opposite signs"}) - } - - if x.form == zero && y.form == zero { - // ±0 + ±0 - z.acc = Exact - z.form = zero - z.neg = x.neg && y.neg // -0 + -0 == -0 - return z - } - - if x.form == inf || y.form == zero { - // ±Inf + y - // x + ±0 - return z.Set(x) - } - - // ±0 + y - // x + ±Inf - return z.Set(y) -} - -// Sub sets z to the rounded difference x-y and returns z. -// Precision, rounding, and accuracy reporting are as for Add. -// Sub panics with ErrNaN if x and y are infinities with equal -// signs. The value of z is undefined in that case. -func (z *Float) Sub(x, y *Float) *Float { - if debugFloat { - x.validate() - y.validate() - } - - if z.prec == 0 { - z.prec = umax32(x.prec, y.prec) - } - - if x.form == finite && y.form == finite { - // x - y (common case) - z.neg = x.neg - if x.neg != y.neg { - // x - (-y) == x + y - // (-x) - y == -(x + y) - z.uadd(x, y) - } else { - // x - y == x - y == -(y - x) - // (-x) - (-y) == y - x == -(x - y) - if x.ucmp(y) > 0 { - z.usub(x, y) - } else { - z.neg = !z.neg - z.usub(y, x) - } - } - return z - } - - if x.form == inf && y.form == inf && x.neg == y.neg { - // +Inf - +Inf - // -Inf - -Inf - // value of z is undefined but make sure it's valid - z.acc = Exact - z.form = zero - z.neg = false - panic(ErrNaN{"subtraction of infinities with equal signs"}) - } - - if x.form == zero && y.form == zero { - // ±0 - ±0 - z.acc = Exact - z.form = zero - z.neg = x.neg && !y.neg // -0 - +0 == -0 - return z - } - - if x.form == inf || y.form == zero { - // ±Inf - y - // x - ±0 - return z.Set(x) - } - - // ±0 - y - // x - ±Inf - return z.Neg(y) -} - -// Mul sets z to the rounded product x*y and returns z. -// Precision, rounding, and accuracy reporting are as for Add. -// Mul panics with ErrNaN if one operand is zero and the other -// operand an infinity. The value of z is undefined in that case. -func (z *Float) Mul(x, y *Float) *Float { - if debugFloat { - x.validate() - y.validate() - } - - if z.prec == 0 { - z.prec = umax32(x.prec, y.prec) - } - - z.neg = x.neg != y.neg - - if x.form == finite && y.form == finite { - // x * y (common case) - z.umul(x, y) - return z - } - - z.acc = Exact - if x.form == zero && y.form == inf || x.form == inf && y.form == zero { - // ±0 * ±Inf - // ±Inf * ±0 - // value of z is undefined but make sure it's valid - z.form = zero - z.neg = false - panic(ErrNaN{"multiplication of zero with infinity"}) - } - - if x.form == inf || y.form == inf { - // ±Inf * y - // x * ±Inf - z.form = inf - return z - } - - // ±0 * y - // x * ±0 - z.form = zero - return z -} - -// Quo sets z to the rounded quotient x/y and returns z. -// Precision, rounding, and accuracy reporting are as for Add. -// Quo panics with ErrNaN if both operands are zero or infinities. -// The value of z is undefined in that case. -func (z *Float) Quo(x, y *Float) *Float { - if debugFloat { - x.validate() - y.validate() - } - - if z.prec == 0 { - z.prec = umax32(x.prec, y.prec) - } - - z.neg = x.neg != y.neg - - if x.form == finite && y.form == finite { - // x / y (common case) - z.uquo(x, y) - return z - } - - z.acc = Exact - if x.form == zero && y.form == zero || x.form == inf && y.form == inf { - // ±0 / ±0 - // ±Inf / ±Inf - // value of z is undefined but make sure it's valid - z.form = zero - z.neg = false - panic(ErrNaN{"division of zero by zero or infinity by infinity"}) - } - - if x.form == zero || y.form == inf { - // ±0 / y - // x / ±Inf - z.form = zero - return z - } - - // x / ±0 - // ±Inf / y - z.form = inf - return z -} - -// Cmp compares x and y and returns: -// -// -1 if x < y -// 0 if x == y (incl. -0 == 0, -Inf == -Inf, and +Inf == +Inf) -// +1 if x > y -// -func (x *Float) Cmp(y *Float) int { - if debugFloat { - x.validate() - y.validate() - } - - mx := x.ord() - my := y.ord() - switch { - case mx < my: - return -1 - case mx > my: - return +1 - } - // mx == my - - // only if |mx| == 1 we have to compare the mantissae - switch mx { - case -1: - return y.ucmp(x) - case +1: - return x.ucmp(y) - } - - return 0 -} - -// ord classifies x and returns: -// -// -2 if -Inf == x -// -1 if -Inf < x < 0 -// 0 if x == 0 (signed or unsigned) -// +1 if 0 < x < +Inf -// +2 if x == +Inf -// -func (x *Float) ord() int { - var m int - switch x.form { - case finite: - m = 1 - case zero: - return 0 - case inf: - m = 2 - } - if x.neg { - m = -m - } - return m -} - -func umax32(x, y uint32) uint32 { - if x > y { - return x - } - return y -} diff --git a/src/cmd/internal/gc/big/float_test.go b/src/cmd/internal/gc/big/float_test.go deleted file mode 100644 index de79b07aaf..0000000000 --- a/src/cmd/internal/gc/big/float_test.go +++ /dev/null @@ -1,1664 +0,0 @@ -// Copyright 2014 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 big - -import ( - "fmt" - "math" - "strconv" - "strings" - "testing" -) - -func (x *Float) uint64() uint64 { - u, acc := x.Uint64() - if acc != Exact { - panic(fmt.Sprintf("%s is not a uint64", x.Format('g', 10))) - } - return u -} - -func (x *Float) int64() int64 { - i, acc := x.Int64() - if acc != Exact { - panic(fmt.Sprintf("%s is not an int64", x.Format('g', 10))) - } - return i -} - -func TestFloatZeroValue(t *testing.T) { - // zero (uninitialized) value is a ready-to-use 0.0 - var x Float - if s := x.Format('f', 1); s != "0.0" { - t.Errorf("zero value = %s; want 0.0", s) - } - - // zero value has precision 0 - if prec := x.Prec(); prec != 0 { - t.Errorf("prec = %d; want 0", prec) - } - - // zero value can be used in any and all positions of binary operations - make := func(x int) *Float { - var f Float - if x != 0 { - f.SetInt64(int64(x)) - } - // x == 0 translates into the zero value - return &f - } - for _, test := range []struct { - z, x, y, want int - opname rune - op func(z, x, y *Float) *Float - }{ - {0, 0, 0, 0, '+', (*Float).Add}, - {0, 1, 2, 3, '+', (*Float).Add}, - {1, 2, 0, 2, '+', (*Float).Add}, - {2, 0, 1, 1, '+', (*Float).Add}, - - {0, 0, 0, 0, '-', (*Float).Sub}, - {0, 1, 2, -1, '-', (*Float).Sub}, - {1, 2, 0, 2, '-', (*Float).Sub}, - {2, 0, 1, -1, '-', (*Float).Sub}, - - {0, 0, 0, 0, '*', (*Float).Mul}, - {0, 1, 2, 2, '*', (*Float).Mul}, - {1, 2, 0, 0, '*', (*Float).Mul}, - {2, 0, 1, 0, '*', (*Float).Mul}, - - // {0, 0, 0, 0, '/', (*Float).Quo}, // panics - {0, 2, 1, 2, '/', (*Float).Quo}, - {1, 2, 0, 0, '/', (*Float).Quo}, // = +Inf - {2, 0, 1, 0, '/', (*Float).Quo}, - } { - z := make(test.z) - test.op(z, make(test.x), make(test.y)) - got := 0 - if !z.IsInf() { - got = int(z.int64()) - } - if got != test.want { - t.Errorf("%d %c %d = %d; want %d", test.x, test.opname, test.y, got, test.want) - } - } - - // TODO(gri) test how precision is set for zero value results -} - -func makeFloat(s string) *Float { - var x Float - - switch s { - case "0": - return &x - case "-0": - return x.Neg(&x) - case "Inf", "+Inf": - return x.SetInf(false) - case "-Inf": - return x.SetInf(true) - } - - x.SetPrec(1000) - if _, ok := x.SetString(s); !ok { - panic(fmt.Sprintf("%q is not a valid float", s)) - } - return &x -} - -func TestFloatSetPrec(t *testing.T) { - for _, test := range []struct { - x string - prec uint - want string - acc Accuracy - }{ - // prec 0 - {"0", 0, "0", Exact}, - {"-0", 0, "-0", Exact}, - {"-Inf", 0, "-Inf", Exact}, - {"+Inf", 0, "+Inf", Exact}, - {"123", 0, "0", Below}, - {"-123", 0, "-0", Above}, - - // prec at upper limit - {"0", MaxPrec, "0", Exact}, - {"-0", MaxPrec, "-0", Exact}, - {"-Inf", MaxPrec, "-Inf", Exact}, - {"+Inf", MaxPrec, "+Inf", Exact}, - - // just a few regular cases - general rounding is tested elsewhere - {"1.5", 1, "2", Above}, - {"-1.5", 1, "-2", Below}, - {"123", 1e6, "123", Exact}, - {"-123", 1e6, "-123", Exact}, - } { - x := makeFloat(test.x).SetPrec(test.prec) - prec := test.prec - if prec > MaxPrec { - prec = MaxPrec - } - if got := x.Prec(); got != prec { - t.Errorf("%s.SetPrec(%d).Prec() == %d; want %d", test.x, test.prec, got, prec) - } - if got, acc := x.String(), x.Acc(); got != test.want || acc != test.acc { - t.Errorf("%s.SetPrec(%d) = %s (%s); want %s (%s)", test.x, test.prec, got, acc, test.want, test.acc) - } - } -} - -func TestFloatMinPrec(t *testing.T) { - const max = 100 - for _, test := range []struct { - x string - want uint - }{ - {"0", 0}, - {"-0", 0}, - {"+Inf", 0}, - {"-Inf", 0}, - {"1", 1}, - {"2", 1}, - {"3", 2}, - {"0x8001", 16}, - {"0x8001p-1000", 16}, - {"0x8001p+1000", 16}, - {"0.1", max}, - } { - x := makeFloat(test.x).SetPrec(max) - if got := x.MinPrec(); got != test.want { - t.Errorf("%s.MinPrec() = %d; want %d", test.x, got, test.want) - } - } -} - -func TestFloatSign(t *testing.T) { - for _, test := range []struct { - x string - s int - }{ - {"-Inf", -1}, - {"-1", -1}, - {"-0", 0}, - {"+0", 0}, - {"+1", +1}, - {"+Inf", +1}, - } { - x := makeFloat(test.x) - s := x.Sign() - if s != test.s { - t.Errorf("%s.Sign() = %d; want %d", test.x, s, test.s) - } - } -} - -// alike(x, y) is like x.Cmp(y) == 0 but also considers the sign of 0 (0 != -0). -func alike(x, y *Float) bool { - return x.Cmp(y) == 0 && x.Signbit() == y.Signbit() -} - -func TestFloatMantExp(t *testing.T) { - for _, test := range []struct { - x string - mant string - exp int - }{ - {"0", "0", 0}, - {"+0", "0", 0}, - {"-0", "-0", 0}, - {"Inf", "+Inf", 0}, - {"+Inf", "+Inf", 0}, - {"-Inf", "-Inf", 0}, - {"1.5", "0.75", 1}, - {"1.024e3", "0.5", 11}, - {"-0.125", "-0.5", -2}, - } { - x := makeFloat(test.x) - mant := makeFloat(test.mant) - m := new(Float) - e := x.MantExp(m) - if !alike(m, mant) || e != test.exp { - t.Errorf("%s.MantExp() = %s, %d; want %s, %d", test.x, m.Format('g', 10), e, test.mant, test.exp) - } - } -} - -func TestFloatMantExpAliasing(t *testing.T) { - x := makeFloat("0.5p10") - if e := x.MantExp(x); e != 10 { - t.Fatalf("Float.MantExp aliasing error: got %d; want 10", e) - } - if want := makeFloat("0.5"); !alike(x, want) { - t.Fatalf("Float.MantExp aliasing error: got %s; want %s", x.Format('g', 10), want.Format('g', 10)) - } -} - -func TestFloatSetMantExp(t *testing.T) { - for _, test := range []struct { - frac string - exp int - z string - }{ - {"0", 0, "0"}, - {"+0", 0, "0"}, - {"-0", 0, "-0"}, - {"Inf", 1234, "+Inf"}, - {"+Inf", -1234, "+Inf"}, - {"-Inf", -1234, "-Inf"}, - {"0", MinExp, "0"}, - {"0.25", MinExp, "+0"}, // exponent underflow - {"-0.25", MinExp, "-0"}, // exponent underflow - {"1", MaxExp, "+Inf"}, // exponent overflow - {"2", MaxExp - 1, "+Inf"}, // exponent overflow - {"0.75", 1, "1.5"}, - {"0.5", 11, "1024"}, - {"-0.5", -2, "-0.125"}, - {"32", 5, "1024"}, - {"1024", -10, "1"}, - } { - frac := makeFloat(test.frac) - want := makeFloat(test.z) - var z Float - z.SetMantExp(frac, test.exp) - if !alike(&z, want) { - t.Errorf("SetMantExp(%s, %d) = %s; want %s", test.frac, test.exp, z.Format('g', 10), test.z) - } - // test inverse property - mant := new(Float) - if z.SetMantExp(mant, want.MantExp(mant)).Cmp(want) != 0 { - t.Errorf("Inverse property not satisfied: got %s; want %s", z.Format('g', 10), test.z) - } - } -} - -func TestFloatPredicates(t *testing.T) { - for _, test := range []struct { - x string - sign int - signbit, inf bool - }{ - {x: "-Inf", sign: -1, signbit: true, inf: true}, - {x: "-1", sign: -1, signbit: true}, - {x: "-0", signbit: true}, - {x: "0"}, - {x: "1", sign: 1}, - {x: "+Inf", sign: 1, inf: true}, - } { - x := makeFloat(test.x) - if got := x.Signbit(); got != test.signbit { - t.Errorf("(%s).Signbit() = %v; want %v", test.x, got, test.signbit) - } - if got := x.Sign(); got != test.sign { - t.Errorf("(%s).Sign() = %d; want %d", test.x, got, test.sign) - } - if got := x.IsInf(); got != test.inf { - t.Errorf("(%s).IsInf() = %v; want %v", test.x, got, test.inf) - } - } -} - -func TestFloatIsInt(t *testing.T) { - for _, test := range []string{ - "0 int", - "-0 int", - "1 int", - "-1 int", - "0.5", - "1.23", - "1.23e1", - "1.23e2 int", - "0.000000001e+8", - "0.000000001e+9 int", - "1.2345e200 int", - "Inf", - "+Inf", - "-Inf", - } { - s := strings.TrimSuffix(test, " int") - want := s != test - if got := makeFloat(s).IsInt(); got != want { - t.Errorf("%s.IsInt() == %t", s, got) - } - } -} - -func fromBinary(s string) int64 { - x, err := strconv.ParseInt(s, 2, 64) - if err != nil { - panic(err) - } - return x -} - -func toBinary(x int64) string { - return strconv.FormatInt(x, 2) -} - -func testFloatRound(t *testing.T, x, r int64, prec uint, mode RoundingMode) { - // verify test data - var ok bool - switch mode { - case ToNearestEven, ToNearestAway: - ok = true // nothing to do for now - case ToZero: - if x < 0 { - ok = r >= x - } else { - ok = r <= x - } - case AwayFromZero: - if x < 0 { - ok = r <= x - } else { - ok = r >= x - } - case ToNegativeInf: - ok = r <= x - case ToPositiveInf: - ok = r >= x - default: - panic("unreachable") - } - if !ok { - t.Fatalf("incorrect test data for prec = %d, %s: x = %s, r = %s", prec, mode, toBinary(x), toBinary(r)) - } - - // compute expected accuracy - a := Exact - switch { - case r < x: - a = Below - case r > x: - a = Above - } - - // round - f := new(Float).SetMode(mode).SetInt64(x).SetPrec(prec) - - // check result - r1 := f.int64() - p1 := f.Prec() - a1 := f.Acc() - if r1 != r || p1 != prec || a1 != a { - t.Errorf("round %s (%d bits, %s) incorrect: got %s (%d bits, %s); want %s (%d bits, %s)", - toBinary(x), prec, mode, - toBinary(r1), p1, a1, - toBinary(r), prec, a) - return - } - - // g and f should be the same - // (rounding by SetPrec after SetInt64 using default precision - // should be the same as rounding by SetInt64 after setting the - // precision) - g := new(Float).SetMode(mode).SetPrec(prec).SetInt64(x) - if !alike(g, f) { - t.Errorf("round %s (%d bits, %s) not symmetric: got %s and %s; want %s", - toBinary(x), prec, mode, - toBinary(g.int64()), - toBinary(r1), - toBinary(r), - ) - return - } - - // h and f should be the same - // (repeated rounding should be idempotent) - h := new(Float).SetMode(mode).SetPrec(prec).Set(f) - if !alike(h, f) { - t.Errorf("round %s (%d bits, %s) not idempotent: got %s and %s; want %s", - toBinary(x), prec, mode, - toBinary(h.int64()), - toBinary(r1), - toBinary(r), - ) - return - } -} - -// TestFloatRound tests basic rounding. -func TestFloatRound(t *testing.T) { - for _, test := range []struct { - prec uint - x, zero, neven, naway, away string // input, results rounded to prec bits - }{ - {5, "1000", "1000", "1000", "1000", "1000"}, - {5, "1001", "1001", "1001", "1001", "1001"}, - {5, "1010", "1010", "1010", "1010", "1010"}, - {5, "1011", "1011", "1011", "1011", "1011"}, - {5, "1100", "1100", "1100", "1100", "1100"}, - {5, "1101", "1101", "1101", "1101", "1101"}, - {5, "1110", "1110", "1110", "1110", "1110"}, - {5, "1111", "1111", "1111", "1111", "1111"}, - - {4, "1000", "1000", "1000", "1000", "1000"}, - {4, "1001", "1001", "1001", "1001", "1001"}, - {4, "1010", "1010", "1010", "1010", "1010"}, - {4, "1011", "1011", "1011", "1011", "1011"}, - {4, "1100", "1100", "1100", "1100", "1100"}, - {4, "1101", "1101", "1101", "1101", "1101"}, - {4, "1110", "1110", "1110", "1110", "1110"}, - {4, "1111", "1111", "1111", "1111", "1111"}, - - {3, "1000", "1000", "1000", "1000", "1000"}, - {3, "1001", "1000", "1000", "1010", "1010"}, - {3, "1010", "1010", "1010", "1010", "1010"}, - {3, "1011", "1010", "1100", "1100", "1100"}, - {3, "1100", "1100", "1100", "1100", "1100"}, - {3, "1101", "1100", "1100", "1110", "1110"}, - {3, "1110", "1110", "1110", "1110", "1110"}, - {3, "1111", "1110", "10000", "10000", "10000"}, - - {3, "1000001", "1000000", "1000000", "1000000", "1010000"}, - {3, "1001001", "1000000", "1010000", "1010000", "1010000"}, - {3, "1010001", "1010000", "1010000", "1010000", "1100000"}, - {3, "1011001", "1010000", "1100000", "1100000", "1100000"}, - {3, "1100001", "1100000", "1100000", "1100000", "1110000"}, - {3, "1101001", "1100000", "1110000", "1110000", "1110000"}, - {3, "1110001", "1110000", "1110000", "1110000", "10000000"}, - {3, "1111001", "1110000", "10000000", "10000000", "10000000"}, - - {2, "1000", "1000", "1000", "1000", "1000"}, - {2, "1001", "1000", "1000", "1000", "1100"}, - {2, "1010", "1000", "1000", "1100", "1100"}, - {2, "1011", "1000", "1100", "1100", "1100"}, - {2, "1100", "1100", "1100", "1100", "1100"}, - {2, "1101", "1100", "1100", "1100", "10000"}, - {2, "1110", "1100", "10000", "10000", "10000"}, - {2, "1111", "1100", "10000", "10000", "10000"}, - - {2, "1000001", "1000000", "1000000", "1000000", "1100000"}, - {2, "1001001", "1000000", "1000000", "1000000", "1100000"}, - {2, "1010001", "1000000", "1100000", "1100000", "1100000"}, - {2, "1011001", "1000000", "1100000", "1100000", "1100000"}, - {2, "1100001", "1100000", "1100000", "1100000", "10000000"}, - {2, "1101001", "1100000", "1100000", "1100000", "10000000"}, - {2, "1110001", "1100000", "10000000", "10000000", "10000000"}, - {2, "1111001", "1100000", "10000000", "10000000", "10000000"}, - - {1, "1000", "1000", "1000", "1000", "1000"}, - {1, "1001", "1000", "1000", "1000", "10000"}, - {1, "1010", "1000", "1000", "1000", "10000"}, - {1, "1011", "1000", "1000", "1000", "10000"}, - {1, "1100", "1000", "10000", "10000", "10000"}, - {1, "1101", "1000", "10000", "10000", "10000"}, - {1, "1110", "1000", "10000", "10000", "10000"}, - {1, "1111", "1000", "10000", "10000", "10000"}, - - {1, "1000001", "1000000", "1000000", "1000000", "10000000"}, - {1, "1001001", "1000000", "1000000", "1000000", "10000000"}, - {1, "1010001", "1000000", "1000000", "1000000", "10000000"}, - {1, "1011001", "1000000", "1000000", "1000000", "10000000"}, - {1, "1100001", "1000000", "10000000", "10000000", "10000000"}, - {1, "1101001", "1000000", "10000000", "10000000", "10000000"}, - {1, "1110001", "1000000", "10000000", "10000000", "10000000"}, - {1, "1111001", "1000000", "10000000", "10000000", "10000000"}, - } { - x := fromBinary(test.x) - z := fromBinary(test.zero) - e := fromBinary(test.neven) - n := fromBinary(test.naway) - a := fromBinary(test.away) - prec := test.prec - - testFloatRound(t, x, z, prec, ToZero) - testFloatRound(t, x, e, prec, ToNearestEven) - testFloatRound(t, x, n, prec, ToNearestAway) - testFloatRound(t, x, a, prec, AwayFromZero) - - testFloatRound(t, x, z, prec, ToNegativeInf) - testFloatRound(t, x, a, prec, ToPositiveInf) - - testFloatRound(t, -x, -a, prec, ToNegativeInf) - testFloatRound(t, -x, -z, prec, ToPositiveInf) - } -} - -// TestFloatRound24 tests that rounding a float64 to 24 bits -// matches IEEE-754 rounding to nearest when converting a -// float64 to a float32 (excluding denormal numbers). -func TestFloatRound24(t *testing.T) { - const x0 = 1<<26 - 0x10 // 11...110000 (26 bits) - for d := 0; d <= 0x10; d++ { - x := float64(x0 + d) - f := new(Float).SetPrec(24).SetFloat64(x) - got, _ := f.Float32() - want := float32(x) - if got != want { - t.Errorf("Round(%g, 24) = %g; want %g", x, got, want) - } - } -} - -func TestFloatSetUint64(t *testing.T) { - for _, want := range []uint64{ - 0, - 1, - 2, - 10, - 100, - 1<<32 - 1, - 1 << 32, - 1<<64 - 1, - } { - var f Float - f.SetUint64(want) - if got := f.uint64(); got != want { - t.Errorf("got %#x (%s); want %#x", got, f.Format('p', 0), want) - } - } - - // test basic rounding behavior (exhaustive rounding testing is done elsewhere) - const x uint64 = 0x8765432187654321 // 64 bits needed - for prec := uint(1); prec <= 64; prec++ { - f := new(Float).SetPrec(prec).SetMode(ToZero).SetUint64(x) - got := f.uint64() - want := x &^ (1<<(64-prec) - 1) // cut off (round to zero) low 64-prec bits - if got != want { - t.Errorf("got %#x (%s); want %#x", got, f.Format('p', 0), want) - } - } -} - -func TestFloatSetInt64(t *testing.T) { - for _, want := range []int64{ - 0, - 1, - 2, - 10, - 100, - 1<<32 - 1, - 1 << 32, - 1<<63 - 1, - } { - for i := range [2]int{} { - if i&1 != 0 { - want = -want - } - var f Float - f.SetInt64(want) - if got := f.int64(); got != want { - t.Errorf("got %#x (%s); want %#x", got, f.Format('p', 0), want) - } - } - } - - // test basic rounding behavior (exhaustive rounding testing is done elsewhere) - const x int64 = 0x7654321076543210 // 63 bits needed - for prec := uint(1); prec <= 63; prec++ { - f := new(Float).SetPrec(prec).SetMode(ToZero).SetInt64(x) - got := f.int64() - want := x &^ (1<<(63-prec) - 1) // cut off (round to zero) low 63-prec bits - if got != want { - t.Errorf("got %#x (%s); want %#x", got, f.Format('p', 0), want) - } - } -} - -func TestFloatSetFloat64(t *testing.T) { - for _, want := range []float64{ - 0, - 1, - 2, - 12345, - 1e10, - 1e100, - 3.14159265e10, - 2.718281828e-123, - 1.0 / 3, - math.MaxFloat32, - math.MaxFloat64, - math.SmallestNonzeroFloat32, - math.SmallestNonzeroFloat64, - math.Inf(-1), - math.Inf(0), - -math.Inf(1), - } { - for i := range [2]int{} { - if i&1 != 0 { - want = -want - } - var f Float - f.SetFloat64(want) - if got, acc := f.Float64(); got != want || acc != Exact { - t.Errorf("got %g (%s, %s); want %g (Exact)", got, f.Format('p', 0), acc, want) - } - } - } - - // test basic rounding behavior (exhaustive rounding testing is done elsewhere) - const x uint64 = 0x8765432143218 // 53 bits needed - for prec := uint(1); prec <= 52; prec++ { - f := new(Float).SetPrec(prec).SetMode(ToZero).SetFloat64(float64(x)) - got, _ := f.Float64() - want := float64(x &^ (1<<(52-prec) - 1)) // cut off (round to zero) low 53-prec bits - if got != want { - t.Errorf("got %g (%s); want %g", got, f.Format('p', 0), want) - } - } - - // test NaN - defer func() { - if p, ok := recover().(ErrNaN); !ok { - t.Errorf("got %v; want ErrNaN panic", p) - } - }() - var f Float - f.SetFloat64(math.NaN()) - // should not reach here - t.Errorf("got %s; want ErrNaN panic", f.Format('p', 0)) -} - -func TestFloatSetInt(t *testing.T) { - for _, want := range []string{ - "0", - "1", - "-1", - "1234567890", - "123456789012345678901234567890", - "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", - } { - var x Int - _, ok := x.SetString(want, 0) - if !ok { - t.Errorf("invalid integer %s", want) - continue - } - n := x.BitLen() - - var f Float - f.SetInt(&x) - - // check precision - if n < 64 { - n = 64 - } - if prec := f.Prec(); prec != uint(n) { - t.Errorf("got prec = %d; want %d", prec, n) - } - - // check value - got := f.Format('g', 100) - if got != want { - t.Errorf("got %s (%s); want %s", got, f.Format('p', 0), want) - } - } - - // TODO(gri) test basic rounding behavior -} - -func TestFloatSetRat(t *testing.T) { - for _, want := range []string{ - "0", - "1", - "-1", - "1234567890", - "123456789012345678901234567890", - "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", - "1.2", - "3.14159265", - // TODO(gri) expand - } { - var x Rat - _, ok := x.SetString(want) - if !ok { - t.Errorf("invalid fraction %s", want) - continue - } - n := max(x.Num().BitLen(), x.Denom().BitLen()) - - var f1, f2 Float - f2.SetPrec(1000) - f1.SetRat(&x) - f2.SetRat(&x) - - // check precision when set automatically - if n < 64 { - n = 64 - } - if prec := f1.Prec(); prec != uint(n) { - t.Errorf("got prec = %d; want %d", prec, n) - } - - got := f2.Format('g', 100) - if got != want { - t.Errorf("got %s (%s); want %s", got, f2.Format('p', 0), want) - } - } -} - -func TestFloatSetInf(t *testing.T) { - var f Float - for _, test := range []struct { - signbit bool - prec uint - want string - }{ - {false, 0, "+Inf"}, - {true, 0, "-Inf"}, - {false, 10, "+Inf"}, - {true, 30, "-Inf"}, - } { - x := f.SetPrec(test.prec).SetInf(test.signbit) - if got := x.String(); got != test.want || x.Prec() != test.prec { - t.Errorf("SetInf(%v) = %s (prec = %d); want %s (prec = %d)", test.signbit, got, x.Prec(), test.want, test.prec) - } - } -} - -func TestFloatUint64(t *testing.T) { - for _, test := range []struct { - x string - out uint64 - acc Accuracy - }{ - {"-Inf", 0, Above}, - {"-1", 0, Above}, - {"-1e-1000", 0, Above}, - {"-0", 0, Exact}, - {"0", 0, Exact}, - {"1e-1000", 0, Below}, - {"1", 1, Exact}, - {"1.000000000000000000001", 1, Below}, - {"12345.0", 12345, Exact}, - {"12345.000000000000000000001", 12345, Below}, - {"18446744073709551615", 18446744073709551615, Exact}, - {"18446744073709551615.000000000000000000001", math.MaxUint64, Below}, - {"18446744073709551616", math.MaxUint64, Below}, - {"1e10000", math.MaxUint64, Below}, - {"+Inf", math.MaxUint64, Below}, - } { - x := makeFloat(test.x) - out, acc := x.Uint64() - if out != test.out || acc != test.acc { - t.Errorf("%s: got %d (%s); want %d (%s)", test.x, out, acc, test.out, test.acc) - } - } -} - -func TestFloatInt64(t *testing.T) { - for _, test := range []struct { - x string - out int64 - acc Accuracy - }{ - {"-Inf", math.MinInt64, Above}, - {"-1e10000", math.MinInt64, Above}, - {"-9223372036854775809", math.MinInt64, Above}, - {"-9223372036854775808.000000000000000000001", math.MinInt64, Above}, - {"-9223372036854775808", -9223372036854775808, Exact}, - {"-9223372036854775807.000000000000000000001", -9223372036854775807, Above}, - {"-9223372036854775807", -9223372036854775807, Exact}, - {"-12345.000000000000000000001", -12345, Above}, - {"-12345.0", -12345, Exact}, - {"-1.000000000000000000001", -1, Above}, - {"-1.5", -1, Above}, - {"-1", -1, Exact}, - {"-1e-1000", 0, Above}, - {"0", 0, Exact}, - {"1e-1000", 0, Below}, - {"1", 1, Exact}, - {"1.000000000000000000001", 1, Below}, - {"1.5", 1, Below}, - {"12345.0", 12345, Exact}, - {"12345.000000000000000000001", 12345, Below}, - {"9223372036854775807", 9223372036854775807, Exact}, - {"9223372036854775807.000000000000000000001", math.MaxInt64, Below}, - {"9223372036854775808", math.MaxInt64, Below}, - {"1e10000", math.MaxInt64, Below}, - {"+Inf", math.MaxInt64, Below}, - } { - x := makeFloat(test.x) - out, acc := x.Int64() - if out != test.out || acc != test.acc { - t.Errorf("%s: got %d (%s); want %d (%s)", test.x, out, acc, test.out, test.acc) - } - } -} - -func TestFloatFloat32(t *testing.T) { - for _, test := range []struct { - x string - out float32 - acc Accuracy - }{ - {"-Inf", float32(math.Inf(-1)), Exact}, - {"-0x1.ffffff0p2147483646", float32(-math.Inf(+1)), Below}, // overflow in rounding - {"-1e10000", float32(math.Inf(-1)), Below}, // overflow - {"-0x1p128", float32(math.Inf(-1)), Below}, // overflow - {"-0x1.ffffff0p127", float32(-math.Inf(+1)), Below}, // overflow - {"-0x1.fffffe8p127", -math.MaxFloat32, Above}, - {"-0x1.fffffe0p127", -math.MaxFloat32, Exact}, - {"-12345.000000000000000000001", -12345, Above}, - {"-12345.0", -12345, Exact}, - {"-1.000000000000000000001", -1, Above}, - {"-1", -1, Exact}, - {"-0x0.000002p-126", -math.SmallestNonzeroFloat32, Exact}, - {"-0x0.000002p-127", -0, Above}, // underflow - {"-1e-1000", -0, Above}, // underflow - {"0", 0, Exact}, - {"1e-1000", 0, Below}, // underflow - {"0x0.000002p-127", 0, Below}, // underflow - {"0x0.000002p-126", math.SmallestNonzeroFloat32, Exact}, - {"1", 1, Exact}, - {"1.000000000000000000001", 1, Below}, - {"12345.0", 12345, Exact}, - {"12345.000000000000000000001", 12345, Below}, - {"0x1.fffffe0p127", math.MaxFloat32, Exact}, - {"0x1.fffffe8p127", math.MaxFloat32, Below}, - {"0x1.ffffff0p127", float32(math.Inf(+1)), Above}, // overflow - {"0x1p128", float32(math.Inf(+1)), Above}, // overflow - {"1e10000", float32(math.Inf(+1)), Above}, // overflow - {"0x1.ffffff0p2147483646", float32(math.Inf(+1)), Above}, // overflow in rounding - {"+Inf", float32(math.Inf(+1)), Exact}, - } { - // conversion should match strconv where syntax is agreeable - if f, err := strconv.ParseFloat(test.x, 32); err == nil && float32(f) != test.out { - t.Errorf("%s: got %g; want %g (incorrect test data)", test.x, f, test.out) - } - - x := makeFloat(test.x) - out, acc := x.Float32() - if out != test.out || acc != test.acc { - t.Errorf("%s: got %g (%#x, %s); want %g (%#x, %s)", test.x, out, math.Float32bits(out), acc, test.out, math.Float32bits(test.out), test.acc) - } - - // test that x.SetFloat64(float64(f)).Float32() == f - var x2 Float - out2, acc2 := x2.SetFloat64(float64(out)).Float32() - if out2 != out || acc2 != Exact { - t.Errorf("idempotency test: got %g (%s); want %g (Exact)", out2, acc2, out) - } - } -} - -func TestFloatFloat64(t *testing.T) { - const smallestNormalFloat64 = 2.2250738585072014e-308 // 1p-1022 - for _, test := range []struct { - x string - 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}, - {"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 - {"0x1.fffffffffffff8p2147483646", math.Inf(+1), Above}, // overflow in rounding - {"+Inf", math.Inf(+1), Exact}, - - // selected denormalized values that were handled incorrectly in the past - {"0x.fffffffffffffp-1022", smallestNormalFloat64 - math.SmallestNonzeroFloat64, Exact}, - {"4503599627370495p-1074", smallestNormalFloat64 - math.SmallestNonzeroFloat64, Exact}, - - // http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/ - {"2.2250738585072011e-308", 2.225073858507201e-308, Below}, - // 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) - } - - 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) - } - - // 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) - } - } -} - -func TestFloatInt(t *testing.T) { - for _, test := range []struct { - x string - want string - acc Accuracy - }{ - {"0", "0", Exact}, - {"+0", "0", Exact}, - {"-0", "0", Exact}, - {"Inf", "nil", Below}, - {"+Inf", "nil", Below}, - {"-Inf", "nil", Above}, - {"1", "1", Exact}, - {"-1", "-1", Exact}, - {"1.23", "1", Below}, - {"-1.23", "-1", Above}, - {"123e-2", "1", Below}, - {"123e-3", "0", Below}, - {"123e-4", "0", Below}, - {"1e-1000", "0", Below}, - {"-1e-1000", "0", Above}, - {"1e+10", "10000000000", Exact}, - {"1e+100", "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", Exact}, - } { - x := makeFloat(test.x) - res, acc := x.Int(nil) - got := "nil" - if res != nil { - got = res.String() - } - if got != test.want || acc != test.acc { - t.Errorf("%s: got %s (%s); want %s (%s)", test.x, got, acc, test.want, test.acc) - } - } - - // check that supplied *Int is used - for _, f := range []string{"0", "1", "-1", "1234"} { - x := makeFloat(f) - i := new(Int) - if res, _ := x.Int(i); res != i { - t.Errorf("(%s).Int is not using supplied *Int", f) - } - } -} - -func TestFloatRat(t *testing.T) { - for _, test := range []struct { - x, want string - acc Accuracy - }{ - {"0", "0/1", Exact}, - {"+0", "0/1", Exact}, - {"-0", "0/1", Exact}, - {"Inf", "nil", Below}, - {"+Inf", "nil", Below}, - {"-Inf", "nil", Above}, - {"1", "1/1", Exact}, - {"-1", "-1/1", Exact}, - {"1.25", "5/4", Exact}, - {"-1.25", "-5/4", Exact}, - {"1e10", "10000000000/1", Exact}, - {"1p10", "1024/1", Exact}, - {"-1p-10", "-1/1024", Exact}, - {"3.14159265", "7244019449799623199/2305843009213693952", Exact}, - } { - x := makeFloat(test.x).SetPrec(64) - res, acc := x.Rat(nil) - got := "nil" - if res != nil { - got = res.String() - } - if got != test.want { - t.Errorf("%s: got %s; want %s", test.x, got, test.want) - continue - } - if acc != test.acc { - t.Errorf("%s: got %s; want %s", test.x, acc, test.acc) - continue - } - - // inverse conversion - if res != nil { - got := new(Float).SetPrec(64).SetRat(res) - if got.Cmp(x) != 0 { - t.Errorf("%s: got %s; want %s", test.x, got, x) - } - } - } - - // check that supplied *Rat is used - for _, f := range []string{"0", "1", "-1", "1234"} { - x := makeFloat(f) - r := new(Rat) - if res, _ := x.Rat(r); res != r { - t.Errorf("(%s).Rat is not using supplied *Rat", f) - } - } -} - -func TestFloatAbs(t *testing.T) { - for _, test := range []string{ - "0", - "1", - "1234", - "1.23e-2", - "1e-1000", - "1e1000", - "Inf", - } { - p := makeFloat(test) - a := new(Float).Abs(p) - if !alike(a, p) { - t.Errorf("%s: got %s; want %s", test, a.Format('g', 10), test) - } - - n := makeFloat("-" + test) - a.Abs(n) - if !alike(a, p) { - t.Errorf("-%s: got %s; want %s", test, a.Format('g', 10), test) - } - } -} - -func TestFloatNeg(t *testing.T) { - for _, test := range []string{ - "0", - "1", - "1234", - "1.23e-2", - "1e-1000", - "1e1000", - "Inf", - } { - p1 := makeFloat(test) - n1 := makeFloat("-" + test) - n2 := new(Float).Neg(p1) - p2 := new(Float).Neg(n2) - if !alike(n2, n1) { - t.Errorf("%s: got %s; want %s", test, n2.Format('g', 10), n1.Format('g', 10)) - } - if !alike(p2, p1) { - t.Errorf("%s: got %s; want %s", test, p2.Format('g', 10), p1.Format('g', 10)) - } - } -} - -func TestFloatInc(t *testing.T) { - const n = 10 - for _, prec := range precList { - if 1< y: - want = +1 - } - if got != want { - t.Errorf("(%g).Cmp(%g) = %v; want %v", x, y, got, want) - } - } - } - } -} diff --git a/src/cmd/internal/gc/big/floatconv.go b/src/cmd/internal/gc/big/floatconv.go deleted file mode 100644 index b929d1202c..0000000000 --- a/src/cmd/internal/gc/big/floatconv.go +++ /dev/null @@ -1,373 +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. - -// This file implements float-to-string conversion functions. - -package big - -import ( - "fmt" - "io" - "strconv" - "strings" -) - -// SetString sets z to the value of s and returns z and a boolean indicating -// success. s must be a floating-point number of the same format as accepted -// by Scan, with number prefixes permitted. -func (z *Float) SetString(s string) (*Float, bool) { - r := strings.NewReader(s) - - f, _, err := z.Scan(r, 0) - if err != nil { - return nil, false - } - - // there should be no unread characters left - if _, err = r.ReadByte(); err != io.EOF { - return nil, false - } - - return f, true -} - -// Scan scans the number corresponding to the longest possible prefix -// of r representing a floating-point number with a mantissa in the -// given conversion base (the exponent is always a decimal number). -// It sets z to the (possibly rounded) value of the corresponding -// floating-point number, and returns z, the actual base b, and an -// error err, if any. If z's precision is 0, it is changed to 64 -// before rounding takes effect. The number must be of the form: -// -// number = [ sign ] [ prefix ] mantissa [ exponent ] . -// sign = "+" | "-" . -// prefix = "0" ( "x" | "X" | "b" | "B" ) . -// mantissa = digits | digits "." [ digits ] | "." digits . -// exponent = ( "E" | "e" | "p" ) [ sign ] digits . -// digits = digit { digit } . -// digit = "0" ... "9" | "a" ... "z" | "A" ... "Z" . -// -// The base argument must be 0, 2, 10, or 16. Providing an invalid base -// argument will lead to a run-time panic. -// -// For base 0, the number prefix determines the actual base: A prefix of -// "0x" or "0X" selects base 16, and a "0b" or "0B" prefix selects -// base 2; otherwise, the actual base is 10 and no prefix is accepted. -// The octal prefix "0" is not supported (a leading "0" is simply -// considered a "0"). -// -// A "p" exponent indicates a binary (rather then decimal) exponent; -// for instance "0x1.fffffffffffffp1023" (using base 0) represents the -// maximum float64 value. For hexadecimal mantissae, the exponent must -// be binary, if present (an "e" or "E" exponent indicator cannot be -// distinguished from a mantissa digit). -// -// The returned *Float f is nil and the value of z is valid but not -// defined if an error is reported. -// -// BUG(gri) The Float.Scan signature conflicts with Scan(s fmt.ScanState, ch rune) error. -func (z *Float) Scan(r io.ByteScanner, base int) (f *Float, b int, err error) { - prec := z.prec - if prec == 0 { - prec = 64 - } - - // A reasonable value in case of an error. - z.form = zero - - // sign - z.neg, err = scanSign(r) - if err != nil { - return - } - - // mantissa - var fcount int // fractional digit count; valid if <= 0 - z.mant, b, fcount, err = z.mant.scan(r, base, true) - if err != nil { - return - } - - // exponent - var exp int64 - var ebase int - exp, ebase, err = scanExponent(r, true) - if err != nil { - return - } - - // special-case 0 - if len(z.mant) == 0 { - z.prec = prec - z.acc = Exact - z.form = zero - f = z - return - } - // len(z.mant) > 0 - - // The mantissa may have a decimal point (fcount <= 0) and there - // may be a nonzero exponent exp. The decimal point amounts to a - // division by b**(-fcount). An exponent means multiplication by - // ebase**exp. Finally, mantissa normalization (shift left) requires - // a correcting multiplication by 2**(-shiftcount). Multiplications - // are commutative, so we can apply them in any order as long as there - // is no loss of precision. We only have powers of 2 and 10; keep - // track via separate exponents exp2 and exp10. - - // normalize mantissa and get initial binary exponent - var exp2 = int64(len(z.mant))*_W - fnorm(z.mant) - - // determine binary or decimal exponent contribution of decimal point - var exp10 int64 - if fcount < 0 { - // The mantissa has a "decimal" point ddd.dddd; and - // -fcount is the number of digits to the right of '.'. - // Adjust relevant exponent accodingly. - switch b { - case 16: - fcount *= 4 // hexadecimal digits are 4 bits each - fallthrough - case 2: - exp2 += int64(fcount) - default: // b == 10 - exp10 = int64(fcount) - } - // we don't need fcount anymore - } - - // take actual exponent into account - if ebase == 2 { - exp2 += exp - } else { // ebase == 10 - exp10 += exp - } - // we don't need exp anymore - - // apply 2**exp2 - if MinExp <= exp2 && exp2 <= MaxExp { - z.prec = prec - z.form = finite - z.exp = int32(exp2) - f = z - } else { - err = fmt.Errorf("exponent overflow") - return - } - - if exp10 == 0 { - // no decimal exponent to consider - z.round(0) - return - } - // exp10 != 0 - - // apply 10**exp10 - p := new(Float).SetPrec(z.Prec() + 64) // use more bits for p -- TODO(gri) what is the right number? - if exp10 < 0 { - z.uquo(z, p.pow10(-exp10)) - } else { - z.umul(z, p.pow10(exp10)) - } - - return -} - -// These powers of 10 can be represented exactly as a float64. -var pow10tab = [...]float64{ - 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, - 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, -} - -// pow10 sets z to 10**n and returns z. -// n must not be negative. -func (z *Float) pow10(n int64) *Float { - if n < 0 { - panic("pow10 called with negative argument") - } - - const m = int64(len(pow10tab) - 1) - if n <= m { - return z.SetFloat64(pow10tab[n]) - } - // n > m - - z.SetFloat64(pow10tab[m]) - n -= m - - // use more bits for f than for z - // TODO(gri) what is the right number? - f := new(Float).SetPrec(z.Prec() + 64).SetInt64(10) - - for n > 0 { - if n&1 != 0 { - z.Mul(z, f) - } - f.Mul(f, f) - n >>= 1 - } - - return z -} - -// Parse is like z.Scan(r, base), but instead of reading from an -// io.ByteScanner, it parses the string s. An error is also returned -// if the string contains invalid or trailing bytes not belonging to -// the number. -func (z *Float) Parse(s string, base int) (f *Float, b int, err error) { - r := strings.NewReader(s) - - if f, b, err = z.Scan(r, base); err != nil { - return - } - - // entire string must have been consumed - if ch, err2 := r.ReadByte(); err2 == nil { - err = fmt.Errorf("expected end of string, found %q", ch) - } else if err2 != io.EOF { - err = err2 - } - - return -} - -// ScanFloat is like f.Scan(r, base) with f set to the given precision -// and rounding mode. -func ScanFloat(r io.ByteScanner, base int, prec uint, mode RoundingMode) (f *Float, b int, err error) { - return new(Float).SetPrec(prec).SetMode(mode).Scan(r, base) -} - -// ParseFloat is like f.Parse(s, base) with f set to the given precision -// and rounding mode. -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. -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') - return strconv.AppendInt(buf, int64(x.exp), 10) -} diff --git a/src/cmd/internal/gc/big/floatconv_test.go b/src/cmd/internal/gc/big/floatconv_test.go deleted file mode 100644 index 96c01eed81..0000000000 --- a/src/cmd/internal/gc/big/floatconv_test.go +++ /dev/null @@ -1,397 +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 big - -import ( - "math" - "strconv" - "testing" -) - -func TestFloatSetFloat64String(t *testing.T) { - for _, test := range []struct { - s string - x float64 - }{ - // basics - {"0", 0}, - {"-0", -0}, - {"+0", 0}, - {"1", 1}, - {"-1", -1}, - {"+1", 1}, - {"1.234", 1.234}, - {"-1.234", -1.234}, - {"+1.234", 1.234}, - {".1", 0.1}, - {"1.", 1}, - {"+1.", 1}, - - // various zeros - {"0e100", 0}, - {"-0e+100", 0}, - {"+0e-100", 0}, - {"0E100", 0}, - {"-0E+100", 0}, - {"+0E-100", 0}, - - // various decimal exponent formats - {"1.e10", 1e10}, - {"1e+10", 1e10}, - {"+1e-10", 1e-10}, - {"1E10", 1e10}, - {"1.E+10", 1e10}, - {"+1E-10", 1e-10}, - - // misc decimal values - {"3.14159265", 3.14159265}, - {"-687436.79457e-245", -687436.79457e-245}, - {"-687436.79457E245", -687436.79457e245}, - {".0000000000000000000000000000000000000001", 1e-40}, - {"+10000000000000000000000000000000000000000e-0", 1e40}, - - // decimal mantissa, binary exponent - {"0p0", 0}, - {"-0p0", -0}, - {"1p10", 1 << 10}, - {"1p+10", 1 << 10}, - {"+1p-10", 1.0 / (1 << 10)}, - {"1024p-12", 0.25}, - {"-1p10", -1024}, - {"1.5p1", 3}, - - // binary mantissa, decimal exponent - {"0b0", 0}, - {"-0b0", -0}, - {"0b0e+10", 0}, - {"-0b0e-10", -0}, - {"0b1010", 10}, - {"0B1010E2", 1000}, - {"0b.1", 0.5}, - {"0b.001", 0.125}, - {"0b.001e3", 125}, - - // binary mantissa, binary exponent - {"0b0p+10", 0}, - {"-0b0p-10", -0}, - {"0b.1010p4", 10}, - {"0b1p-1", 0.5}, - {"0b001p-3", 0.125}, - {"0b.001p3", 1}, - {"0b0.01p2", 1}, - - // hexadecimal mantissa and exponent - {"0x0", 0}, - {"-0x0", -0}, - {"0x0p+10", 0}, - {"-0x0p-10", -0}, - {"0xff", 255}, - {"0X.8p1", 1}, - {"-0X0.00008p16", -0.5}, - {"0x0.0000000000001p-1022", math.SmallestNonzeroFloat64}, - {"0x1.fffffffffffffp1023", math.MaxFloat64}, - } { - var x Float - x.SetPrec(53) - _, ok := x.SetString(test.s) - if !ok { - t.Errorf("%s: parse error", test.s) - continue - } - f, _ := x.Float64() - want := new(Float).SetFloat64(test.x) - if x.Cmp(want) != 0 { - t.Errorf("%s: got %s (%v); want %v", test.s, &x, f, test.x) - } - } -} - -const ( - below1e23 = 99999999999999974834176 - above1e23 = 100000000000000008388608 -) - -func TestFloat64Format(t *testing.T) { - for _, test := range []struct { - x float64 - format byte - prec int - want string - }{ - {0, 'f', 0, "0"}, - {math.Copysign(0, -1), 'f', 0, "-0"}, - {1, 'f', 0, "1"}, - {-1, 'f', 0, "-1"}, - - {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"}, - - {1.459, 'f', 0, "1"}, - {2.459, 'f', 1, "2.5"}, - {3.459, 'f', 2, "3.46"}, - {4.459, 'f', 3, "4.459"}, - {5.459, 'f', 4, "5.4590"}, - - {0, 'b', 0, "0"}, - {math.Copysign(0, -1), 'b', 0, "-0"}, - {1.0, 'b', 0, "4503599627370496p-52"}, - {-1.0, 'b', 0, "-4503599627370496p-52"}, - {4503599627370496, 'b', 0, "4503599627370496p+0"}, - - {0, 'p', 0, "0"}, - {math.Copysign(0, -1), 'p', 0, "-0"}, - {1024.0, 'p', 0, "0x.8p11"}, - {-1024.0, 'p', 0, "-0x.8p11"}, - - // all test cases below from strconv/ftoa_test.go - {1, 'e', 5, "1.00000e+00"}, - {1, 'f', 5, "1.00000"}, - {1, 'g', 5, "1"}, - // {1, 'g', -1, "1"}, - // {20, 'g', -1, "20"}, - // {1234567.8, 'g', -1, "1.2345678e+06"}, - // {200000, 'g', -1, "200000"}, - // {2000000, 'g', -1, "2e+06"}, - - // g conversion and zero suppression - {400, 'g', 2, "4e+02"}, - {40, 'g', 2, "40"}, - {4, 'g', 2, "4"}, - {.4, 'g', 2, "0.4"}, - {.04, 'g', 2, "0.04"}, - {.004, 'g', 2, "0.004"}, - {.0004, 'g', 2, "0.0004"}, - {.00004, 'g', 2, "4e-05"}, - {.000004, 'g', 2, "4e-06"}, - - {0, 'e', 5, "0.00000e+00"}, - {0, 'f', 5, "0.00000"}, - {0, 'g', 5, "0"}, - // {0, 'g', -1, "0"}, - - {-1, 'e', 5, "-1.00000e+00"}, - {-1, 'f', 5, "-1.00000"}, - {-1, 'g', 5, "-1"}, - // {-1, 'g', -1, "-1"}, - - {12, 'e', 5, "1.20000e+01"}, - {12, 'f', 5, "12.00000"}, - {12, 'g', 5, "12"}, - // {12, 'g', -1, "12"}, - - {123456700, 'e', 5, "1.23457e+08"}, - {123456700, 'f', 5, "123456700.00000"}, - {123456700, 'g', 5, "1.2346e+08"}, - // {123456700, 'g', -1, "1.234567e+08"}, - - {1.2345e6, 'e', 5, "1.23450e+06"}, - {1.2345e6, 'f', 5, "1234500.00000"}, - {1.2345e6, 'g', 5, "1.2345e+06"}, - - {1e23, 'e', 17, "9.99999999999999916e+22"}, - {1e23, 'f', 17, "99999999999999991611392.00000000000000000"}, - {1e23, 'g', 17, "9.9999999999999992e+22"}, - - // {1e23, 'e', -1, "1e+23"}, - // {1e23, 'f', -1, "100000000000000000000000"}, - // {1e23, 'g', -1, "1e+23"}, - - {below1e23, 'e', 17, "9.99999999999999748e+22"}, - {below1e23, 'f', 17, "99999999999999974834176.00000000000000000"}, - {below1e23, 'g', 17, "9.9999999999999975e+22"}, - - // {below1e23, 'e', -1, "9.999999999999997e+22"}, - // {below1e23, 'f', -1, "99999999999999970000000"}, - // {below1e23, 'g', -1, "9.999999999999997e+22"}, - - {above1e23, 'e', 17, "1.00000000000000008e+23"}, - {above1e23, 'f', 17, "100000000000000008388608.00000000000000000"}, - // {above1e23, 'g', 17, "1.0000000000000001e+23"}, - - // {above1e23, 'e', -1, "1.0000000000000001e+23"}, - // {above1e23, 'f', -1, "100000000000000010000000"}, - // {above1e23, 'g', -1, "1.0000000000000001e+23"}, - - // {fdiv(5e-304, 1e20), 'g', -1, "5e-324"}, - // {fdiv(-5e-304, 1e20), 'g', -1, "-5e-324"}, - - // {32, 'g', -1, "32"}, - // {32, 'g', 0, "3e+01"}, - - // {100, 'x', -1, "%x"}, - - // {math.NaN(), 'g', -1, "NaN"}, - // {-math.NaN(), 'g', -1, "NaN"}, - {math.Inf(0), 'g', -1, "+Inf"}, - {math.Inf(-1), 'g', -1, "-Inf"}, - {-math.Inf(0), 'g', -1, "-Inf"}, - - {-1, 'b', -1, "-4503599627370496p-52"}, - - // fixed bugs - {0.9, 'f', 1, "0.9"}, - {0.09, 'f', 1, "0.1"}, - {0.0999, 'f', 1, "0.1"}, - {0.05, 'f', 1, "0.1"}, - {0.05, 'f', 0, "0"}, - {0.5, 'f', 1, "0.5"}, - {0.5, 'f', 0, "0"}, - {1.5, 'f', 0, "2"}, - - // http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/ - // {2.2250738585072012e-308, 'g', -1, "2.2250738585072014e-308"}, - // http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/ - // {2.2250738585072011e-308, 'g', -1, "2.225073858507201e-308"}, - - // Issue 2625. - {383260575764816448, 'f', 0, "383260575764816448"}, - // {383260575764816448, 'g', -1, "3.8326057576481645e+17"}, - } { - f := new(Float).SetFloat64(test.x) - got := f.Format(test.format, test.prec) - if got != test.want { - t.Errorf("%v: got %s; want %s", test, got, test.want) - } - - if test.format == 'b' && test.x == 0 { - continue // 'b' format in strconv.Float requires knowledge of bias for 0.0 - } - if test.format == 'p' { - continue // 'p' format not supported in strconv.Format - } - - // verify that Float format matches strconv format - want := strconv.FormatFloat(test.x, test.format, test.prec, 64) - if got != want { - t.Errorf("%v: got %s; want %s (strconv)", test, got, want) - } - } -} - -func TestFloatFormat(t *testing.T) { - for _, test := range []struct { - x string - prec uint - format byte - digits int - want string - }{ - {"0", 10, 'f', 0, "0"}, - {"-0", 10, 'f', 0, "-0"}, - {"1", 10, 'f', 0, "1"}, - {"-1", 10, 'f', 0, "-1"}, - - {"1.459", 100, 'e', 0, "1e+00"}, - {"2.459", 100, 'e', 1, "2.5e+00"}, - {"3.459", 100, 'e', 2, "3.46e+00"}, - {"4.459", 100, 'e', 3, "4.459e+00"}, - {"5.459", 100, 'e', 4, "5.4590e+00"}, - - {"1.459", 100, 'E', 0, "1E+00"}, - {"2.459", 100, 'E', 1, "2.5E+00"}, - {"3.459", 100, 'E', 2, "3.46E+00"}, - {"4.459", 100, 'E', 3, "4.459E+00"}, - {"5.459", 100, 'E', 4, "5.4590E+00"}, - - {"1.459", 100, 'f', 0, "1"}, - {"2.459", 100, 'f', 1, "2.5"}, - {"3.459", 100, 'f', 2, "3.46"}, - {"4.459", 100, 'f', 3, "4.459"}, - {"5.459", 100, 'f', 4, "5.4590"}, - - {"1.459", 100, 'g', 0, "1"}, - {"2.459", 100, 'g', 1, "2"}, - {"3.459", 100, 'g', 2, "3.5"}, - {"4.459", 100, 'g', 3, "4.46"}, - {"5.459", 100, 'g', 4, "5.459"}, - - {"1459", 53, 'g', 0, "1e+03"}, - {"2459", 53, 'g', 1, "2e+03"}, - {"3459", 53, 'g', 2, "3.5e+03"}, - {"4459", 53, 'g', 3, "4.46e+03"}, - {"5459", 53, 'g', 4, "5459"}, - - {"1459", 53, 'G', 0, "1E+03"}, - {"2459", 53, 'G', 1, "2E+03"}, - {"3459", 53, 'G', 2, "3.5E+03"}, - {"4459", 53, 'G', 3, "4.46E+03"}, - {"5459", 53, 'G', 4, "5459"}, - - {"3", 10, 'e', 40, "3.0000000000000000000000000000000000000000e+00"}, - {"3", 10, 'f', 40, "3.0000000000000000000000000000000000000000"}, - {"3", 10, 'g', 40, "3"}, - - {"3e40", 100, 'e', 40, "3.0000000000000000000000000000000000000000e+40"}, - {"3e40", 100, 'f', 4, "30000000000000000000000000000000000000000.0000"}, - {"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"}, - {"1e-1000000", 64, 'p', 0, "0x.efb4542cc8ca418ap-3321928"}, - {"1e-1000000000", 64, 'p', 0, "0x.8a64dd983a4c7dabp-1538481528"}, - - // TODO(gri) need tests for actual large Floats - - {"0", 53, 'b', 0, "0"}, - {"-0", 53, 'b', 0, "-0"}, - {"1.0", 53, 'b', 0, "4503599627370496p-52"}, - {"-1.0", 53, 'b', 0, "-4503599627370496p-52"}, - {"4503599627370496", 53, 'b', 0, "4503599627370496p+0"}, - - // issue 9939 - {"3", 350, 'b', 0, "1720123961992553633708115671476565205597423741876210842803191629540192157066363606052513914832594264915968p-348"}, - {"03", 350, 'b', 0, "1720123961992553633708115671476565205597423741876210842803191629540192157066363606052513914832594264915968p-348"}, - {"3.", 350, 'b', 0, "1720123961992553633708115671476565205597423741876210842803191629540192157066363606052513914832594264915968p-348"}, - {"3.0", 350, 'b', 0, "1720123961992553633708115671476565205597423741876210842803191629540192157066363606052513914832594264915968p-348"}, - {"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"}, - - {"0", 64, 'p', 0, "0"}, - {"-0", 64, 'p', 0, "-0"}, - {"1024.0", 64, 'p', 0, "0x.8p11"}, - {"-1024.0", 64, 'p', 0, "-0x.8p11"}, - - // unsupported format - {"3.14", 64, 'x', 0, "%x"}, - } { - f, _, err := ParseFloat(test.x, 0, test.prec, ToNearestEven) - if err != nil { - t.Errorf("%v: %s", test, err) - continue - } - - got := f.Format(test.format, test.digits) - if got != test.want { - t.Errorf("%v: got %s; want %s", test, got, test.want) - } - - // compare with strconv.FormatFloat output if possible - // ('p' format is not supported by strconv.FormatFloat, - // and its output for 0.0 prints a biased exponent value - // as in 0p-1074 which makes no sense to emulate here) - if test.prec == 53 && test.format != 'p' && f.Sign() != 0 { - f64, acc := f.Float64() - if acc != Exact { - t.Errorf("%v: expected exact conversion to float64", test) - continue - } - got := strconv.FormatFloat(f64, test.format, test.digits, 64) - if got != test.want { - t.Errorf("%v: got %s; want %s", test, got, test.want) - } - } - } -} diff --git a/src/cmd/internal/gc/big/floatexample_test.go b/src/cmd/internal/gc/big/floatexample_test.go deleted file mode 100644 index 7db10238bc..0000000000 --- a/src/cmd/internal/gc/big/floatexample_test.go +++ /dev/null @@ -1,111 +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 big_test - -import ( - "fmt" - "math" - "math/big" -) - -func ExampleFloat_Add() { - // Operating on numbers of different precision. - var x, y, z big.Float - x.SetInt64(1000) // x is automatically set to 64bit precision - y.SetFloat64(2.718281828) // y is automatically set to 53bit precision - z.SetPrec(32) - z.Add(&x, &y) - fmt.Printf("x = %s (%s, prec = %d, acc = %s)\n", &x, x.Format('p', 0), x.Prec(), x.Acc()) - 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) -} - -func Example_Shift() { - // Implementing Float "shift" by modifying the (binary) exponents directly. - for s := -5; s <= 5; s++ { - x := big.NewFloat(0.5) - x.SetMantExp(x, x.MantExp(nil)+s) // shift x by s - fmt.Println(x) - } - // Output: - // 0.015625 - // 0.03125 - // 0.0625 - // 0.125 - // 0.25 - // 0.5 - // 1 - // 2 - // 4 - // 8 - // 16 -} - -func ExampleFloat_Cmp() { - inf := math.Inf(1) - zero := 0.0 - - operands := []float64{-inf, -1.2, -zero, 0, +1.2, +inf} - - fmt.Println(" x y cmp") - fmt.Println("---------------") - for _, x64 := range operands { - x := big.NewFloat(x64) - for _, y64 := range operands { - y := big.NewFloat(y64) - fmt.Printf("%4s %4s %3d\n", x, y, x.Cmp(y)) - } - fmt.Println() - } - - // Output: - // x y cmp - // --------------- - // -Inf -Inf 0 - // -Inf -1.2 -1 - // -Inf -0 -1 - // -Inf 0 -1 - // -Inf 1.2 -1 - // -Inf +Inf -1 - // - // -1.2 -Inf 1 - // -1.2 -1.2 0 - // -1.2 -0 -1 - // -1.2 0 -1 - // -1.2 1.2 -1 - // -1.2 +Inf -1 - // - // -0 -Inf 1 - // -0 -1.2 1 - // -0 -0 0 - // -0 0 0 - // -0 1.2 -1 - // -0 +Inf -1 - // - // 0 -Inf 1 - // 0 -1.2 1 - // 0 -0 0 - // 0 0 0 - // 0 1.2 -1 - // 0 +Inf -1 - // - // 1.2 -Inf 1 - // 1.2 -1.2 1 - // 1.2 -0 1 - // 1.2 0 1 - // 1.2 1.2 0 - // 1.2 +Inf -1 - // - // +Inf -Inf 1 - // +Inf -1.2 1 - // +Inf -0 1 - // +Inf 0 1 - // +Inf 1.2 1 - // +Inf +Inf 0 -} diff --git a/src/cmd/internal/gc/big/ftoa.go b/src/cmd/internal/gc/big/ftoa.go deleted file mode 100644 index 0a9edfd7b2..0000000000 --- a/src/cmd/internal/gc/big/ftoa.go +++ /dev/null @@ -1,190 +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. - -// 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 - -package big - -import "strconv" - -// TODO(gri) Consider moving sign into decimal - could make the signatures below cleaner. - -// 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") - } - - // 1) convert Float to multiprecision decimal - var mant nat - if f.form == finite { - mant = f.mant - } - var d decimal - d.init(mant, int(f.exp)-f.mant.bitLen()) - - // 2) round to desired precision - shortest := false - if prec < 0 { - shortest = true - panic("unimplemented") - // TODO(gri) complete this - // roundShortest(&d, f.mant, int(f.exp)) - // Precision for shortest representation mode. - switch fmt { - case 'e', 'E': - prec = len(d.mant) - 1 - case 'f': - prec = max(len(d.mant)-d.exp, 0) - case 'g', 'G': - prec = len(d.mant) - } - } else { - // round appropriately - switch fmt { - case 'e', 'E': - // one digit before and number of digits after decimal point - d.round(1 + prec) - case 'f': - // number of digits before and after decimal point - d.round(d.exp + prec) - case 'g', 'G': - if prec == 0 { - prec = 1 - } - d.round(prec) - } - } - - // 3) read digits out and format - switch fmt { - case 'e', 'E': - return fmtE(buf, fmt, prec, f.neg, d) - case 'f': - return fmtF(buf, prec, f.neg, d) - case 'g', 'G': - // trim trailing fractional zeros in %e format - eprec := prec - if eprec > len(d.mant) && len(d.mant) >= d.exp { - eprec = len(d.mant) - } - // %e is used if the exponent from the conversion - // is less than -4 or greater than or equal to the precision. - // If precision was the shortest possible, use eprec = 6 for - // this decision. - if shortest { - eprec = 6 - } - exp := d.exp - 1 - if exp < -4 || exp >= eprec { - if prec > len(d.mant) { - prec = len(d.mant) - } - return fmtE(buf, fmt+'e'-'g', prec-1, f.neg, d) - } - if prec > d.exp { - prec = len(d.mant) - } - return fmtF(buf, max(prec-d.exp, 0), f.neg, d) - } - - // unknown format - 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, '-') - } - - // first digit - ch := byte('0') - if len(d.mant) > 0 { - ch = d.mant[0] - } - buf = append(buf, ch) - - // .moredigits - if prec > 0 { - buf = append(buf, '.') - i := 1 - m := min(len(d.mant), prec+1) - if i < m { - buf = append(buf, d.mant[i:m]...) - i = m - } - for ; i <= prec; i++ { - buf = append(buf, '0') - } - } - - // e± - buf = append(buf, fmt) - var exp int64 - if len(d.mant) > 0 { - exp = int64(d.exp) - 1 // -1 because first digit was printed before '.' - } - if exp < 0 { - ch = '-' - exp = -exp - } else { - ch = '+' - } - buf = append(buf, ch) - - // dd...d - if exp < 10 { - buf = append(buf, '0') // at least 2 exponent digits - } - 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, '-') - } - - // integer, padded with zeros as needed - if d.exp > 0 { - m := min(len(d.mant), d.exp) - buf = append(buf, d.mant[:m]...) - for ; m < d.exp; m++ { - buf = append(buf, '0') - } - } else { - buf = append(buf, '0') - } - - // fraction - if prec > 0 { - buf = append(buf, '.') - for i := 0; i < prec; i++ { - ch := byte('0') - if j := d.exp + i; 0 <= j && j < len(d.mant) { - ch = d.mant[j] - } - buf = append(buf, ch) - } - } - - return buf -} - -func min(x, y int) int { - if x < y { - return x - } - return y -} diff --git a/src/cmd/internal/gc/big/gcd_test.go b/src/cmd/internal/gc/big/gcd_test.go deleted file mode 100644 index c0b9f58300..0000000000 --- a/src/cmd/internal/gc/big/gcd_test.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2012 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. - -// This file implements a GCD benchmark. -// Usage: go test math/big -test.bench GCD - -package big - -import ( - "math/rand" - "testing" -) - -// randInt returns a pseudo-random Int in the range [1<<(size-1), (1< 1<<(size-1) -} - -func runGCD(b *testing.B, aSize, bSize uint) { - b.StopTimer() - var r = rand.New(rand.NewSource(1234)) - aa := randInt(r, aSize) - bb := randInt(r, bSize) - b.StartTimer() - for i := 0; i < b.N; i++ { - new(Int).GCD(nil, nil, aa, bb) - } -} - -func BenchmarkGCD10x10(b *testing.B) { runGCD(b, 10, 10) } -func BenchmarkGCD10x100(b *testing.B) { runGCD(b, 10, 100) } -func BenchmarkGCD10x1000(b *testing.B) { runGCD(b, 10, 1000) } -func BenchmarkGCD10x10000(b *testing.B) { runGCD(b, 10, 10000) } -func BenchmarkGCD10x100000(b *testing.B) { runGCD(b, 10, 100000) } -func BenchmarkGCD100x100(b *testing.B) { runGCD(b, 100, 100) } -func BenchmarkGCD100x1000(b *testing.B) { runGCD(b, 100, 1000) } -func BenchmarkGCD100x10000(b *testing.B) { runGCD(b, 100, 10000) } -func BenchmarkGCD100x100000(b *testing.B) { runGCD(b, 100, 100000) } -func BenchmarkGCD1000x1000(b *testing.B) { runGCD(b, 1000, 1000) } -func BenchmarkGCD1000x10000(b *testing.B) { runGCD(b, 1000, 10000) } -func BenchmarkGCD1000x100000(b *testing.B) { runGCD(b, 1000, 100000) } -func BenchmarkGCD10000x10000(b *testing.B) { runGCD(b, 10000, 10000) } -func BenchmarkGCD10000x100000(b *testing.B) { runGCD(b, 10000, 100000) } -func BenchmarkGCD100000x100000(b *testing.B) { runGCD(b, 100000, 100000) } diff --git a/src/cmd/internal/gc/big/hilbert_test.go b/src/cmd/internal/gc/big/hilbert_test.go deleted file mode 100644 index 1a84341b3c..0000000000 --- a/src/cmd/internal/gc/big/hilbert_test.go +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2009 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. - -// A little test program and benchmark for rational arithmetics. -// Computes a Hilbert matrix, its inverse, multiplies them -// and verifies that the product is the identity matrix. - -package big - -import ( - "fmt" - "testing" -) - -type matrix struct { - n, m int - a []*Rat -} - -func (a *matrix) at(i, j int) *Rat { - if !(0 <= i && i < a.n && 0 <= j && j < a.m) { - panic("index out of range") - } - return a.a[i*a.m+j] -} - -func (a *matrix) set(i, j int, x *Rat) { - if !(0 <= i && i < a.n && 0 <= j && j < a.m) { - panic("index out of range") - } - a.a[i*a.m+j] = x -} - -func newMatrix(n, m int) *matrix { - if !(0 <= n && 0 <= m) { - panic("illegal matrix") - } - a := new(matrix) - a.n = n - a.m = m - a.a = make([]*Rat, n*m) - return a -} - -func newUnit(n int) *matrix { - a := newMatrix(n, n) - for i := 0; i < n; i++ { - for j := 0; j < n; j++ { - x := NewRat(0, 1) - if i == j { - x.SetInt64(1) - } - a.set(i, j, x) - } - } - return a -} - -func newHilbert(n int) *matrix { - a := newMatrix(n, n) - for i := 0; i < n; i++ { - for j := 0; j < n; j++ { - a.set(i, j, NewRat(1, int64(i+j+1))) - } - } - return a -} - -func newInverseHilbert(n int) *matrix { - a := newMatrix(n, n) - for i := 0; i < n; i++ { - for j := 0; j < n; j++ { - x1 := new(Rat).SetInt64(int64(i + j + 1)) - x2 := new(Rat).SetInt(new(Int).Binomial(int64(n+i), int64(n-j-1))) - x3 := new(Rat).SetInt(new(Int).Binomial(int64(n+j), int64(n-i-1))) - x4 := new(Rat).SetInt(new(Int).Binomial(int64(i+j), int64(i))) - - x1.Mul(x1, x2) - x1.Mul(x1, x3) - x1.Mul(x1, x4) - x1.Mul(x1, x4) - - if (i+j)&1 != 0 { - x1.Neg(x1) - } - - a.set(i, j, x1) - } - } - return a -} - -func (a *matrix) mul(b *matrix) *matrix { - if a.m != b.n { - panic("illegal matrix multiply") - } - c := newMatrix(a.n, b.m) - for i := 0; i < c.n; i++ { - for j := 0; j < c.m; j++ { - x := NewRat(0, 1) - for k := 0; k < a.m; k++ { - x.Add(x, new(Rat).Mul(a.at(i, k), b.at(k, j))) - } - c.set(i, j, x) - } - } - return c -} - -func (a *matrix) eql(b *matrix) bool { - if a.n != b.n || a.m != b.m { - return false - } - for i := 0; i < a.n; i++ { - for j := 0; j < a.m; j++ { - if a.at(i, j).Cmp(b.at(i, j)) != 0 { - return false - } - } - } - return true -} - -func (a *matrix) String() string { - s := "" - for i := 0; i < a.n; i++ { - for j := 0; j < a.m; j++ { - s += fmt.Sprintf("\t%s", a.at(i, j)) - } - s += "\n" - } - return s -} - -func doHilbert(t *testing.T, n int) { - a := newHilbert(n) - b := newInverseHilbert(n) - I := newUnit(n) - ab := a.mul(b) - if !ab.eql(I) { - if t == nil { - panic("Hilbert failed") - } - t.Errorf("a = %s\n", a) - t.Errorf("b = %s\n", b) - t.Errorf("a*b = %s\n", ab) - t.Errorf("I = %s\n", I) - } -} - -func TestHilbert(t *testing.T) { - doHilbert(t, 10) -} - -func BenchmarkHilbert(b *testing.B) { - for i := 0; i < b.N; i++ { - doHilbert(nil, 10) - } -} diff --git a/src/cmd/internal/gc/big/int.go b/src/cmd/internal/gc/big/int.go deleted file mode 100644 index 7b419bf688..0000000000 --- a/src/cmd/internal/gc/big/int.go +++ /dev/null @@ -1,848 +0,0 @@ -// Copyright 2009 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. - -// This file implements signed multi-precision integers. - -package big - -import ( - "fmt" - "io" - "math/rand" - "strings" -) - -// An Int represents a signed multi-precision integer. -// The zero value for an Int represents the value 0. -type Int struct { - neg bool // sign - abs nat // absolute value of the integer -} - -var intOne = &Int{false, natOne} - -// Sign returns: -// -// -1 if x < 0 -// 0 if x == 0 -// +1 if x > 0 -// -func (x *Int) Sign() int { - if len(x.abs) == 0 { - return 0 - } - if x.neg { - return -1 - } - return 1 -} - -// SetInt64 sets z to x and returns z. -func (z *Int) SetInt64(x int64) *Int { - neg := false - if x < 0 { - neg = true - x = -x - } - z.abs = z.abs.setUint64(uint64(x)) - z.neg = neg - return z -} - -// SetUint64 sets z to x and returns z. -func (z *Int) SetUint64(x uint64) *Int { - z.abs = z.abs.setUint64(x) - z.neg = false - return z -} - -// NewInt allocates and returns a new Int set to x. -func NewInt(x int64) *Int { - return new(Int).SetInt64(x) -} - -// Set sets z to x and returns z. -func (z *Int) Set(x *Int) *Int { - if z != x { - z.abs = z.abs.set(x.abs) - z.neg = x.neg - } - return z -} - -// Bits provides raw (unchecked but fast) access to x by returning its -// absolute value as a little-endian Word slice. The result and x share -// the same underlying array. -// Bits is intended to support implementation of missing low-level Int -// functionality outside this package; it should be avoided otherwise. -func (x *Int) Bits() []Word { - return x.abs -} - -// SetBits provides raw (unchecked but fast) access to z by setting its -// value to abs, interpreted as a little-endian Word slice, and returning -// z. The result and abs share the same underlying array. -// SetBits is intended to support implementation of missing low-level Int -// functionality outside this package; it should be avoided otherwise. -func (z *Int) SetBits(abs []Word) *Int { - z.abs = nat(abs).norm() - z.neg = false - return z -} - -// Abs sets z to |x| (the absolute value of x) and returns z. -func (z *Int) Abs(x *Int) *Int { - z.Set(x) - z.neg = false - return z -} - -// Neg sets z to -x and returns z. -func (z *Int) Neg(x *Int) *Int { - z.Set(x) - z.neg = len(z.abs) > 0 && !z.neg // 0 has no sign - return z -} - -// Add sets z to the sum x+y and returns z. -func (z *Int) Add(x, y *Int) *Int { - neg := x.neg - if x.neg == y.neg { - // x + y == x + y - // (-x) + (-y) == -(x + y) - z.abs = z.abs.add(x.abs, y.abs) - } else { - // x + (-y) == x - y == -(y - x) - // (-x) + y == y - x == -(x - y) - if x.abs.cmp(y.abs) >= 0 { - z.abs = z.abs.sub(x.abs, y.abs) - } else { - neg = !neg - z.abs = z.abs.sub(y.abs, x.abs) - } - } - z.neg = len(z.abs) > 0 && neg // 0 has no sign - return z -} - -// Sub sets z to the difference x-y and returns z. -func (z *Int) Sub(x, y *Int) *Int { - neg := x.neg - if x.neg != y.neg { - // x - (-y) == x + y - // (-x) - y == -(x + y) - z.abs = z.abs.add(x.abs, y.abs) - } else { - // x - y == x - y == -(y - x) - // (-x) - (-y) == y - x == -(x - y) - if x.abs.cmp(y.abs) >= 0 { - z.abs = z.abs.sub(x.abs, y.abs) - } else { - neg = !neg - z.abs = z.abs.sub(y.abs, x.abs) - } - } - z.neg = len(z.abs) > 0 && neg // 0 has no sign - return z -} - -// Mul sets z to the product x*y and returns z. -func (z *Int) Mul(x, y *Int) *Int { - // x * y == x * y - // x * (-y) == -(x * y) - // (-x) * y == -(x * y) - // (-x) * (-y) == x * y - z.abs = z.abs.mul(x.abs, y.abs) - z.neg = len(z.abs) > 0 && x.neg != y.neg // 0 has no sign - return z -} - -// MulRange sets z to the product of all integers -// in the range [a, b] inclusively and returns z. -// If a > b (empty range), the result is 1. -func (z *Int) MulRange(a, b int64) *Int { - switch { - case a > b: - return z.SetInt64(1) // empty range - case a <= 0 && b >= 0: - return z.SetInt64(0) // range includes 0 - } - // a <= b && (b < 0 || a > 0) - - neg := false - if a < 0 { - neg = (b-a)&1 == 0 - a, b = -b, -a - } - - z.abs = z.abs.mulRange(uint64(a), uint64(b)) - z.neg = neg - return z -} - -// Binomial sets z to the binomial coefficient of (n, k) and returns z. -func (z *Int) Binomial(n, k int64) *Int { - // reduce the number of multiplications by reducing k - if n/2 < k && k <= n { - k = n - k // Binomial(n, k) == Binomial(n, n-k) - } - var a, b Int - a.MulRange(n-k+1, n) - b.MulRange(1, k) - return z.Quo(&a, &b) -} - -// Quo sets z to the quotient x/y for y != 0 and returns z. -// If y == 0, a division-by-zero run-time panic occurs. -// Quo implements truncated division (like Go); see QuoRem for more details. -func (z *Int) Quo(x, y *Int) *Int { - z.abs, _ = z.abs.div(nil, x.abs, y.abs) - z.neg = len(z.abs) > 0 && x.neg != y.neg // 0 has no sign - return z -} - -// Rem sets z to the remainder x%y for y != 0 and returns z. -// If y == 0, a division-by-zero run-time panic occurs. -// Rem implements truncated modulus (like Go); see QuoRem for more details. -func (z *Int) Rem(x, y *Int) *Int { - _, z.abs = nat(nil).div(z.abs, x.abs, y.abs) - z.neg = len(z.abs) > 0 && x.neg // 0 has no sign - return z -} - -// QuoRem sets z to the quotient x/y and r to the remainder x%y -// and returns the pair (z, r) for y != 0. -// If y == 0, a division-by-zero run-time panic occurs. -// -// QuoRem implements T-division and modulus (like Go): -// -// q = x/y with the result truncated to zero -// r = x - y*q -// -// (See Daan Leijen, ``Division and Modulus for Computer Scientists''.) -// See DivMod for Euclidean division and modulus (unlike Go). -// -func (z *Int) QuoRem(x, y, r *Int) (*Int, *Int) { - z.abs, r.abs = z.abs.div(r.abs, x.abs, y.abs) - z.neg, r.neg = len(z.abs) > 0 && x.neg != y.neg, len(r.abs) > 0 && x.neg // 0 has no sign - return z, r -} - -// Div sets z to the quotient x/y for y != 0 and returns z. -// If y == 0, a division-by-zero run-time panic occurs. -// Div implements Euclidean division (unlike Go); see DivMod for more details. -func (z *Int) Div(x, y *Int) *Int { - y_neg := y.neg // z may be an alias for y - var r Int - z.QuoRem(x, y, &r) - if r.neg { - if y_neg { - z.Add(z, intOne) - } else { - z.Sub(z, intOne) - } - } - return z -} - -// Mod sets z to the modulus x%y for y != 0 and returns z. -// If y == 0, a division-by-zero run-time panic occurs. -// Mod implements Euclidean modulus (unlike Go); see DivMod for more details. -func (z *Int) Mod(x, y *Int) *Int { - y0 := y // save y - if z == y || alias(z.abs, y.abs) { - y0 = new(Int).Set(y) - } - var q Int - q.QuoRem(x, y, z) - if z.neg { - if y0.neg { - z.Sub(z, y0) - } else { - z.Add(z, y0) - } - } - return z -} - -// DivMod sets z to the quotient x div y and m to the modulus x mod y -// and returns the pair (z, m) for y != 0. -// If y == 0, a division-by-zero run-time panic occurs. -// -// DivMod implements Euclidean division and modulus (unlike Go): -// -// q = x div y such that -// m = x - y*q with 0 <= m < |q| -// -// (See Raymond T. Boute, ``The Euclidean definition of the functions -// div and mod''. ACM Transactions on Programming Languages and -// Systems (TOPLAS), 14(2):127-144, New York, NY, USA, 4/1992. -// ACM press.) -// See QuoRem for T-division and modulus (like Go). -// -func (z *Int) DivMod(x, y, m *Int) (*Int, *Int) { - y0 := y // save y - if z == y || alias(z.abs, y.abs) { - y0 = new(Int).Set(y) - } - z.QuoRem(x, y, m) - if m.neg { - if y0.neg { - z.Add(z, intOne) - m.Sub(m, y0) - } else { - z.Sub(z, intOne) - m.Add(m, y0) - } - } - return z, m -} - -// Cmp compares x and y and returns: -// -// -1 if x < y -// 0 if x == y -// +1 if x > y -// -func (x *Int) Cmp(y *Int) (r int) { - // x cmp y == x cmp y - // x cmp (-y) == x - // (-x) cmp y == y - // (-x) cmp (-y) == -(x cmp y) - switch { - case x.neg == y.neg: - r = x.abs.cmp(y.abs) - if x.neg { - r = -r - } - case x.neg: - r = -1 - default: - r = 1 - } - return -} - -// low32 returns the least significant 32 bits of z. -func low32(z nat) uint32 { - if len(z) == 0 { - return 0 - } - return uint32(z[0]) -} - -// low64 returns the least significant 64 bits of z. -func low64(z nat) uint64 { - if len(z) == 0 { - return 0 - } - v := uint64(z[0]) - if _W == 32 && len(z) > 1 { - v |= uint64(z[1]) << 32 - } - return v -} - -// Int64 returns the int64 representation of x. -// If x cannot be represented in an int64, the result is undefined. -func (x *Int) Int64() int64 { - v := int64(low64(x.abs)) - if x.neg { - v = -v - } - return v -} - -// Uint64 returns the uint64 representation of x. -// If x cannot be represented in a uint64, the result is undefined. -func (x *Int) Uint64() uint64 { - return low64(x.abs) -} - -// SetString sets z to the value of s, interpreted in the given base, -// and returns z and a boolean indicating success. If SetString fails, -// the value of z is undefined but the returned value is nil. -// -// The base argument must be 0 or a value between 2 and MaxBase. If the base -// is 0, the string prefix determines the actual conversion base. A prefix of -// ``0x'' or ``0X'' selects base 16; the ``0'' prefix selects base 8, and a -// ``0b'' or ``0B'' prefix selects base 2. Otherwise the selected base is 10. -// -func (z *Int) SetString(s string, base int) (*Int, bool) { - r := strings.NewReader(s) - _, _, err := z.scan(r, base) - if err != nil { - return nil, false - } - _, err = r.ReadByte() - if err != io.EOF { - return nil, false - } - return z, true // err == io.EOF => scan consumed all of s -} - -// SetBytes interprets buf as the bytes of a big-endian unsigned -// integer, sets z to that value, and returns z. -func (z *Int) SetBytes(buf []byte) *Int { - z.abs = z.abs.setBytes(buf) - z.neg = false - return z -} - -// Bytes returns the absolute value of x as a big-endian byte slice. -func (x *Int) Bytes() []byte { - buf := make([]byte, len(x.abs)*_S) - return buf[x.abs.bytes(buf):] -} - -// BitLen returns the length of the absolute value of x in bits. -// The bit length of 0 is 0. -func (x *Int) BitLen() int { - return x.abs.bitLen() -} - -// Exp sets z = x**y mod |m| (i.e. the sign of m is ignored), and returns z. -// If y <= 0, the result is 1 mod |m|; if m == nil or m == 0, z = x**y. -// See Knuth, volume 2, section 4.6.3. -func (z *Int) Exp(x, y, m *Int) *Int { - var yWords nat - if !y.neg { - yWords = y.abs - } - // y >= 0 - - var mWords nat - if m != nil { - mWords = m.abs // m.abs may be nil for m == 0 - } - - z.abs = z.abs.expNN(x.abs, yWords, mWords) - z.neg = len(z.abs) > 0 && x.neg && len(yWords) > 0 && yWords[0]&1 == 1 // 0 has no sign - if z.neg && len(mWords) > 0 { - // make modulus result positive - z.abs = z.abs.sub(mWords, z.abs) // z == x**y mod |m| && 0 <= z < |m| - z.neg = false - } - - return z -} - -// GCD sets z to the greatest common divisor of a and b, which both must -// be > 0, and returns z. -// If x and y are not nil, GCD sets x and y such that z = a*x + b*y. -// If either a or b is <= 0, GCD sets z = x = y = 0. -func (z *Int) GCD(x, y, a, b *Int) *Int { - if a.Sign() <= 0 || b.Sign() <= 0 { - z.SetInt64(0) - if x != nil { - x.SetInt64(0) - } - if y != nil { - y.SetInt64(0) - } - return z - } - if x == nil && y == nil { - return z.binaryGCD(a, b) - } - - A := new(Int).Set(a) - B := new(Int).Set(b) - - X := new(Int) - Y := new(Int).SetInt64(1) - - lastX := new(Int).SetInt64(1) - lastY := new(Int) - - q := new(Int) - temp := new(Int) - - for len(B.abs) > 0 { - r := new(Int) - q, r = q.QuoRem(A, B, r) - - A, B = B, r - - temp.Set(X) - X.Mul(X, q) - X.neg = !X.neg - X.Add(X, lastX) - lastX.Set(temp) - - temp.Set(Y) - Y.Mul(Y, q) - Y.neg = !Y.neg - Y.Add(Y, lastY) - lastY.Set(temp) - } - - if x != nil { - *x = *lastX - } - - if y != nil { - *y = *lastY - } - - *z = *A - return z -} - -// binaryGCD sets z to the greatest common divisor of a and b, which both must -// be > 0, and returns z. -// See Knuth, The Art of Computer Programming, Vol. 2, Section 4.5.2, Algorithm B. -func (z *Int) binaryGCD(a, b *Int) *Int { - u := z - v := new(Int) - - // use one Euclidean iteration to ensure that u and v are approx. the same size - switch { - case len(a.abs) > len(b.abs): - u.Set(b) - v.Rem(a, b) - case len(a.abs) < len(b.abs): - u.Set(a) - v.Rem(b, a) - default: - u.Set(a) - v.Set(b) - } - - // v might be 0 now - if len(v.abs) == 0 { - return u - } - // u > 0 && v > 0 - - // determine largest k such that u = u' << k, v = v' << k - k := u.abs.trailingZeroBits() - if vk := v.abs.trailingZeroBits(); vk < k { - k = vk - } - u.Rsh(u, k) - v.Rsh(v, k) - - // determine t (we know that u > 0) - t := new(Int) - if u.abs[0]&1 != 0 { - // u is odd - t.Neg(v) - } else { - t.Set(u) - } - - for len(t.abs) > 0 { - // reduce t - t.Rsh(t, t.abs.trailingZeroBits()) - if t.neg { - v, t = t, v - v.neg = len(v.abs) > 0 && !v.neg // 0 has no sign - } else { - u, t = t, u - } - t.Sub(u, v) - } - - return z.Lsh(u, k) -} - -// ProbablyPrime performs n Miller-Rabin tests to check whether x is prime. -// If it returns true, x is prime with probability 1 - 1/4^n. -// If it returns false, x is not prime. n must be > 0. -func (x *Int) ProbablyPrime(n int) bool { - if n <= 0 { - panic("non-positive n for ProbablyPrime") - } - return !x.neg && x.abs.probablyPrime(n) -} - -// Rand sets z to a pseudo-random number in [0, n) and returns z. -func (z *Int) Rand(rnd *rand.Rand, n *Int) *Int { - z.neg = false - if n.neg == true || len(n.abs) == 0 { - z.abs = nil - return z - } - z.abs = z.abs.random(rnd, n.abs, n.abs.bitLen()) - return z -} - -// ModInverse sets z to the multiplicative inverse of g in the ring ℤ/nℤ -// and returns z. If g and n are not relatively prime, the result is undefined. -func (z *Int) ModInverse(g, n *Int) *Int { - var d Int - d.GCD(z, nil, g, n) - // x and y are such that g*x + n*y = d. Since g and n are - // relatively prime, d = 1. Taking that modulo n results in - // g*x = 1, therefore x is the inverse element. - if z.neg { - z.Add(z, n) - } - return z -} - -// 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) - z.neg = x.neg - return z -} - -// Rsh sets z = x >> n and returns z. -func (z *Int) Rsh(x *Int, n uint) *Int { - if x.neg { - // (-x) >> s == ^(x-1) >> s == ^((x-1) >> s) == -(((x-1) >> s) + 1) - t := z.abs.sub(x.abs, natOne) // no underflow because |x| > 0 - t = t.shr(t, n) - z.abs = t.add(t, natOne) - z.neg = true // z cannot be zero if x is negative - return z - } - - z.abs = z.abs.shr(x.abs, n) - z.neg = false - return z -} - -// Bit returns the value of the i'th bit of x. That is, it -// returns (x>>i)&1. The bit index i must be >= 0. -func (x *Int) Bit(i int) uint { - if i == 0 { - // optimization for common case: odd/even test of x - if len(x.abs) > 0 { - return uint(x.abs[0] & 1) // bit 0 is same for -x - } - return 0 - } - if i < 0 { - panic("negative bit index") - } - if x.neg { - t := nat(nil).sub(x.abs, natOne) - return t.bit(uint(i)) ^ 1 - } - - return x.abs.bit(uint(i)) -} - -// SetBit sets z to x, with x's i'th bit set to b (0 or 1). -// That is, if b is 1 SetBit sets z = x | (1 << i); -// if b is 0 SetBit sets z = x &^ (1 << i). If b is not 0 or 1, -// SetBit will panic. -func (z *Int) SetBit(x *Int, i int, b uint) *Int { - if i < 0 { - panic("negative bit index") - } - if x.neg { - t := z.abs.sub(x.abs, natOne) - t = t.setBit(t, uint(i), b^1) - z.abs = t.add(t, natOne) - z.neg = len(z.abs) > 0 - return z - } - z.abs = z.abs.setBit(x.abs, uint(i), b) - z.neg = false - return z -} - -// And sets z = x & y and returns z. -func (z *Int) And(x, y *Int) *Int { - if x.neg == y.neg { - if x.neg { - // (-x) & (-y) == ^(x-1) & ^(y-1) == ^((x-1) | (y-1)) == -(((x-1) | (y-1)) + 1) - x1 := nat(nil).sub(x.abs, natOne) - y1 := nat(nil).sub(y.abs, natOne) - z.abs = z.abs.add(z.abs.or(x1, y1), natOne) - z.neg = true // z cannot be zero if x and y are negative - return z - } - - // x & y == x & y - z.abs = z.abs.and(x.abs, y.abs) - z.neg = false - return z - } - - // x.neg != y.neg - if x.neg { - x, y = y, x // & is symmetric - } - - // x & (-y) == x & ^(y-1) == x &^ (y-1) - y1 := nat(nil).sub(y.abs, natOne) - z.abs = z.abs.andNot(x.abs, y1) - z.neg = false - return z -} - -// AndNot sets z = x &^ y and returns z. -func (z *Int) AndNot(x, y *Int) *Int { - if x.neg == y.neg { - if x.neg { - // (-x) &^ (-y) == ^(x-1) &^ ^(y-1) == ^(x-1) & (y-1) == (y-1) &^ (x-1) - x1 := nat(nil).sub(x.abs, natOne) - y1 := nat(nil).sub(y.abs, natOne) - z.abs = z.abs.andNot(y1, x1) - z.neg = false - return z - } - - // x &^ y == x &^ y - z.abs = z.abs.andNot(x.abs, y.abs) - z.neg = false - return z - } - - if x.neg { - // (-x) &^ y == ^(x-1) &^ y == ^(x-1) & ^y == ^((x-1) | y) == -(((x-1) | y) + 1) - x1 := nat(nil).sub(x.abs, natOne) - z.abs = z.abs.add(z.abs.or(x1, y.abs), natOne) - z.neg = true // z cannot be zero if x is negative and y is positive - return z - } - - // x &^ (-y) == x &^ ^(y-1) == x & (y-1) - y1 := nat(nil).sub(y.abs, natOne) - z.abs = z.abs.and(x.abs, y1) - z.neg = false - return z -} - -// Or sets z = x | y and returns z. -func (z *Int) Or(x, y *Int) *Int { - if x.neg == y.neg { - if x.neg { - // (-x) | (-y) == ^(x-1) | ^(y-1) == ^((x-1) & (y-1)) == -(((x-1) & (y-1)) + 1) - x1 := nat(nil).sub(x.abs, natOne) - y1 := nat(nil).sub(y.abs, natOne) - z.abs = z.abs.add(z.abs.and(x1, y1), natOne) - z.neg = true // z cannot be zero if x and y are negative - return z - } - - // x | y == x | y - z.abs = z.abs.or(x.abs, y.abs) - z.neg = false - return z - } - - // x.neg != y.neg - if x.neg { - x, y = y, x // | is symmetric - } - - // x | (-y) == x | ^(y-1) == ^((y-1) &^ x) == -(^((y-1) &^ x) + 1) - y1 := nat(nil).sub(y.abs, natOne) - z.abs = z.abs.add(z.abs.andNot(y1, x.abs), natOne) - z.neg = true // z cannot be zero if one of x or y is negative - return z -} - -// Xor sets z = x ^ y and returns z. -func (z *Int) Xor(x, y *Int) *Int { - if x.neg == y.neg { - if x.neg { - // (-x) ^ (-y) == ^(x-1) ^ ^(y-1) == (x-1) ^ (y-1) - x1 := nat(nil).sub(x.abs, natOne) - y1 := nat(nil).sub(y.abs, natOne) - z.abs = z.abs.xor(x1, y1) - z.neg = false - return z - } - - // x ^ y == x ^ y - z.abs = z.abs.xor(x.abs, y.abs) - z.neg = false - return z - } - - // x.neg != y.neg - if x.neg { - x, y = y, x // ^ is symmetric - } - - // x ^ (-y) == x ^ ^(y-1) == ^(x ^ (y-1)) == -((x ^ (y-1)) + 1) - y1 := nat(nil).sub(y.abs, natOne) - z.abs = z.abs.add(z.abs.xor(x.abs, y1), natOne) - z.neg = true // z cannot be zero if only one of x or y is negative - return z -} - -// Not sets z = ^x and returns z. -func (z *Int) Not(x *Int) *Int { - if x.neg { - // ^(-x) == ^(^(x-1)) == x-1 - z.abs = z.abs.sub(x.abs, natOne) - z.neg = false - return z - } - - // ^x == -x-1 == -(x+1) - z.abs = z.abs.add(x.abs, natOne) - z.neg = true // z cannot be zero if x is positive - return z -} - -// Gob codec version. Permits backward-compatible changes to the encoding. -const intGobVersion byte = 1 - -// GobEncode implements the gob.GobEncoder interface. -func (x *Int) GobEncode() ([]byte, error) { - if x == nil { - return nil, nil - } - buf := make([]byte, 1+len(x.abs)*_S) // extra byte for version and sign bit - i := x.abs.bytes(buf) - 1 // i >= 0 - b := intGobVersion << 1 // make space for sign bit - if x.neg { - b |= 1 - } - buf[i] = b - return buf[i:], nil -} - -// GobDecode implements the gob.GobDecoder interface. -func (z *Int) GobDecode(buf []byte) error { - if len(buf) == 0 { - // Other side sent a nil or default value. - *z = Int{} - return nil - } - b := buf[0] - if b>>1 != intGobVersion { - return fmt.Errorf("Int.GobDecode: encoding version %d not supported", b>>1) - } - z.neg = b&1 != 0 - z.abs = z.abs.setBytes(buf[1:]) - return nil -} - -// MarshalJSON implements the json.Marshaler interface. -func (z *Int) MarshalJSON() ([]byte, error) { - // TODO(gri): get rid of the []byte/string conversions - return []byte(z.String()), nil -} - -// UnmarshalJSON implements the json.Unmarshaler interface. -func (z *Int) UnmarshalJSON(text []byte) error { - // TODO(gri): get rid of the []byte/string conversions - if _, ok := z.SetString(string(text), 0); !ok { - return fmt.Errorf("math/big: cannot unmarshal %q into a *big.Int", text) - } - return nil -} - -// MarshalText implements the encoding.TextMarshaler interface. -func (z *Int) MarshalText() (text []byte, err error) { - return []byte(z.String()), nil -} - -// UnmarshalText implements the encoding.TextUnmarshaler interface. -func (z *Int) UnmarshalText(text []byte) error { - if _, ok := z.SetString(string(text), 0); !ok { - return fmt.Errorf("math/big: cannot unmarshal %q into a *big.Int", text) - } - return nil -} diff --git a/src/cmd/internal/gc/big/int_test.go b/src/cmd/internal/gc/big/int_test.go deleted file mode 100644 index a972a7249b..0000000000 --- a/src/cmd/internal/gc/big/int_test.go +++ /dev/null @@ -1,1387 +0,0 @@ -// Copyright 2009 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 big - -import ( - "bytes" - "encoding/gob" - "encoding/hex" - "encoding/json" - "encoding/xml" - "fmt" - "math/rand" - "testing" - "testing/quick" -) - -func isNormalized(x *Int) bool { - if len(x.abs) == 0 { - return !x.neg - } - // len(x.abs) > 0 - return x.abs[len(x.abs)-1] != 0 -} - -type funZZ func(z, x, y *Int) *Int -type argZZ struct { - z, x, y *Int -} - -var sumZZ = []argZZ{ - {NewInt(0), NewInt(0), NewInt(0)}, - {NewInt(1), NewInt(1), NewInt(0)}, - {NewInt(1111111110), NewInt(123456789), NewInt(987654321)}, - {NewInt(-1), NewInt(-1), NewInt(0)}, - {NewInt(864197532), NewInt(-123456789), NewInt(987654321)}, - {NewInt(-1111111110), NewInt(-123456789), NewInt(-987654321)}, -} - -var prodZZ = []argZZ{ - {NewInt(0), NewInt(0), NewInt(0)}, - {NewInt(0), NewInt(1), NewInt(0)}, - {NewInt(1), NewInt(1), NewInt(1)}, - {NewInt(-991 * 991), NewInt(991), NewInt(-991)}, - // TODO(gri) add larger products -} - -func TestSignZ(t *testing.T) { - var zero Int - for _, a := range sumZZ { - s := a.z.Sign() - e := a.z.Cmp(&zero) - if s != e { - t.Errorf("got %d; want %d for z = %v", s, e, a.z) - } - } -} - -func TestSetZ(t *testing.T) { - for _, a := range sumZZ { - var z Int - z.Set(a.z) - if !isNormalized(&z) { - t.Errorf("%v is not normalized", z) - } - if (&z).Cmp(a.z) != 0 { - t.Errorf("got z = %v; want %v", z, a.z) - } - } -} - -func TestAbsZ(t *testing.T) { - var zero Int - for _, a := range sumZZ { - var z Int - z.Abs(a.z) - var e Int - e.Set(a.z) - if e.Cmp(&zero) < 0 { - e.Sub(&zero, &e) - } - if z.Cmp(&e) != 0 { - t.Errorf("got z = %v; want %v", z, e) - } - } -} - -func testFunZZ(t *testing.T, msg string, f funZZ, a argZZ) { - var z Int - f(&z, a.x, a.y) - if !isNormalized(&z) { - t.Errorf("%s%v is not normalized", msg, z) - } - if (&z).Cmp(a.z) != 0 { - t.Errorf("%s%+v\n\tgot z = %v; want %v", msg, a, &z, a.z) - } -} - -func TestSumZZ(t *testing.T) { - AddZZ := func(z, x, y *Int) *Int { return z.Add(x, y) } - SubZZ := func(z, x, y *Int) *Int { return z.Sub(x, y) } - for _, a := range sumZZ { - arg := a - testFunZZ(t, "AddZZ", AddZZ, arg) - - arg = argZZ{a.z, a.y, a.x} - testFunZZ(t, "AddZZ symmetric", AddZZ, arg) - - arg = argZZ{a.x, a.z, a.y} - testFunZZ(t, "SubZZ", SubZZ, arg) - - arg = argZZ{a.y, a.z, a.x} - testFunZZ(t, "SubZZ symmetric", SubZZ, arg) - } -} - -func TestProdZZ(t *testing.T) { - MulZZ := func(z, x, y *Int) *Int { return z.Mul(x, y) } - for _, a := range prodZZ { - arg := a - testFunZZ(t, "MulZZ", MulZZ, arg) - - arg = argZZ{a.z, a.y, a.x} - testFunZZ(t, "MulZZ symmetric", MulZZ, arg) - } -} - -// mulBytes returns x*y via grade school multiplication. Both inputs -// and the result are assumed to be in big-endian representation (to -// match the semantics of Int.Bytes and Int.SetBytes). -func mulBytes(x, y []byte) []byte { - z := make([]byte, len(x)+len(y)) - - // multiply - k0 := len(z) - 1 - for j := len(y) - 1; j >= 0; j-- { - d := int(y[j]) - if d != 0 { - k := k0 - carry := 0 - for i := len(x) - 1; i >= 0; i-- { - t := int(z[k]) + int(x[i])*d + carry - z[k], carry = byte(t), t>>8 - k-- - } - z[k] = byte(carry) - } - k0-- - } - - // normalize (remove leading 0's) - i := 0 - for i < len(z) && z[i] == 0 { - i++ - } - - return z[i:] -} - -func checkMul(a, b []byte) bool { - var x, y, z1 Int - x.SetBytes(a) - y.SetBytes(b) - z1.Mul(&x, &y) - - var z2 Int - z2.SetBytes(mulBytes(a, b)) - - return z1.Cmp(&z2) == 0 -} - -func TestMul(t *testing.T) { - if err := quick.Check(checkMul, nil); err != nil { - t.Error(err) - } -} - -var mulRangesZ = []struct { - a, b int64 - prod string -}{ - // entirely positive ranges are covered by mulRangesN - {-1, 1, "0"}, - {-2, -1, "2"}, - {-3, -2, "6"}, - {-3, -1, "-6"}, - {1, 3, "6"}, - {-10, -10, "-10"}, - {0, -1, "1"}, // empty range - {-1, -100, "1"}, // empty range - {-1, 1, "0"}, // range includes 0 - {-1e9, 0, "0"}, // range includes 0 - {-1e9, 1e9, "0"}, // range includes 0 - {-10, -1, "3628800"}, // 10! - {-20, -2, "-2432902008176640000"}, // -20! - {-99, -1, - "-933262154439441526816992388562667004907159682643816214685929" + - "638952175999932299156089414639761565182862536979208272237582" + - "511852109168640000000000000000000000", // -99! - }, -} - -func TestMulRangeZ(t *testing.T) { - var tmp Int - // test entirely positive ranges - for i, r := range mulRangesN { - prod := tmp.MulRange(int64(r.a), int64(r.b)).String() - if prod != r.prod { - t.Errorf("#%da: got %s; want %s", i, prod, r.prod) - } - } - // test other ranges - for i, r := range mulRangesZ { - prod := tmp.MulRange(r.a, r.b).String() - if prod != r.prod { - t.Errorf("#%db: got %s; want %s", i, prod, r.prod) - } - } -} - -func TestBinomial(t *testing.T) { - var z Int - for _, test := range []struct { - n, k int64 - want string - }{ - {0, 0, "1"}, - {0, 1, "0"}, - {1, 0, "1"}, - {1, 1, "1"}, - {1, 10, "0"}, - {4, 0, "1"}, - {4, 1, "4"}, - {4, 2, "6"}, - {4, 3, "4"}, - {4, 4, "1"}, - {10, 1, "10"}, - {10, 9, "10"}, - {10, 5, "252"}, - {11, 5, "462"}, - {11, 6, "462"}, - {100, 10, "17310309456440"}, - {100, 90, "17310309456440"}, - {1000, 10, "263409560461970212832400"}, - {1000, 990, "263409560461970212832400"}, - } { - if got := z.Binomial(test.n, test.k).String(); got != test.want { - t.Errorf("Binomial(%d, %d) = %s; want %s", test.n, test.k, got, test.want) - } - } -} - -func BenchmarkBinomial(b *testing.B) { - var z Int - for i := b.N - 1; i >= 0; i-- { - z.Binomial(1000, 990) - } -} - -// Examples from the Go Language Spec, section "Arithmetic operators" -var divisionSignsTests = []struct { - x, y int64 - q, r int64 // T-division - d, m int64 // Euclidian division -}{ - {5, 3, 1, 2, 1, 2}, - {-5, 3, -1, -2, -2, 1}, - {5, -3, -1, 2, -1, 2}, - {-5, -3, 1, -2, 2, 1}, - {1, 2, 0, 1, 0, 1}, - {8, 4, 2, 0, 2, 0}, -} - -func TestDivisionSigns(t *testing.T) { - for i, test := range divisionSignsTests { - x := NewInt(test.x) - y := NewInt(test.y) - q := NewInt(test.q) - r := NewInt(test.r) - d := NewInt(test.d) - m := NewInt(test.m) - - q1 := new(Int).Quo(x, y) - r1 := new(Int).Rem(x, y) - if !isNormalized(q1) { - t.Errorf("#%d Quo: %v is not normalized", i, *q1) - } - if !isNormalized(r1) { - t.Errorf("#%d Rem: %v is not normalized", i, *r1) - } - if q1.Cmp(q) != 0 || r1.Cmp(r) != 0 { - t.Errorf("#%d QuoRem: got (%s, %s), want (%s, %s)", i, q1, r1, q, r) - } - - q2, r2 := new(Int).QuoRem(x, y, new(Int)) - if !isNormalized(q2) { - t.Errorf("#%d Quo: %v is not normalized", i, *q2) - } - if !isNormalized(r2) { - t.Errorf("#%d Rem: %v is not normalized", i, *r2) - } - if q2.Cmp(q) != 0 || r2.Cmp(r) != 0 { - t.Errorf("#%d QuoRem: got (%s, %s), want (%s, %s)", i, q2, r2, q, r) - } - - d1 := new(Int).Div(x, y) - m1 := new(Int).Mod(x, y) - if !isNormalized(d1) { - t.Errorf("#%d Div: %v is not normalized", i, *d1) - } - if !isNormalized(m1) { - t.Errorf("#%d Mod: %v is not normalized", i, *m1) - } - if d1.Cmp(d) != 0 || m1.Cmp(m) != 0 { - t.Errorf("#%d DivMod: got (%s, %s), want (%s, %s)", i, d1, m1, d, m) - } - - d2, m2 := new(Int).DivMod(x, y, new(Int)) - if !isNormalized(d2) { - t.Errorf("#%d Div: %v is not normalized", i, *d2) - } - if !isNormalized(m2) { - t.Errorf("#%d Mod: %v is not normalized", i, *m2) - } - if d2.Cmp(d) != 0 || m2.Cmp(m) != 0 { - t.Errorf("#%d DivMod: got (%s, %s), want (%s, %s)", i, d2, m2, d, m) - } - } -} - -func norm(x nat) nat { - i := len(x) - for i > 0 && x[i-1] == 0 { - i-- - } - return x[:i] -} - -func TestBits(t *testing.T) { - for _, test := range []nat{ - nil, - {0}, - {1}, - {0, 1, 2, 3, 4}, - {4, 3, 2, 1, 0}, - {4, 3, 2, 1, 0, 0, 0, 0}, - } { - var z Int - z.neg = true - got := z.SetBits(test) - want := norm(test) - if got.abs.cmp(want) != 0 { - t.Errorf("SetBits(%v) = %v; want %v", test, got.abs, want) - } - - if got.neg { - t.Errorf("SetBits(%v): got negative result", test) - } - - bits := nat(z.Bits()) - if bits.cmp(want) != 0 { - t.Errorf("%v.Bits() = %v; want %v", z.abs, bits, want) - } - } -} - -func checkSetBytes(b []byte) bool { - hex1 := hex.EncodeToString(new(Int).SetBytes(b).Bytes()) - hex2 := hex.EncodeToString(b) - - for len(hex1) < len(hex2) { - hex1 = "0" + hex1 - } - - for len(hex1) > len(hex2) { - hex2 = "0" + hex2 - } - - return hex1 == hex2 -} - -func TestSetBytes(t *testing.T) { - if err := quick.Check(checkSetBytes, nil); err != nil { - t.Error(err) - } -} - -func checkBytes(b []byte) bool { - b2 := new(Int).SetBytes(b).Bytes() - return bytes.Equal(b, b2) -} - -func TestBytes(t *testing.T) { - if err := quick.Check(checkBytes, nil); err != nil { - t.Error(err) - } -} - -func checkQuo(x, y []byte) bool { - u := new(Int).SetBytes(x) - v := new(Int).SetBytes(y) - - if len(v.abs) == 0 { - return true - } - - r := new(Int) - q, r := new(Int).QuoRem(u, v, r) - - if r.Cmp(v) >= 0 { - return false - } - - uprime := new(Int).Set(q) - uprime.Mul(uprime, v) - uprime.Add(uprime, r) - - return uprime.Cmp(u) == 0 -} - -var quoTests = []struct { - x, y string - q, r string -}{ - { - "476217953993950760840509444250624797097991362735329973741718102894495832294430498335824897858659711275234906400899559094370964723884706254265559534144986498357", - "9353930466774385905609975137998169297361893554149986716853295022578535724979483772383667534691121982974895531435241089241440253066816724367338287092081996", - "50911", - "1", - }, - { - "11510768301994997771168", - "1328165573307167369775", - "8", - "885443715537658812968", - }, -} - -func TestQuo(t *testing.T) { - if err := quick.Check(checkQuo, nil); err != nil { - t.Error(err) - } - - for i, test := range quoTests { - x, _ := new(Int).SetString(test.x, 10) - y, _ := new(Int).SetString(test.y, 10) - expectedQ, _ := new(Int).SetString(test.q, 10) - expectedR, _ := new(Int).SetString(test.r, 10) - - r := new(Int) - q, r := new(Int).QuoRem(x, y, r) - - if q.Cmp(expectedQ) != 0 || r.Cmp(expectedR) != 0 { - t.Errorf("#%d got (%s, %s) want (%s, %s)", i, q, r, expectedQ, expectedR) - } - } -} - -func TestQuoStepD6(t *testing.T) { - // See Knuth, Volume 2, section 4.3.1, exercise 21. This code exercises - // a code path which only triggers 1 in 10^{-19} cases. - - u := &Int{false, nat{0, 0, 1 + 1<<(_W-1), _M ^ (1 << (_W - 1))}} - v := &Int{false, nat{5, 2 + 1<<(_W-1), 1 << (_W - 1)}} - - r := new(Int) - q, r := new(Int).QuoRem(u, v, r) - const expectedQ64 = "18446744073709551613" - const expectedR64 = "3138550867693340382088035895064302439801311770021610913807" - const expectedQ32 = "4294967293" - const expectedR32 = "39614081266355540837921718287" - if q.String() != expectedQ64 && q.String() != expectedQ32 || - r.String() != expectedR64 && r.String() != expectedR32 { - t.Errorf("got (%s, %s) want (%s, %s) or (%s, %s)", q, r, expectedQ64, expectedR64, expectedQ32, expectedR32) - } -} - -var bitLenTests = []struct { - in string - out int -}{ - {"-1", 1}, - {"0", 0}, - {"1", 1}, - {"2", 2}, - {"4", 3}, - {"0xabc", 12}, - {"0x8000", 16}, - {"0x80000000", 32}, - {"0x800000000000", 48}, - {"0x8000000000000000", 64}, - {"0x80000000000000000000", 80}, - {"-0x4000000000000000000000", 87}, -} - -func TestBitLen(t *testing.T) { - for i, test := range bitLenTests { - x, ok := new(Int).SetString(test.in, 0) - if !ok { - t.Errorf("#%d test input invalid: %s", i, test.in) - continue - } - - if n := x.BitLen(); n != test.out { - t.Errorf("#%d got %d want %d", i, n, test.out) - } - } -} - -var expTests = []struct { - x, y, m string - out string -}{ - // y <= 0 - {"0", "0", "", "1"}, - {"1", "0", "", "1"}, - {"-10", "0", "", "1"}, - {"1234", "-1", "", "1"}, - - // m == 1 - {"0", "0", "1", "0"}, - {"1", "0", "1", "0"}, - {"-10", "0", "1", "0"}, - {"1234", "-1", "1", "0"}, - - // misc - {"5", "-7", "", "1"}, - {"-5", "-7", "", "1"}, - {"5", "0", "", "1"}, - {"-5", "0", "", "1"}, - {"5", "1", "", "5"}, - {"-5", "1", "", "-5"}, - {"-5", "1", "7", "2"}, - {"-2", "3", "2", "0"}, - {"5", "2", "", "25"}, - {"1", "65537", "2", "1"}, - {"0x8000000000000000", "2", "", "0x40000000000000000000000000000000"}, - {"0x8000000000000000", "2", "6719", "4944"}, - {"0x8000000000000000", "3", "6719", "5447"}, - {"0x8000000000000000", "1000", "6719", "1603"}, - {"0x8000000000000000", "1000000", "6719", "3199"}, - {"0x8000000000000000", "-1000000", "6719", "1"}, - { - "2938462938472983472983659726349017249287491026512746239764525612965293865296239471239874193284792387498274256129746192347", - "298472983472983471903246121093472394872319615612417471234712061", - "29834729834729834729347290846729561262544958723956495615629569234729836259263598127342374289365912465901365498236492183464", - "23537740700184054162508175125554701713153216681790245129157191391322321508055833908509185839069455749219131480588829346291", - }, - // test case for issue 8822 - { - "-0x1BCE04427D8032319A89E5C4136456671AC620883F2C4139E57F91307C485AD2D6204F4F87A58262652DB5DBBAC72B0613E51B835E7153BEC6068F5C8D696B74DBD18FEC316AEF73985CF0475663208EB46B4F17DD9DA55367B03323E5491A70997B90C059FB34809E6EE55BCFBD5F2F52233BFE62E6AA9E4E26A1D4C2439883D14F2633D55D8AA66A1ACD5595E778AC3A280517F1157989E70C1A437B849F1877B779CC3CDDEDE2DAA6594A6C66D181A00A5F777EE60596D8773998F6E988DEAE4CCA60E4DDCF9590543C89F74F603259FCAD71660D30294FBBE6490300F78A9D63FA660DC9417B8B9DDA28BEB3977B621B988E23D4D954F322C3540541BC649ABD504C50FADFD9F0987D58A2BF689313A285E773FF02899A6EF887D1D4A0D2", - "0xB08FFB20760FFED58FADA86DFEF71AD72AA0FA763219618FE022C197E54708BB1191C66470250FCE8879487507CEE41381CA4D932F81C2B3F1AB20B539D50DCD", - "0xAC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73", - "21484252197776302499639938883777710321993113097987201050501182909581359357618579566746556372589385361683610524730509041328855066514963385522570894839035884713051640171474186548713546686476761306436434146475140156284389181808675016576845833340494848283681088886584219750554408060556769486628029028720727393293111678826356480455433909233520504112074401376133077150471237549474149190242010469539006449596611576612573955754349042329130631128234637924786466585703488460540228477440853493392086251021228087076124706778899179648655221663765993962724699135217212118535057766739392069738618682722216712319320435674779146070442", - }, -} - -func TestExp(t *testing.T) { - for i, test := range expTests { - x, ok1 := new(Int).SetString(test.x, 0) - y, ok2 := new(Int).SetString(test.y, 0) - out, ok3 := new(Int).SetString(test.out, 0) - - var ok4 bool - var m *Int - - if len(test.m) == 0 { - m, ok4 = nil, true - } else { - m, ok4 = new(Int).SetString(test.m, 0) - } - - if !ok1 || !ok2 || !ok3 || !ok4 { - t.Errorf("#%d: error in input", i) - continue - } - - z1 := new(Int).Exp(x, y, m) - if !isNormalized(z1) { - t.Errorf("#%d: %v is not normalized", i, *z1) - } - if z1.Cmp(out) != 0 { - t.Errorf("#%d: got %s want %s", i, z1, out) - } - - if m == nil { - // The result should be the same as for m == 0; - // specifically, there should be no div-zero panic. - m = &Int{abs: nat{}} // m != nil && len(m.abs) == 0 - z2 := new(Int).Exp(x, y, m) - if z2.Cmp(z1) != 0 { - t.Errorf("#%d: got %s want %s", i, z2, z1) - } - } - } -} - -func checkGcd(aBytes, bBytes []byte) bool { - x := new(Int) - y := new(Int) - a := new(Int).SetBytes(aBytes) - b := new(Int).SetBytes(bBytes) - - d := new(Int).GCD(x, y, a, b) - x.Mul(x, a) - y.Mul(y, b) - x.Add(x, y) - - return x.Cmp(d) == 0 -} - -var gcdTests = []struct { - d, x, y, a, b string -}{ - // a <= 0 || b <= 0 - {"0", "0", "0", "0", "0"}, - {"0", "0", "0", "0", "7"}, - {"0", "0", "0", "11", "0"}, - {"0", "0", "0", "-77", "35"}, - {"0", "0", "0", "64515", "-24310"}, - {"0", "0", "0", "-64515", "-24310"}, - - {"1", "-9", "47", "120", "23"}, - {"7", "1", "-2", "77", "35"}, - {"935", "-3", "8", "64515", "24310"}, - {"935000000000000000", "-3", "8", "64515000000000000000", "24310000000000000000"}, - {"1", "-221", "22059940471369027483332068679400581064239780177629666810348940098015901108344", "98920366548084643601728869055592650835572950932266967461790948584315647051443", "991"}, - - // test early exit (after one Euclidean iteration) in binaryGCD - {"1", "", "", "1", "98920366548084643601728869055592650835572950932266967461790948584315647051443"}, -} - -func testGcd(t *testing.T, d, x, y, a, b *Int) { - var X *Int - if x != nil { - X = new(Int) - } - var Y *Int - if y != nil { - Y = new(Int) - } - - D := new(Int).GCD(X, Y, a, b) - if D.Cmp(d) != 0 { - t.Errorf("GCD(%s, %s): got d = %s, want %s", a, b, D, d) - } - if x != nil && X.Cmp(x) != 0 { - t.Errorf("GCD(%s, %s): got x = %s, want %s", a, b, X, x) - } - if y != nil && Y.Cmp(y) != 0 { - t.Errorf("GCD(%s, %s): got y = %s, want %s", a, b, Y, y) - } - - // binaryGCD requires a > 0 && b > 0 - if a.Sign() <= 0 || b.Sign() <= 0 { - return - } - - D.binaryGCD(a, b) - if D.Cmp(d) != 0 { - t.Errorf("binaryGcd(%s, %s): got d = %s, want %s", a, b, D, d) - } -} - -func TestGcd(t *testing.T) { - for _, test := range gcdTests { - d, _ := new(Int).SetString(test.d, 0) - x, _ := new(Int).SetString(test.x, 0) - y, _ := new(Int).SetString(test.y, 0) - a, _ := new(Int).SetString(test.a, 0) - b, _ := new(Int).SetString(test.b, 0) - - testGcd(t, d, nil, nil, a, b) - testGcd(t, d, x, nil, a, b) - testGcd(t, d, nil, y, a, b) - testGcd(t, d, x, y, a, b) - } - - quick.Check(checkGcd, nil) -} - -var primes = []string{ - "2", - "3", - "5", - "7", - "11", - - "13756265695458089029", - "13496181268022124907", - "10953742525620032441", - "17908251027575790097", - - // http://golang.org/issue/638 - "18699199384836356663", - - "98920366548084643601728869055592650835572950932266967461790948584315647051443", - "94560208308847015747498523884063394671606671904944666360068158221458669711639", - - // http://primes.utm.edu/lists/small/small3.html - "449417999055441493994709297093108513015373787049558499205492347871729927573118262811508386655998299074566974373711472560655026288668094291699357843464363003144674940345912431129144354948751003607115263071543163", - "230975859993204150666423538988557839555560243929065415434980904258310530753006723857139742334640122533598517597674807096648905501653461687601339782814316124971547968912893214002992086353183070342498989426570593", - "5521712099665906221540423207019333379125265462121169655563495403888449493493629943498064604536961775110765377745550377067893607246020694972959780839151452457728855382113555867743022746090187341871655890805971735385789993", - "203956878356401977405765866929034577280193993314348263094772646453283062722701277632936616063144088173312372882677123879538709400158306567338328279154499698366071906766440037074217117805690872792848149112022286332144876183376326512083574821647933992961249917319836219304274280243803104015000563790123", -} - -var composites = []string{ - "0", - "1", - "21284175091214687912771199898307297748211672914763848041968395774954376176754", - "6084766654921918907427900243509372380954290099172559290432744450051395395951", - "84594350493221918389213352992032324280367711247940675652888030554255915464401", - "82793403787388584738507275144194252681", -} - -func TestProbablyPrime(t *testing.T) { - nreps := 20 - if testing.Short() { - nreps = 1 - } - for i, s := range primes { - p, _ := new(Int).SetString(s, 10) - if !p.ProbablyPrime(nreps) { - t.Errorf("#%d prime found to be non-prime (%s)", i, s) - } - } - - for i, s := range composites { - c, _ := new(Int).SetString(s, 10) - if c.ProbablyPrime(nreps) { - t.Errorf("#%d composite found to be prime (%s)", i, s) - } - if testing.Short() { - break - } - } - - // check that ProbablyPrime panics if n <= 0 - c := NewInt(11) // a prime - for _, n := range []int{-1, 0, 1} { - func() { - defer func() { - if n <= 0 && recover() == nil { - t.Fatalf("expected panic from ProbablyPrime(%d)", n) - } - }() - if !c.ProbablyPrime(n) { - t.Fatalf("%v should be a prime", c) - } - }() - } -} - -type intShiftTest struct { - in string - shift uint - out string -} - -var rshTests = []intShiftTest{ - {"0", 0, "0"}, - {"-0", 0, "0"}, - {"0", 1, "0"}, - {"0", 2, "0"}, - {"1", 0, "1"}, - {"1", 1, "0"}, - {"1", 2, "0"}, - {"2", 0, "2"}, - {"2", 1, "1"}, - {"-1", 0, "-1"}, - {"-1", 1, "-1"}, - {"-1", 10, "-1"}, - {"-100", 2, "-25"}, - {"-100", 3, "-13"}, - {"-100", 100, "-1"}, - {"4294967296", 0, "4294967296"}, - {"4294967296", 1, "2147483648"}, - {"4294967296", 2, "1073741824"}, - {"18446744073709551616", 0, "18446744073709551616"}, - {"18446744073709551616", 1, "9223372036854775808"}, - {"18446744073709551616", 2, "4611686018427387904"}, - {"18446744073709551616", 64, "1"}, - {"340282366920938463463374607431768211456", 64, "18446744073709551616"}, - {"340282366920938463463374607431768211456", 128, "1"}, -} - -func TestRsh(t *testing.T) { - for i, test := range rshTests { - in, _ := new(Int).SetString(test.in, 10) - expected, _ := new(Int).SetString(test.out, 10) - out := new(Int).Rsh(in, test.shift) - - if !isNormalized(out) { - t.Errorf("#%d: %v is not normalized", i, *out) - } - if out.Cmp(expected) != 0 { - t.Errorf("#%d: got %s want %s", i, out, expected) - } - } -} - -func TestRshSelf(t *testing.T) { - for i, test := range rshTests { - z, _ := new(Int).SetString(test.in, 10) - expected, _ := new(Int).SetString(test.out, 10) - z.Rsh(z, test.shift) - - if !isNormalized(z) { - t.Errorf("#%d: %v is not normalized", i, *z) - } - if z.Cmp(expected) != 0 { - t.Errorf("#%d: got %s want %s", i, z, expected) - } - } -} - -var lshTests = []intShiftTest{ - {"0", 0, "0"}, - {"0", 1, "0"}, - {"0", 2, "0"}, - {"1", 0, "1"}, - {"1", 1, "2"}, - {"1", 2, "4"}, - {"2", 0, "2"}, - {"2", 1, "4"}, - {"2", 2, "8"}, - {"-87", 1, "-174"}, - {"4294967296", 0, "4294967296"}, - {"4294967296", 1, "8589934592"}, - {"4294967296", 2, "17179869184"}, - {"18446744073709551616", 0, "18446744073709551616"}, - {"9223372036854775808", 1, "18446744073709551616"}, - {"4611686018427387904", 2, "18446744073709551616"}, - {"1", 64, "18446744073709551616"}, - {"18446744073709551616", 64, "340282366920938463463374607431768211456"}, - {"1", 128, "340282366920938463463374607431768211456"}, -} - -func TestLsh(t *testing.T) { - for i, test := range lshTests { - in, _ := new(Int).SetString(test.in, 10) - expected, _ := new(Int).SetString(test.out, 10) - out := new(Int).Lsh(in, test.shift) - - if !isNormalized(out) { - t.Errorf("#%d: %v is not normalized", i, *out) - } - if out.Cmp(expected) != 0 { - t.Errorf("#%d: got %s want %s", i, out, expected) - } - } -} - -func TestLshSelf(t *testing.T) { - for i, test := range lshTests { - z, _ := new(Int).SetString(test.in, 10) - expected, _ := new(Int).SetString(test.out, 10) - z.Lsh(z, test.shift) - - if !isNormalized(z) { - t.Errorf("#%d: %v is not normalized", i, *z) - } - if z.Cmp(expected) != 0 { - t.Errorf("#%d: got %s want %s", i, z, expected) - } - } -} - -func TestLshRsh(t *testing.T) { - for i, test := range rshTests { - in, _ := new(Int).SetString(test.in, 10) - out := new(Int).Lsh(in, test.shift) - out = out.Rsh(out, test.shift) - - if !isNormalized(out) { - t.Errorf("#%d: %v is not normalized", i, *out) - } - if in.Cmp(out) != 0 { - t.Errorf("#%d: got %s want %s", i, out, in) - } - } - for i, test := range lshTests { - in, _ := new(Int).SetString(test.in, 10) - out := new(Int).Lsh(in, test.shift) - out.Rsh(out, test.shift) - - if !isNormalized(out) { - t.Errorf("#%d: %v is not normalized", i, *out) - } - if in.Cmp(out) != 0 { - t.Errorf("#%d: got %s want %s", i, out, in) - } - } -} - -var int64Tests = []int64{ - 0, - 1, - -1, - 4294967295, - -4294967295, - 4294967296, - -4294967296, - 9223372036854775807, - -9223372036854775807, - -9223372036854775808, -} - -func TestInt64(t *testing.T) { - for i, testVal := range int64Tests { - in := NewInt(testVal) - out := in.Int64() - - if out != testVal { - t.Errorf("#%d got %d want %d", i, out, testVal) - } - } -} - -var uint64Tests = []uint64{ - 0, - 1, - 4294967295, - 4294967296, - 8589934591, - 8589934592, - 9223372036854775807, - 9223372036854775808, - 18446744073709551615, // 1<<64 - 1 -} - -func TestUint64(t *testing.T) { - in := new(Int) - for i, testVal := range uint64Tests { - in.SetUint64(testVal) - out := in.Uint64() - - if out != testVal { - t.Errorf("#%d got %d want %d", i, out, testVal) - } - - str := fmt.Sprint(testVal) - strOut := in.String() - if strOut != str { - t.Errorf("#%d.String got %s want %s", i, strOut, str) - } - } -} - -var bitwiseTests = []struct { - x, y string - and, or, xor, andNot string -}{ - {"0x00", "0x00", "0x00", "0x00", "0x00", "0x00"}, - {"0x00", "0x01", "0x00", "0x01", "0x01", "0x00"}, - {"0x01", "0x00", "0x00", "0x01", "0x01", "0x01"}, - {"-0x01", "0x00", "0x00", "-0x01", "-0x01", "-0x01"}, - {"-0xaf", "-0x50", "-0xf0", "-0x0f", "0xe1", "0x41"}, - {"0x00", "-0x01", "0x00", "-0x01", "-0x01", "0x00"}, - {"0x01", "0x01", "0x01", "0x01", "0x00", "0x00"}, - {"-0x01", "-0x01", "-0x01", "-0x01", "0x00", "0x00"}, - {"0x07", "0x08", "0x00", "0x0f", "0x0f", "0x07"}, - {"0x05", "0x0f", "0x05", "0x0f", "0x0a", "0x00"}, - {"0xff", "-0x0a", "0xf6", "-0x01", "-0xf7", "0x09"}, - {"0x013ff6", "0x9a4e", "0x1a46", "0x01bffe", "0x01a5b8", "0x0125b0"}, - {"-0x013ff6", "0x9a4e", "0x800a", "-0x0125b2", "-0x01a5bc", "-0x01c000"}, - {"-0x013ff6", "-0x9a4e", "-0x01bffe", "-0x1a46", "0x01a5b8", "0x8008"}, - { - "0x1000009dc6e3d9822cba04129bcbe3401", - "0xb9bd7d543685789d57cb918e833af352559021483cdb05cc21fd", - "0x1000001186210100001000009048c2001", - "0xb9bd7d543685789d57cb918e8bfeff7fddb2ebe87dfbbdfe35fd", - "0xb9bd7d543685789d57ca918e8ae69d6fcdb2eae87df2b97215fc", - "0x8c40c2d8822caa04120b8321400", - }, - { - "0x1000009dc6e3d9822cba04129bcbe3401", - "-0xb9bd7d543685789d57cb918e833af352559021483cdb05cc21fd", - "0x8c40c2d8822caa04120b8321401", - "-0xb9bd7d543685789d57ca918e82229142459020483cd2014001fd", - "-0xb9bd7d543685789d57ca918e8ae69d6fcdb2eae87df2b97215fe", - "0x1000001186210100001000009048c2000", - }, - { - "-0x1000009dc6e3d9822cba04129bcbe3401", - "-0xb9bd7d543685789d57cb918e833af352559021483cdb05cc21fd", - "-0xb9bd7d543685789d57cb918e8bfeff7fddb2ebe87dfbbdfe35fd", - "-0x1000001186210100001000009048c2001", - "0xb9bd7d543685789d57ca918e8ae69d6fcdb2eae87df2b97215fc", - "0xb9bd7d543685789d57ca918e82229142459020483cd2014001fc", - }, -} - -type bitFun func(z, x, y *Int) *Int - -func testBitFun(t *testing.T, msg string, f bitFun, x, y *Int, exp string) { - expected := new(Int) - expected.SetString(exp, 0) - - out := f(new(Int), x, y) - if out.Cmp(expected) != 0 { - t.Errorf("%s: got %s want %s", msg, out, expected) - } -} - -func testBitFunSelf(t *testing.T, msg string, f bitFun, x, y *Int, exp string) { - self := new(Int) - self.Set(x) - expected := new(Int) - expected.SetString(exp, 0) - - self = f(self, self, y) - if self.Cmp(expected) != 0 { - t.Errorf("%s: got %s want %s", msg, self, expected) - } -} - -func altBit(x *Int, i int) uint { - z := new(Int).Rsh(x, uint(i)) - z = z.And(z, NewInt(1)) - if z.Cmp(new(Int)) != 0 { - return 1 - } - return 0 -} - -func altSetBit(z *Int, x *Int, i int, b uint) *Int { - one := NewInt(1) - m := one.Lsh(one, uint(i)) - switch b { - case 1: - return z.Or(x, m) - case 0: - return z.AndNot(x, m) - } - panic("set bit is not 0 or 1") -} - -func testBitset(t *testing.T, x *Int) { - n := x.BitLen() - z := new(Int).Set(x) - z1 := new(Int).Set(x) - for i := 0; i < n+10; i++ { - old := z.Bit(i) - old1 := altBit(z1, i) - if old != old1 { - t.Errorf("bitset: inconsistent value for Bit(%s, %d), got %v want %v", z1, i, old, old1) - } - z := new(Int).SetBit(z, i, 1) - z1 := altSetBit(new(Int), z1, i, 1) - if z.Bit(i) == 0 { - t.Errorf("bitset: bit %d of %s got 0 want 1", i, x) - } - if z.Cmp(z1) != 0 { - t.Errorf("bitset: inconsistent value after SetBit 1, got %s want %s", z, z1) - } - z.SetBit(z, i, 0) - altSetBit(z1, z1, i, 0) - if z.Bit(i) != 0 { - t.Errorf("bitset: bit %d of %s got 1 want 0", i, x) - } - if z.Cmp(z1) != 0 { - t.Errorf("bitset: inconsistent value after SetBit 0, got %s want %s", z, z1) - } - altSetBit(z1, z1, i, old) - z.SetBit(z, i, old) - if z.Cmp(z1) != 0 { - t.Errorf("bitset: inconsistent value after SetBit old, got %s want %s", z, z1) - } - } - if z.Cmp(x) != 0 { - t.Errorf("bitset: got %s want %s", z, x) - } -} - -var bitsetTests = []struct { - x string - i int - b uint -}{ - {"0", 0, 0}, - {"0", 200, 0}, - {"1", 0, 1}, - {"1", 1, 0}, - {"-1", 0, 1}, - {"-1", 200, 1}, - {"0x2000000000000000000000000000", 108, 0}, - {"0x2000000000000000000000000000", 109, 1}, - {"0x2000000000000000000000000000", 110, 0}, - {"-0x2000000000000000000000000001", 108, 1}, - {"-0x2000000000000000000000000001", 109, 0}, - {"-0x2000000000000000000000000001", 110, 1}, -} - -func TestBitSet(t *testing.T) { - for _, test := range bitwiseTests { - x := new(Int) - x.SetString(test.x, 0) - testBitset(t, x) - x = new(Int) - x.SetString(test.y, 0) - testBitset(t, x) - } - for i, test := range bitsetTests { - x := new(Int) - x.SetString(test.x, 0) - b := x.Bit(test.i) - if b != test.b { - t.Errorf("#%d got %v want %v", i, b, test.b) - } - } - z := NewInt(1) - z.SetBit(NewInt(0), 2, 1) - if z.Cmp(NewInt(4)) != 0 { - t.Errorf("destination leaked into result; got %s want 4", z) - } -} - -func BenchmarkBitset(b *testing.B) { - z := new(Int) - z.SetBit(z, 512, 1) - b.ResetTimer() - b.StartTimer() - for i := b.N - 1; i >= 0; i-- { - z.SetBit(z, i&512, 1) - } -} - -func BenchmarkBitsetNeg(b *testing.B) { - z := NewInt(-1) - z.SetBit(z, 512, 0) - b.ResetTimer() - b.StartTimer() - for i := b.N - 1; i >= 0; i-- { - z.SetBit(z, i&512, 0) - } -} - -func BenchmarkBitsetOrig(b *testing.B) { - z := new(Int) - altSetBit(z, z, 512, 1) - b.ResetTimer() - b.StartTimer() - for i := b.N - 1; i >= 0; i-- { - altSetBit(z, z, i&512, 1) - } -} - -func BenchmarkBitsetNegOrig(b *testing.B) { - z := NewInt(-1) - altSetBit(z, z, 512, 0) - b.ResetTimer() - b.StartTimer() - for i := b.N - 1; i >= 0; i-- { - altSetBit(z, z, i&512, 0) - } -} - -func TestBitwise(t *testing.T) { - x := new(Int) - y := new(Int) - for _, test := range bitwiseTests { - x.SetString(test.x, 0) - y.SetString(test.y, 0) - - testBitFun(t, "and", (*Int).And, x, y, test.and) - testBitFunSelf(t, "and", (*Int).And, x, y, test.and) - testBitFun(t, "andNot", (*Int).AndNot, x, y, test.andNot) - testBitFunSelf(t, "andNot", (*Int).AndNot, x, y, test.andNot) - testBitFun(t, "or", (*Int).Or, x, y, test.or) - testBitFunSelf(t, "or", (*Int).Or, x, y, test.or) - testBitFun(t, "xor", (*Int).Xor, x, y, test.xor) - testBitFunSelf(t, "xor", (*Int).Xor, x, y, test.xor) - } -} - -var notTests = []struct { - in string - out string -}{ - {"0", "-1"}, - {"1", "-2"}, - {"7", "-8"}, - {"0", "-1"}, - {"-81910", "81909"}, - { - "298472983472983471903246121093472394872319615612417471234712061", - "-298472983472983471903246121093472394872319615612417471234712062", - }, -} - -func TestNot(t *testing.T) { - in := new(Int) - out := new(Int) - expected := new(Int) - for i, test := range notTests { - in.SetString(test.in, 10) - expected.SetString(test.out, 10) - out = out.Not(in) - if out.Cmp(expected) != 0 { - t.Errorf("#%d: got %s want %s", i, out, expected) - } - out = out.Not(out) - if out.Cmp(in) != 0 { - t.Errorf("#%d: got %s want %s", i, out, in) - } - } -} - -var modInverseTests = []struct { - element string - modulus string -}{ - {"1234567", "458948883992"}, - {"239487239847", "2410312426921032588552076022197566074856950548502459942654116941958108831682612228890093858261341614673227141477904012196503648957050582631942730706805009223062734745341073406696246014589361659774041027169249453200378729434170325843778659198143763193776859869524088940195577346119843545301547043747207749969763750084308926339295559968882457872412993810129130294592999947926365264059284647209730384947211681434464714438488520940127459844288859336526896320919633919"}, -} - -func TestModInverse(t *testing.T) { - var element, modulus, gcd, inverse Int - one := NewInt(1) - for i, test := range modInverseTests { - (&element).SetString(test.element, 10) - (&modulus).SetString(test.modulus, 10) - (&inverse).ModInverse(&element, &modulus) - (&inverse).Mul(&inverse, &element) - (&inverse).Mod(&inverse, &modulus) - if (&inverse).Cmp(one) != 0 { - t.Errorf("#%d: failed (e·e^(-1)=%s)", i, &inverse) - } - } - // exhaustive test for small values - for n := 2; n < 100; n++ { - (&modulus).SetInt64(int64(n)) - for x := 1; x < n; x++ { - (&element).SetInt64(int64(x)) - (&gcd).GCD(nil, nil, &element, &modulus) - if (&gcd).Cmp(one) != 0 { - continue - } - (&inverse).ModInverse(&element, &modulus) - (&inverse).Mul(&inverse, &element) - (&inverse).Mod(&inverse, &modulus) - if (&inverse).Cmp(one) != 0 { - t.Errorf("ModInverse(%d,%d)*%d%%%d=%d, not 1", &element, &modulus, &element, &modulus, &inverse) - } - } - } -} - -var encodingTests = []string{ - "-539345864568634858364538753846587364875430589374589", - "-678645873", - "-100", - "-2", - "-1", - "0", - "1", - "2", - "10", - "42", - "1234567890", - "298472983472983471903246121093472394872319615612417471234712061", -} - -func TestIntGobEncoding(t *testing.T) { - var medium bytes.Buffer - enc := gob.NewEncoder(&medium) - dec := gob.NewDecoder(&medium) - for _, test := range encodingTests { - medium.Reset() // empty buffer for each test case (in case of failures) - var tx Int - tx.SetString(test, 10) - if err := enc.Encode(&tx); err != nil { - t.Errorf("encoding of %s failed: %s", &tx, err) - } - var rx Int - if err := dec.Decode(&rx); err != nil { - t.Errorf("decoding of %s failed: %s", &tx, err) - } - if rx.Cmp(&tx) != 0 { - t.Errorf("transmission of %s failed: got %s want %s", &tx, &rx, &tx) - } - } -} - -// Sending a nil Int pointer (inside a slice) on a round trip through gob should yield a zero. -// TODO: top-level nils. -func TestGobEncodingNilIntInSlice(t *testing.T) { - buf := new(bytes.Buffer) - enc := gob.NewEncoder(buf) - dec := gob.NewDecoder(buf) - - var in = make([]*Int, 1) - err := enc.Encode(&in) - if err != nil { - t.Errorf("gob encode failed: %q", err) - } - var out []*Int - err = dec.Decode(&out) - if err != nil { - t.Fatalf("gob decode failed: %q", err) - } - if len(out) != 1 { - t.Fatalf("wrong len; want 1 got %d", len(out)) - } - var zero Int - if out[0].Cmp(&zero) != 0 { - t.Errorf("transmission of (*Int)(nill) failed: got %s want 0", out) - } -} - -func TestIntJSONEncoding(t *testing.T) { - for _, test := range encodingTests { - var tx Int - tx.SetString(test, 10) - b, err := json.Marshal(&tx) - if err != nil { - t.Errorf("marshaling of %s failed: %s", &tx, err) - } - var rx Int - if err := json.Unmarshal(b, &rx); err != nil { - t.Errorf("unmarshaling of %s failed: %s", &tx, err) - } - if rx.Cmp(&tx) != 0 { - t.Errorf("JSON encoding of %s failed: got %s want %s", &tx, &rx, &tx) - } - } -} - -var intVals = []string{ - "-141592653589793238462643383279502884197169399375105820974944592307816406286", - "-1415926535897932384626433832795028841971", - "-141592653589793", - "-1", - "0", - "1", - "141592653589793", - "1415926535897932384626433832795028841971", - "141592653589793238462643383279502884197169399375105820974944592307816406286", -} - -func TestIntJSONEncodingTextMarshaller(t *testing.T) { - for _, num := range intVals { - var tx Int - tx.SetString(num, 0) - b, err := json.Marshal(&tx) - if err != nil { - t.Errorf("marshaling of %s failed: %s", &tx, err) - continue - } - var rx Int - if err := json.Unmarshal(b, &rx); err != nil { - t.Errorf("unmarshaling of %s failed: %s", &tx, err) - continue - } - if rx.Cmp(&tx) != 0 { - t.Errorf("JSON encoding of %s failed: got %s want %s", &tx, &rx, &tx) - } - } -} - -func TestIntXMLEncodingTextMarshaller(t *testing.T) { - for _, num := range intVals { - var tx Int - tx.SetString(num, 0) - b, err := xml.Marshal(&tx) - if err != nil { - t.Errorf("marshaling of %s failed: %s", &tx, err) - continue - } - var rx Int - if err := xml.Unmarshal(b, &rx); err != nil { - t.Errorf("unmarshaling of %s failed: %s", &tx, err) - continue - } - if rx.Cmp(&tx) != 0 { - t.Errorf("XML encoding of %s failed: got %s want %s", &tx, &rx, &tx) - } - } -} - -func TestIssue2607(t *testing.T) { - // This code sequence used to hang. - n := NewInt(10) - n.Rand(rand.New(rand.NewSource(9)), n) -} diff --git a/src/cmd/internal/gc/big/intconv.go b/src/cmd/internal/gc/big/intconv.go deleted file mode 100644 index 9c68a22bed..0000000000 --- a/src/cmd/internal/gc/big/intconv.go +++ /dev/null @@ -1,228 +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. - -// This file implements int-to-string conversion functions. - -package big - -import ( - "errors" - "fmt" - "io" -) - -func (x *Int) String() string { - switch { - case x == nil: - return "" - case x.neg: - return "-" + x.abs.decimalString() - } - return x.abs.decimalString() -} - -func charset(ch rune) string { - switch ch { - case 'b': - return lowercaseDigits[0:2] - case 'o': - return lowercaseDigits[0:8] - case 'd', 's', 'v': - return lowercaseDigits[0:10] - case 'x': - return lowercaseDigits[0:16] - case 'X': - return uppercaseDigits[0:16] - } - return "" // unknown format -} - -// write count copies of text to s -func writeMultiple(s fmt.State, text string, count int) { - if len(text) > 0 { - b := []byte(text) - for ; count > 0; count-- { - s.Write(b) - } - } -} - -// Format is a support routine for fmt.Formatter. It accepts -// the formats 'b' (binary), 'o' (octal), 'd' (decimal), 'x' -// (lowercase hexadecimal), and 'X' (uppercase hexadecimal). -// Also supported are the full suite of package fmt's format -// verbs for integral types, including '+', '-', and ' ' -// for sign control, '#' for leading zero in octal and for -// hexadecimal, a leading "0x" or "0X" for "%#x" and "%#X" -// respectively, specification of minimum digits precision, -// output field width, space or zero padding, and left or -// right justification. -// -func (x *Int) Format(s fmt.State, ch rune) { - cs := charset(ch) - - // special cases - switch { - case cs == "": - // unknown format - fmt.Fprintf(s, "%%!%c(big.Int=%s)", ch, x.String()) - return - case x == nil: - fmt.Fprint(s, "") - return - } - - // determine sign character - sign := "" - switch { - case x.neg: - sign = "-" - case s.Flag('+'): // supersedes ' ' when both specified - sign = "+" - case s.Flag(' '): - sign = " " - } - - // determine prefix characters for indicating output base - prefix := "" - if s.Flag('#') { - switch ch { - case 'o': // octal - prefix = "0" - case 'x': // hexadecimal - prefix = "0x" - case 'X': - prefix = "0X" - } - } - - // determine digits with base set by len(cs) and digit characters from cs - digits := x.abs.string(cs) - - // number of characters for the three classes of number padding - var left int // space characters to left of digits for right justification ("%8d") - var zeroes int // zero characters (actually cs[0]) as left-most digits ("%.8d") - var right int // space characters to right of digits for left justification ("%-8d") - - // determine number padding from precision: the least number of digits to output - precision, precisionSet := s.Precision() - if precisionSet { - switch { - case len(digits) < precision: - zeroes = precision - len(digits) // count of zero padding - case digits == "0" && precision == 0: - return // print nothing if zero value (x == 0) and zero precision ("." or ".0") - } - } - - // determine field pad from width: the least number of characters to output - length := len(sign) + len(prefix) + zeroes + len(digits) - if width, widthSet := s.Width(); widthSet && length < width { // pad as specified - switch d := width - length; { - case s.Flag('-'): - // pad on the right with spaces; supersedes '0' when both specified - right = d - case s.Flag('0') && !precisionSet: - // pad with zeroes unless precision also specified - zeroes = d - default: - // pad on the left with spaces - left = d - } - } - - // print number as [left pad][sign][prefix][zero pad][digits][right pad] - writeMultiple(s, " ", left) - writeMultiple(s, sign, 1) - writeMultiple(s, prefix, 1) - writeMultiple(s, "0", zeroes) - writeMultiple(s, digits, 1) - writeMultiple(s, " ", right) -} - -// scan sets z to the integer value corresponding to the longest possible prefix -// read from r representing a signed integer number in a given conversion base. -// It returns z, the actual conversion base used, and an error, if any. In the -// error case, the value of z is undefined but the returned value is nil. The -// syntax follows the syntax of integer literals in Go. -// -// The base argument must be 0 or a value from 2 through MaxBase. If the base -// is 0, the string prefix determines the actual conversion base. A prefix of -// ``0x'' or ``0X'' selects base 16; the ``0'' prefix selects base 8, and a -// ``0b'' or ``0B'' prefix selects base 2. Otherwise the selected base is 10. -// -func (z *Int) scan(r io.ByteScanner, base int) (*Int, int, error) { - // determine sign - neg, err := scanSign(r) - if err != nil { - return nil, 0, err - } - - // determine mantissa - z.abs, base, _, err = z.abs.scan(r, base, false) - if err != nil { - return nil, base, err - } - z.neg = len(z.abs) > 0 && neg // 0 has no sign - - return z, base, nil -} - -func scanSign(r io.ByteScanner) (neg bool, err error) { - var ch byte - if ch, err = r.ReadByte(); err != nil { - return false, err - } - switch ch { - case '-': - neg = true - case '+': - // nothing to do - default: - r.UnreadByte() - } - return -} - -// byteReader is a local wrapper around fmt.ScanState; -// it implements the ByteReader interface. -type byteReader struct { - fmt.ScanState -} - -func (r byteReader) ReadByte() (byte, error) { - ch, size, err := r.ReadRune() - if size != 1 && err == nil { - err = fmt.Errorf("invalid rune %#U", ch) - } - return byte(ch), err -} - -func (r byteReader) UnreadByte() error { - return r.UnreadRune() -} - -// Scan is a support routine for fmt.Scanner; it sets z to the value of -// the scanned number. It accepts the formats 'b' (binary), 'o' (octal), -// 'd' (decimal), 'x' (lowercase hexadecimal), and 'X' (uppercase hexadecimal). -func (z *Int) Scan(s fmt.ScanState, ch rune) error { - s.SkipSpace() // skip leading space characters - base := 0 - switch ch { - case 'b': - base = 2 - case 'o': - base = 8 - case 'd': - base = 10 - case 'x', 'X': - base = 16 - case 's', 'v': - // let scan determine the base - default: - return errors.New("Int.Scan: invalid verb") - } - _, _, err := z.scan(byteReader{s}, base) - return err -} diff --git a/src/cmd/internal/gc/big/intconv_test.go b/src/cmd/internal/gc/big/intconv_test.go deleted file mode 100644 index 2deb84b48f..0000000000 --- a/src/cmd/internal/gc/big/intconv_test.go +++ /dev/null @@ -1,342 +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 big - -import ( - "bytes" - "fmt" - "testing" -) - -var stringTests = []struct { - in string - out string - base int - val int64 - ok bool -}{ - {in: "", ok: false}, - {in: "a", ok: false}, - {in: "z", ok: false}, - {in: "+", ok: false}, - {in: "-", ok: false}, - {in: "0b", ok: false}, - {in: "0x", ok: false}, - {in: "2", base: 2, ok: false}, - {in: "0b2", base: 0, ok: false}, - {in: "08", ok: false}, - {in: "8", base: 8, ok: false}, - {in: "0xg", base: 0, ok: false}, - {in: "g", base: 16, ok: false}, - {"0", "0", 0, 0, true}, - {"0", "0", 10, 0, true}, - {"0", "0", 16, 0, true}, - {"+0", "0", 0, 0, true}, - {"-0", "0", 0, 0, true}, - {"10", "10", 0, 10, true}, - {"10", "10", 10, 10, true}, - {"10", "10", 16, 16, true}, - {"-10", "-10", 16, -16, true}, - {"+10", "10", 16, 16, true}, - {"0x10", "16", 0, 16, true}, - {in: "0x10", base: 16, ok: false}, - {"-0x10", "-16", 0, -16, true}, - {"+0x10", "16", 0, 16, true}, - {"00", "0", 0, 0, true}, - {"0", "0", 8, 0, true}, - {"07", "7", 0, 7, true}, - {"7", "7", 8, 7, true}, - {"023", "19", 0, 19, true}, - {"23", "23", 8, 19, true}, - {"cafebabe", "cafebabe", 16, 0xcafebabe, true}, - {"0b0", "0", 0, 0, true}, - {"-111", "-111", 2, -7, true}, - {"-0b111", "-7", 0, -7, true}, - {"0b1001010111", "599", 0, 0x257, true}, - {"1001010111", "1001010111", 2, 0x257, true}, -} - -func format(base int) string { - switch base { - case 2: - return "%b" - case 8: - return "%o" - case 16: - return "%x" - } - return "%d" -} - -func TestGetString(t *testing.T) { - z := new(Int) - for i, test := range stringTests { - if !test.ok { - continue - } - z.SetInt64(test.val) - - if test.base == 10 { - s := z.String() - if s != test.out { - t.Errorf("#%da got %s; want %s", i, s, test.out) - } - } - - s := fmt.Sprintf(format(test.base), z) - if s != test.out { - t.Errorf("#%db got %s; want %s", i, s, test.out) - } - } -} - -func TestSetString(t *testing.T) { - tmp := new(Int) - for i, test := range stringTests { - // initialize to a non-zero value so that issues with parsing - // 0 are detected - tmp.SetInt64(1234567890) - n1, ok1 := new(Int).SetString(test.in, test.base) - n2, ok2 := tmp.SetString(test.in, test.base) - expected := NewInt(test.val) - if ok1 != test.ok || ok2 != test.ok { - t.Errorf("#%d (input '%s') ok incorrect (should be %t)", i, test.in, test.ok) - continue - } - if !ok1 { - if n1 != nil { - t.Errorf("#%d (input '%s') n1 != nil", i, test.in) - } - continue - } - if !ok2 { - if n2 != nil { - t.Errorf("#%d (input '%s') n2 != nil", i, test.in) - } - continue - } - - if ok1 && !isNormalized(n1) { - t.Errorf("#%d (input '%s'): %v is not normalized", i, test.in, *n1) - } - if ok2 && !isNormalized(n2) { - t.Errorf("#%d (input '%s'): %v is not normalized", i, test.in, *n2) - } - - if n1.Cmp(expected) != 0 { - t.Errorf("#%d (input '%s') got: %s want: %d", i, test.in, n1, test.val) - } - if n2.Cmp(expected) != 0 { - t.Errorf("#%d (input '%s') got: %s want: %d", i, test.in, n2, test.val) - } - } -} - -var formatTests = []struct { - input string - format string - output string -}{ - {"", "%x", ""}, - {"", "%#x", ""}, - {"", "%#y", "%!y(big.Int=)"}, - - {"10", "%b", "1010"}, - {"10", "%o", "12"}, - {"10", "%d", "10"}, - {"10", "%v", "10"}, - {"10", "%x", "a"}, - {"10", "%X", "A"}, - {"-10", "%X", "-A"}, - {"10", "%y", "%!y(big.Int=10)"}, - {"-10", "%y", "%!y(big.Int=-10)"}, - - {"10", "%#b", "1010"}, - {"10", "%#o", "012"}, - {"10", "%#d", "10"}, - {"10", "%#v", "10"}, - {"10", "%#x", "0xa"}, - {"10", "%#X", "0XA"}, - {"-10", "%#X", "-0XA"}, - {"10", "%#y", "%!y(big.Int=10)"}, - {"-10", "%#y", "%!y(big.Int=-10)"}, - - {"1234", "%d", "1234"}, - {"1234", "%3d", "1234"}, - {"1234", "%4d", "1234"}, - {"-1234", "%d", "-1234"}, - {"1234", "% 5d", " 1234"}, - {"1234", "%+5d", "+1234"}, - {"1234", "%-5d", "1234 "}, - {"1234", "%x", "4d2"}, - {"1234", "%X", "4D2"}, - {"-1234", "%3x", "-4d2"}, - {"-1234", "%4x", "-4d2"}, - {"-1234", "%5x", " -4d2"}, - {"-1234", "%-5x", "-4d2 "}, - {"1234", "%03d", "1234"}, - {"1234", "%04d", "1234"}, - {"1234", "%05d", "01234"}, - {"1234", "%06d", "001234"}, - {"-1234", "%06d", "-01234"}, - {"1234", "%+06d", "+01234"}, - {"1234", "% 06d", " 01234"}, - {"1234", "%-6d", "1234 "}, - {"1234", "%-06d", "1234 "}, - {"-1234", "%-06d", "-1234 "}, - - {"1234", "%.3d", "1234"}, - {"1234", "%.4d", "1234"}, - {"1234", "%.5d", "01234"}, - {"1234", "%.6d", "001234"}, - {"-1234", "%.3d", "-1234"}, - {"-1234", "%.4d", "-1234"}, - {"-1234", "%.5d", "-01234"}, - {"-1234", "%.6d", "-001234"}, - - {"1234", "%8.3d", " 1234"}, - {"1234", "%8.4d", " 1234"}, - {"1234", "%8.5d", " 01234"}, - {"1234", "%8.6d", " 001234"}, - {"-1234", "%8.3d", " -1234"}, - {"-1234", "%8.4d", " -1234"}, - {"-1234", "%8.5d", " -01234"}, - {"-1234", "%8.6d", " -001234"}, - - {"1234", "%+8.3d", " +1234"}, - {"1234", "%+8.4d", " +1234"}, - {"1234", "%+8.5d", " +01234"}, - {"1234", "%+8.6d", " +001234"}, - {"-1234", "%+8.3d", " -1234"}, - {"-1234", "%+8.4d", " -1234"}, - {"-1234", "%+8.5d", " -01234"}, - {"-1234", "%+8.6d", " -001234"}, - - {"1234", "% 8.3d", " 1234"}, - {"1234", "% 8.4d", " 1234"}, - {"1234", "% 8.5d", " 01234"}, - {"1234", "% 8.6d", " 001234"}, - {"-1234", "% 8.3d", " -1234"}, - {"-1234", "% 8.4d", " -1234"}, - {"-1234", "% 8.5d", " -01234"}, - {"-1234", "% 8.6d", " -001234"}, - - {"1234", "%.3x", "4d2"}, - {"1234", "%.4x", "04d2"}, - {"1234", "%.5x", "004d2"}, - {"1234", "%.6x", "0004d2"}, - {"-1234", "%.3x", "-4d2"}, - {"-1234", "%.4x", "-04d2"}, - {"-1234", "%.5x", "-004d2"}, - {"-1234", "%.6x", "-0004d2"}, - - {"1234", "%8.3x", " 4d2"}, - {"1234", "%8.4x", " 04d2"}, - {"1234", "%8.5x", " 004d2"}, - {"1234", "%8.6x", " 0004d2"}, - {"-1234", "%8.3x", " -4d2"}, - {"-1234", "%8.4x", " -04d2"}, - {"-1234", "%8.5x", " -004d2"}, - {"-1234", "%8.6x", " -0004d2"}, - - {"1234", "%+8.3x", " +4d2"}, - {"1234", "%+8.4x", " +04d2"}, - {"1234", "%+8.5x", " +004d2"}, - {"1234", "%+8.6x", " +0004d2"}, - {"-1234", "%+8.3x", " -4d2"}, - {"-1234", "%+8.4x", " -04d2"}, - {"-1234", "%+8.5x", " -004d2"}, - {"-1234", "%+8.6x", " -0004d2"}, - - {"1234", "% 8.3x", " 4d2"}, - {"1234", "% 8.4x", " 04d2"}, - {"1234", "% 8.5x", " 004d2"}, - {"1234", "% 8.6x", " 0004d2"}, - {"1234", "% 8.7x", " 00004d2"}, - {"1234", "% 8.8x", " 000004d2"}, - {"-1234", "% 8.3x", " -4d2"}, - {"-1234", "% 8.4x", " -04d2"}, - {"-1234", "% 8.5x", " -004d2"}, - {"-1234", "% 8.6x", " -0004d2"}, - {"-1234", "% 8.7x", "-00004d2"}, - {"-1234", "% 8.8x", "-000004d2"}, - - {"1234", "%-8.3d", "1234 "}, - {"1234", "%-8.4d", "1234 "}, - {"1234", "%-8.5d", "01234 "}, - {"1234", "%-8.6d", "001234 "}, - {"1234", "%-8.7d", "0001234 "}, - {"1234", "%-8.8d", "00001234"}, - {"-1234", "%-8.3d", "-1234 "}, - {"-1234", "%-8.4d", "-1234 "}, - {"-1234", "%-8.5d", "-01234 "}, - {"-1234", "%-8.6d", "-001234 "}, - {"-1234", "%-8.7d", "-0001234"}, - {"-1234", "%-8.8d", "-00001234"}, - - {"16777215", "%b", "111111111111111111111111"}, // 2**24 - 1 - - {"0", "%.d", ""}, - {"0", "%.0d", ""}, - {"0", "%3.d", ""}, -} - -func TestFormat(t *testing.T) { - for i, test := range formatTests { - var x *Int - if test.input != "" { - var ok bool - x, ok = new(Int).SetString(test.input, 0) - if !ok { - t.Errorf("#%d failed reading input %s", i, test.input) - } - } - output := fmt.Sprintf(test.format, x) - if output != test.output { - t.Errorf("#%d got %q; want %q, {%q, %q, %q}", i, output, test.output, test.input, test.format, test.output) - } - } -} - -var scanTests = []struct { - input string - format string - output string - remaining int -}{ - {"1010", "%b", "10", 0}, - {"0b1010", "%v", "10", 0}, - {"12", "%o", "10", 0}, - {"012", "%v", "10", 0}, - {"10", "%d", "10", 0}, - {"10", "%v", "10", 0}, - {"a", "%x", "10", 0}, - {"0xa", "%v", "10", 0}, - {"A", "%X", "10", 0}, - {"-A", "%X", "-10", 0}, - {"+0b1011001", "%v", "89", 0}, - {"0xA", "%v", "10", 0}, - {"0 ", "%v", "0", 1}, - {"2+3", "%v", "2", 2}, - {"0XABC 12", "%v", "2748", 3}, -} - -func TestScan(t *testing.T) { - var buf bytes.Buffer - for i, test := range scanTests { - x := new(Int) - buf.Reset() - buf.WriteString(test.input) - if _, err := fmt.Fscanf(&buf, test.format, x); err != nil { - t.Errorf("#%d error: %s", i, err) - } - if x.String() != test.output { - t.Errorf("#%d got %s; want %s", i, x.String(), test.output) - } - if buf.Len() != test.remaining { - t.Errorf("#%d got %d bytes remaining; want %d", i, buf.Len(), test.remaining) - } - } -} diff --git a/src/cmd/internal/gc/big/nat.go b/src/cmd/internal/gc/big/nat.go deleted file mode 100644 index 2a279d186c..0000000000 --- a/src/cmd/internal/gc/big/nat.go +++ /dev/null @@ -1,1155 +0,0 @@ -// Copyright 2009 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 big implements multi-precision arithmetic (big numbers). -// The following numeric types are supported: -// -// Int signed integers -// Rat rational numbers -// Float floating-point numbers -// -// Methods are typically of the form: -// -// func (z *T) Unary(x *T) *T // z = op x -// func (z *T) Binary(x, y *T) *T // z = x op y -// func (x *T) M() T1 // v = x.M() -// -// with T one of Int, Rat, or Float. For unary and binary operations, the -// result is the receiver (usually named z in that case); if it is one of -// the operands x or y it may be overwritten (and its memory reused). -// To enable chaining of operations, the result is also returned. Methods -// returning a result other than *Int, *Rat, or *Float take an operand as -// the receiver (usually named x in that case). -// -package big - -// This file contains operations on unsigned multi-precision integers. -// These are the building blocks for the operations on signed integers -// and rationals. - -import "math/rand" - -// An unsigned integer x of the form -// -// x = x[n-1]*_B^(n-1) + x[n-2]*_B^(n-2) + ... + x[1]*_B + x[0] -// -// with 0 <= x[i] < _B and 0 <= i < n is stored in a slice of length n, -// with the digits x[i] as the slice elements. -// -// A number is normalized if the slice contains no leading 0 digits. -// During arithmetic operations, denormalized values may occur but are -// always normalized before returning the final result. The normalized -// representation of 0 is the empty or nil slice (length = 0). -// -type nat []Word - -var ( - natOne = nat{1} - natTwo = nat{2} - natTen = nat{10} -) - -func (z nat) clear() { - for i := range z { - z[i] = 0 - } -} - -func (z nat) norm() nat { - i := len(z) - for i > 0 && z[i-1] == 0 { - i-- - } - return z[0:i] -} - -func (z nat) make(n int) nat { - if n <= cap(z) { - return z[:n] // reuse z - } - // Choosing a good value for e has significant performance impact - // because it increases the chance that a value can be reused. - const e = 4 // extra capacity - return make(nat, n, n+e) -} - -func (z nat) setWord(x Word) nat { - if x == 0 { - return z[:0] - } - z = z.make(1) - z[0] = x - return z -} - -func (z nat) setUint64(x uint64) nat { - // single-digit values - if w := Word(x); uint64(w) == x { - return z.setWord(w) - } - - // compute number of words n required to represent x - n := 0 - for t := x; t > 0; t >>= _W { - n++ - } - - // split x into n words - z = z.make(n) - for i := range z { - z[i] = Word(x & _M) - x >>= _W - } - - return z -} - -func (z nat) set(x nat) nat { - z = z.make(len(x)) - copy(z, x) - return z -} - -func (z nat) add(x, y nat) nat { - m := len(x) - n := len(y) - - switch { - case m < n: - return z.add(y, x) - case m == 0: - // n == 0 because m >= n; result is 0 - return z[:0] - case n == 0: - // result is x - return z.set(x) - } - // m > 0 - - z = z.make(m + 1) - c := addVV(z[0:n], x, y) - if m > n { - c = addVW(z[n:m], x[n:], c) - } - z[m] = c - - return z.norm() -} - -func (z nat) sub(x, y nat) nat { - m := len(x) - n := len(y) - - switch { - case m < n: - panic("underflow") - case m == 0: - // n == 0 because m >= n; result is 0 - return z[:0] - case n == 0: - // result is x - return z.set(x) - } - // m > 0 - - z = z.make(m) - c := subVV(z[0:n], x, y) - if m > n { - c = subVW(z[n:], x[n:], c) - } - if c != 0 { - panic("underflow") - } - - return z.norm() -} - -func (x nat) cmp(y nat) (r int) { - m := len(x) - n := len(y) - if m != n || m == 0 { - switch { - case m < n: - r = -1 - case m > n: - r = 1 - } - return - } - - i := m - 1 - for i > 0 && x[i] == y[i] { - i-- - } - - switch { - case x[i] < y[i]: - r = -1 - case x[i] > y[i]: - r = 1 - } - return -} - -func (z nat) mulAddWW(x nat, y, r Word) nat { - m := len(x) - if m == 0 || y == 0 { - return z.setWord(r) // result is r - } - // m > 0 - - z = z.make(m + 1) - z[m] = mulAddVWW(z[0:m], x, y, r) - - return z.norm() -} - -// basicMul multiplies x and y and leaves the result in z. -// The (non-normalized) result is placed in z[0 : len(x) + len(y)]. -func basicMul(z, x, y nat) { - z[0 : len(x)+len(y)].clear() // initialize z - for i, d := range y { - if d != 0 { - z[len(x)+i] = addMulVVW(z[i:i+len(x)], x, d) - } - } -} - -// 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) { - if c := addVV(z[0:n], z, x); c != 0 { - addVW(z[n:n+n>>1], z[n:], c) - } -} - -// Like karatsubaAdd, but does subtract. -func karatsubaSub(z, x nat, n int) { - if c := subVV(z[0:n], z, x); c != 0 { - subVW(z[n:n+n>>1], z[n:], c) - } -} - -// Operands that are shorter than karatsubaThreshold are multiplied using -// "grade school" multiplication; for longer operands the Karatsuba algorithm -// is used. -var karatsubaThreshold int = 40 // computed by calibrate.go - -// karatsuba multiplies x and y and leaves the result in z. -// Both x and y must have the same length n and n must be a -// power of 2. The result vector z must have len(z) >= 6*n. -// The (non-normalized) result is placed in z[0 : 2*n]. -func karatsuba(z, x, y nat) { - n := len(y) - - // Switch to basic multiplication if numbers are odd or small. - // (n is always even if karatsubaThreshold is even, but be - // conservative) - if n&1 != 0 || n < karatsubaThreshold || n < 2 { - basicMul(z, x, y) - return - } - // n&1 == 0 && n >= karatsubaThreshold && n >= 2 - - // Karatsuba multiplication is based on the observation that - // for two numbers x and y with: - // - // x = x1*b + x0 - // y = y1*b + y0 - // - // the product x*y can be obtained with 3 products z2, z1, z0 - // instead of 4: - // - // x*y = x1*y1*b*b + (x1*y0 + x0*y1)*b + x0*y0 - // = z2*b*b + z1*b + z0 - // - // with: - // - // xd = x1 - x0 - // yd = y0 - y1 - // - // z1 = xd*yd + z2 + z0 - // = (x1-x0)*(y0 - y1) + z2 + z0 - // = x1*y0 - x1*y1 - x0*y0 + x0*y1 + z2 + z0 - // = x1*y0 - z2 - z0 + x0*y1 + z2 + z0 - // = x1*y0 + x0*y1 - - // split x, y into "digits" - n2 := n >> 1 // n2 >= 1 - x1, x0 := x[n2:], x[0:n2] // x = x1*b + y0 - y1, y0 := y[n2:], y[0:n2] // y = y1*b + y0 - - // z is used for the result and temporary storage: - // - // 6*n 5*n 4*n 3*n 2*n 1*n 0*n - // z = [z2 copy|z0 copy| xd*yd | yd:xd | x1*y1 | x0*y0 ] - // - // For each recursive call of karatsuba, an unused slice of - // z is passed in that has (at least) half the length of the - // caller's z. - - // compute z0 and z2 with the result "in place" in z - karatsuba(z, x0, y0) // z0 = x0*y0 - karatsuba(z[n:], x1, y1) // z2 = x1*y1 - - // compute xd (or the negative value if underflow occurs) - s := 1 // sign of product xd*yd - xd := z[2*n : 2*n+n2] - if subVV(xd, x1, x0) != 0 { // x1-x0 - s = -s - subVV(xd, x0, x1) // x0-x1 - } - - // compute yd (or the negative value if underflow occurs) - yd := z[2*n+n2 : 3*n] - if subVV(yd, y0, y1) != 0 { // y0-y1 - s = -s - subVV(yd, y1, y0) // y1-y0 - } - - // p = (x1-x0)*(y0-y1) == x1*y0 - x1*y1 - x0*y0 + x0*y1 for s > 0 - // p = (x0-x1)*(y0-y1) == x0*y0 - x0*y1 - x1*y0 + x1*y1 for s < 0 - p := z[n*3:] - karatsuba(p, xd, yd) - - // save original z2:z0 - // (ok to use upper half of z since we're done recursing) - r := z[n*4:] - copy(r, z[:n*2]) - - // add up all partial products - // - // 2*n n 0 - // z = [ z2 | z0 ] - // + [ z0 ] - // + [ z2 ] - // + [ p ] - // - karatsubaAdd(z[n2:], r, n) - karatsubaAdd(z[n2:], r[n:], n) - if s > 0 { - karatsubaAdd(z[n2:], p, n) - } else { - karatsubaSub(z[n2:], p, n) - } -} - -// alias reports whether x and y share the same base array. -func alias(x, y nat) bool { - return cap(x) > 0 && cap(y) > 0 && &x[0:cap(x)][cap(x)-1] == &y[0:cap(y)][cap(y)-1] -} - -// addAt implements z += x<<(_W*i); z must be long enough. -// (we don't use nat.add because we need z to stay the same -// slice, and we don't need to normalize z after each addition) -func addAt(z, x nat, i int) { - if n := len(x); n > 0 { - if c := addVV(z[i:i+n], z[i:], x); c != 0 { - j := i + n - if j < len(z) { - addVW(z[j:], z[j:], c) - } - } - } -} - -func max(x, y int) int { - if x > y { - return x - } - return y -} - -// karatsubaLen computes an approximation to the maximum k <= n such that -// k = p<= 0. Thus, the -// result is the largest number that can be divided repeatedly by 2 before -// becoming about the value of karatsubaThreshold. -func karatsubaLen(n int) int { - i := uint(0) - for n > karatsubaThreshold { - n >>= 1 - i++ - } - return n << i -} - -func (z nat) mul(x, y nat) nat { - m := len(x) - n := len(y) - - switch { - case m < n: - return z.mul(y, x) - case m == 0 || n == 0: - return z[:0] - case n == 1: - return z.mulAddWW(x, y[0], 0) - } - // m >= n > 1 - - // determine if z can be reused - if alias(z, x) || alias(z, y) { - z = nil // z is an alias for x or y - cannot reuse - } - - // use basic multiplication if the numbers are small - if n < karatsubaThreshold { - z = z.make(m + n) - basicMul(z, x, y) - return z.norm() - } - // m >= n && n >= karatsubaThreshold && n >= 2 - - // determine Karatsuba length k such that - // - // x = xh*b + x0 (0 <= x0 < b) - // y = yh*b + y0 (0 <= y0 < b) - // b = 1<<(_W*k) ("base" of digits xi, yi) - // - k := karatsubaLen(n) - // k <= n - - // multiply x0 and y0 via Karatsuba - x0 := x[0:k] // x0 is not normalized - y0 := y[0:k] // y0 is not normalized - z = z.make(max(6*k, m+n)) // enough space for karatsuba of x0*y0 and full result of x*y - karatsuba(z, x0, y0) - z = z[0 : m+n] // z has final length but may be incomplete - z[2*k:].clear() // upper portion of z is garbage (and 2*k <= m+n since k <= n <= m) - - // If xh != 0 or yh != 0, add the missing terms to z. For - // - // xh = xi*b^i + ... + x2*b^2 + x1*b (0 <= xi < b) - // yh = y1*b (0 <= y1 < b) - // - // the missing terms are - // - // x0*y1*b and xi*y0*b^i, xi*y1*b^(i+1) for i > 0 - // - // since all the yi for i > 1 are 0 by choice of k: If any of them - // were > 0, then yh >= b^2 and thus y >= b^2. Then k' = k*2 would - // be a larger valid threshold contradicting the assumption about k. - // - if k < n || m != n { - var t nat - - // add x0*y1*b - x0 := x0.norm() - y1 := y[k:] // y1 is normalized because y is - t = t.mul(x0, y1) // update t so we don't lose t's underlying array - addAt(z, t, k) - - // add xi*y0< k { - xi = xi[:k] - } - xi = xi.norm() - t = t.mul(xi, y0) - addAt(z, t, i) - t = t.mul(xi, y1) - addAt(z, t, i+k) - } - } - - return z.norm() -} - -// mulRange computes the product of all the unsigned integers in the -// range [a, b] inclusively. If a > b (empty range), the result is 1. -func (z nat) mulRange(a, b uint64) nat { - switch { - case a == 0: - // cut long ranges short (optimization) - return z.setUint64(0) - case a > b: - return z.setUint64(1) - case a == b: - return z.setUint64(a) - case a+1 == b: - return z.mul(nat(nil).setUint64(a), nat(nil).setUint64(b)) - } - m := (a + b) / 2 - return z.mul(nat(nil).mulRange(a, m), nat(nil).mulRange(m+1, b)) -} - -// q = (x-r)/y, with 0 <= r < y -func (z nat) divW(x nat, y Word) (q nat, r Word) { - m := len(x) - switch { - case y == 0: - panic("division by zero") - case y == 1: - q = z.set(x) // result is x - return - case m == 0: - q = z[:0] // result is 0 - return - } - // m > 0 - z = z.make(m) - r = divWVW(z, 0, x, y) - q = z.norm() - return -} - -func (z nat) div(z2, u, v nat) (q, r nat) { - if len(v) == 0 { - panic("division by zero") - } - - if u.cmp(v) < 0 { - q = z[:0] - r = z2.set(u) - return - } - - if len(v) == 1 { - var r2 Word - q, r2 = z.divW(u, v[0]) - r = z2.setWord(r2) - return - } - - q, r = z.divLarge(z2, u, v) - return -} - -// q = (uIn-r)/v, with 0 <= r < y -// Uses z as storage for q, and u as storage for r if possible. -// See Knuth, Volume 2, section 4.3.1, Algorithm D. -// Preconditions: -// len(v) >= 2 -// len(uIn) >= len(v) -func (z nat) divLarge(u, uIn, v nat) (q, r nat) { - n := len(v) - m := len(uIn) - n - - // determine if z can be reused - // TODO(gri) should find a better solution - this if statement - // is very costly (see e.g. time pidigits -s -n 10000) - if alias(z, uIn) || alias(z, v) { - z = nil // z is an alias for uIn or v - cannot reuse - } - q = z.make(m + 1) - - qhatv := make(nat, n+1) - if alias(u, uIn) || alias(u, v) { - u = nil // u is an alias for uIn or v - cannot reuse - } - u = u.make(len(uIn) + 1) - u.clear() // TODO(gri) no need to clear if we allocated a new u - - // D1. - shift := leadingZeros(v[n-1]) - if shift > 0 { - // do not modify v, it may be used by another goroutine simultaneously - v1 := make(nat, n) - shlVU(v1, v, shift) - v = v1 - } - u[len(uIn)] = shlVU(u[0:len(uIn)], uIn, shift) - - // D2. - for j := m; j >= 0; j-- { - // D3. - qhat := Word(_M) - if u[j+n] != v[n-1] { - var rhat Word - qhat, rhat = divWW(u[j+n], u[j+n-1], v[n-1]) - - // x1 | x2 = q̂v_{n-2} - x1, x2 := mulWW(qhat, v[n-2]) - // test if q̂v_{n-2} > br̂ + u_{j+n-2} - for greaterThan(x1, x2, rhat, u[j+n-2]) { - qhat-- - prevRhat := rhat - rhat += v[n-1] - // v[n-1] >= 0, so this tests for overflow. - if rhat < prevRhat { - break - } - x1, x2 = mulWW(qhat, v[n-2]) - } - } - - // D4. - qhatv[n] = mulAddVWW(qhatv[0:n], v, qhat, 0) - - c := subVV(u[j:j+len(qhatv)], u[j:], qhatv) - if c != 0 { - c := addVV(u[j:j+n], u[j:], v) - u[j+n] += c - qhat-- - } - - q[j] = qhat - } - - q = q.norm() - shrVU(u, u, shift) - r = u.norm() - - return q, r -} - -// Length of x in bits. x must be normalized. -func (x nat) bitLen() int { - if i := len(x) - 1; i >= 0 { - return i*_W + bitLen(x[i]) - } - return 0 -} - -const deBruijn32 = 0x077CB531 - -var deBruijn32Lookup = []byte{ - 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, - 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9, -} - -const deBruijn64 = 0x03f79d71b4ca8b09 - -var deBruijn64Lookup = []byte{ - 0, 1, 56, 2, 57, 49, 28, 3, 61, 58, 42, 50, 38, 29, 17, 4, - 62, 47, 59, 36, 45, 43, 51, 22, 53, 39, 33, 30, 24, 18, 12, 5, - 63, 55, 48, 27, 60, 41, 37, 16, 46, 35, 44, 21, 52, 32, 23, 11, - 54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6, -} - -// trailingZeroBits returns the number of consecutive least significant zero -// bits of x. -func trailingZeroBits(x Word) uint { - // x & -x leaves only the right-most bit set in the word. Let k be the - // index of that bit. Since only a single bit is set, the value is two - // to the power of k. Multiplying by a power of two is equivalent to - // left shifting, in this case by k bits. The de Bruijn constant is - // such that all six bit, consecutive substrings are distinct. - // Therefore, if we have a left shifted version of this constant we can - // find by how many bits it was shifted by looking at which six bit - // substring ended up at the top of the word. - // (Knuth, volume 4, section 7.3.1) - switch _W { - case 32: - return uint(deBruijn32Lookup[((x&-x)*deBruijn32)>>27]) - case 64: - return uint(deBruijn64Lookup[((x&-x)*(deBruijn64&_M))>>58]) - default: - panic("unknown word size") - } -} - -// trailingZeroBits returns the number of consecutive least significant zero -// bits of x. -func (x nat) trailingZeroBits() uint { - if len(x) == 0 { - return 0 - } - var i uint - for x[i] == 0 { - i++ - } - // x[i] != 0 - return i*_W + trailingZeroBits(x[i]) -} - -// z = x << s -func (z nat) shl(x nat, s uint) nat { - m := len(x) - if m == 0 { - return z[:0] - } - // m > 0 - - n := m + int(s/_W) - z = z.make(n + 1) - z[n] = shlVU(z[n-m:n], x, s%_W) - z[0 : n-m].clear() - - return z.norm() -} - -// z = x >> s -func (z nat) shr(x nat, s uint) nat { - m := len(x) - n := m - int(s/_W) - if n <= 0 { - return z[:0] - } - // n > 0 - - z = z.make(n) - shrVU(z, x[m-n:], s%_W) - - return z.norm() -} - -func (z nat) setBit(x nat, i uint, b uint) nat { - j := int(i / _W) - m := Word(1) << (i % _W) - n := len(x) - switch b { - case 0: - z = z.make(n) - copy(z, x) - if j >= n { - // no need to grow - return z - } - z[j] &^= m - return z.norm() - case 1: - if j >= n { - z = z.make(j + 1) - z[n:].clear() - } else { - z = z.make(n) - } - copy(z, x) - z[j] |= m - // no need to normalize - return z - } - panic("set bit is not 0 or 1") -} - -// bit returns the value of the i'th bit, with lsb == bit 0. -func (x nat) bit(i uint) uint { - j := i / _W - if j >= uint(len(x)) { - return 0 - } - // 0 <= j < len(x) - return uint(x[j] >> (i % _W) & 1) -} - -// sticky returns 1 if there's a 1 bit within the -// i least significant bits, otherwise it returns 0. -func (x nat) sticky(i uint) uint { - j := i / _W - if j >= uint(len(x)) { - if len(x) == 0 { - return 0 - } - return 1 - } - // 0 <= j < len(x) - for _, x := range x[:j] { - if x != 0 { - return 1 - } - } - if x[j]<<(_W-i%_W) != 0 { - return 1 - } - return 0 -} - -func (z nat) and(x, y nat) nat { - m := len(x) - n := len(y) - if m > n { - m = n - } - // m <= n - - z = z.make(m) - for i := 0; i < m; i++ { - z[i] = x[i] & y[i] - } - - return z.norm() -} - -func (z nat) andNot(x, y nat) nat { - m := len(x) - n := len(y) - if n > m { - n = m - } - // m >= n - - z = z.make(m) - for i := 0; i < n; i++ { - z[i] = x[i] &^ y[i] - } - copy(z[n:m], x[n:m]) - - return z.norm() -} - -func (z nat) or(x, y nat) nat { - m := len(x) - n := len(y) - s := x - if m < n { - n, m = m, n - s = y - } - // m >= n - - z = z.make(m) - for i := 0; i < n; i++ { - z[i] = x[i] | y[i] - } - copy(z[n:m], s[n:m]) - - return z.norm() -} - -func (z nat) xor(x, y nat) nat { - m := len(x) - n := len(y) - s := x - if m < n { - n, m = m, n - s = y - } - // m >= n - - z = z.make(m) - for i := 0; i < n; i++ { - z[i] = x[i] ^ y[i] - } - copy(z[n:m], s[n:m]) - - return z.norm() -} - -// greaterThan reports whether (x1<<_W + x2) > (y1<<_W + y2) -func greaterThan(x1, x2, y1, y2 Word) bool { - return x1 > y1 || x1 == y1 && x2 > y2 -} - -// modW returns x % d. -func (x nat) modW(d Word) (r Word) { - // TODO(agl): we don't actually need to store the q value. - var q nat - q = q.make(len(x)) - return divWVW(q, 0, x, d) -} - -// random creates a random integer in [0..limit), using the space in z if -// possible. n is the bit length of limit. -func (z nat) random(rand *rand.Rand, limit nat, n int) nat { - if alias(z, limit) { - z = nil // z is an alias for limit - cannot reuse - } - z = z.make(len(limit)) - - bitLengthOfMSW := uint(n % _W) - if bitLengthOfMSW == 0 { - bitLengthOfMSW = _W - } - mask := Word((1 << bitLengthOfMSW) - 1) - - for { - switch _W { - case 32: - for i := range z { - z[i] = Word(rand.Uint32()) - } - case 64: - for i := range z { - z[i] = Word(rand.Uint32()) | Word(rand.Uint32())<<32 - } - default: - panic("unknown word size") - } - z[len(limit)-1] &= mask - if z.cmp(limit) < 0 { - break - } - } - - return z.norm() -} - -// If m != 0 (i.e., len(m) != 0), expNN sets z to x**y mod m; -// otherwise it sets z to x**y. The result is the value of z. -func (z nat) expNN(x, y, m nat) nat { - if alias(z, x) || alias(z, y) { - // We cannot allow in-place modification of x or y. - z = nil - } - - // x**y mod 1 == 0 - if len(m) == 1 && m[0] == 1 { - return z.setWord(0) - } - // m == 0 || m > 1 - - // x**0 == 1 - if len(y) == 0 { - return z.setWord(1) - } - // y > 0 - - if len(m) != 0 { - // We likely end up being as long as the modulus. - z = z.make(len(m)) - } - z = z.set(x) - - // If the base is non-trivial and the exponent is large, we use - // 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. - if len(x) > 1 && len(y) > 1 && len(m) > 0 { - return z.expNNWindowed(x, y, m) - } - - v := y[len(y)-1] // v > 0 because y is normalized and y > 0 - shift := leadingZeros(v) + 1 - v <<= shift - var q nat - - const mask = 1 << (_W - 1) - - // We walk through the bits of the exponent one by one. Each time we - // see a bit, we square, thus doubling the power. If the bit is a one, - // we also multiply by x, thus adding one to the power. - - w := _W - int(shift) - // zz and r are used to avoid allocating in mul and div as - // otherwise the arguments would alias. - var zz, r nat - for j := 0; j < w; j++ { - zz = zz.mul(z, z) - zz, z = z, zz - - if v&mask != 0 { - zz = zz.mul(z, x) - zz, z = z, zz - } - - if len(m) != 0 { - zz, r = zz.div(r, z, m) - zz, r, q, z = q, z, zz, r - } - - v <<= 1 - } - - for i := len(y) - 2; i >= 0; i-- { - v = y[i] - - for j := 0; j < _W; j++ { - zz = zz.mul(z, z) - zz, z = z, zz - - if v&mask != 0 { - zz = zz.mul(z, x) - zz, z = z, zz - } - - if len(m) != 0 { - zz, r = zz.div(r, z, m) - zz, r, q, z = q, z, zz, r - } - - v <<= 1 - } - } - - return z.norm() -} - -// expNNWindowed calculates x**y mod m using a fixed, 4-bit window. -func (z nat) expNNWindowed(x, y, m nat) nat { - // zz and r are used to avoid allocating in mul and div as otherwise - // the arguments would alias. - var zz, r nat - - const n = 4 - // powers[i] contains x^i. - var powers [1 << n]nat - powers[0] = natOne - powers[1] = x - for i := 2; i < 1<= 0; i-- { - yi := y[i] - for j := 0; j < _W; j += n { - if i != len(y)-1 || j != 0 { - // Unrolled loop for significant performance - // gain. Use go test -bench=".*" in crypto/rsa - // to check performance before making changes. - zz = zz.mul(z, z) - zz, z = z, zz - zz, r = zz.div(r, z, m) - z, r = r, z - - zz = zz.mul(z, z) - zz, z = z, zz - zz, r = zz.div(r, z, m) - z, r = r, z - - zz = zz.mul(z, z) - zz, z = z, zz - zz, r = zz.div(r, z, m) - z, r = r, z - - zz = zz.mul(z, z) - zz, z = z, zz - zz, r = zz.div(r, z, m) - z, r = r, z - } - - zz = zz.mul(z, powers[yi>>(_W-n)]) - zz, z = z, zz - zz, r = zz.div(r, z, m) - z, r = r, z - - yi <<= n - } - } - - return z.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. -func (n nat) probablyPrime(reps int) bool { - if len(n) == 0 { - return false - } - - if len(n) == 1 { - if n[0] < 2 { - return false - } - - if n[0]%2 == 0 { - return n[0] == 2 - } - - // We have to exclude these cases because we reject all - // multiples of these numbers below. - switch n[0] { - case 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53: - return true - } - } - - if n[0]&1 == 0 { - return false // n is even - } - - const primesProduct32 = 0xC0CFD797 // Π {p ∈ primes, 2 < p <= 29} - const primesProduct64 = 0xE221F97C30E94E1D // Π {p ∈ primes, 2 < p <= 53} - - var r Word - switch _W { - case 32: - r = n.modW(primesProduct32) - case 64: - r = n.modW(primesProduct64 & _M) - default: - panic("Unknown word size") - } - - if r%3 == 0 || r%5 == 0 || r%7 == 0 || r%11 == 0 || - r%13 == 0 || r%17 == 0 || r%19 == 0 || r%23 == 0 || r%29 == 0 { - return false - } - - if _W == 64 && (r%31 == 0 || r%37 == 0 || r%41 == 0 || - r%43 == 0 || r%47 == 0 || r%53 == 0) { - return false - } - - nm1 := nat(nil).sub(n, natOne) - // determine q, k such that nm1 = q << k - k := nm1.trailingZeroBits() - q := nat(nil).shr(nm1, k) - - nm3 := nat(nil).sub(nm1, natTwo) - rand := rand.New(rand.NewSource(int64(n[0]))) - - var x, y, quotient nat - nm3Len := nm3.bitLen() - -NextRandom: - for i := 0; i < reps; i++ { - x = x.random(rand, nm3, nm3Len) - x = x.add(x, natTwo) - y = y.expNN(x, q, n) - if y.cmp(natOne) == 0 || y.cmp(nm1) == 0 { - continue - } - for j := uint(1); j < k; j++ { - y = y.mul(y, y) - quotient, y = quotient.div(y, y, n) - if y.cmp(nm1) == 0 { - continue NextRandom - } - if y.cmp(natOne) == 0 { - return false - } - } - return false - } - - return true -} - -// bytes writes the value of z into buf using big-endian encoding. -// len(buf) must be >= len(z)*_S. The value of z is encoded in the -// slice buf[i:]. The number i of unused bytes at the beginning of -// buf is returned as result. -func (z nat) bytes(buf []byte) (i int) { - i = len(buf) - for _, d := range z { - for j := 0; j < _S; j++ { - i-- - buf[i] = byte(d) - d >>= 8 - } - } - - for i < len(buf) && buf[i] == 0 { - i++ - } - - return -} - -// setBytes interprets buf as the bytes of a big-endian unsigned -// integer, sets z to that value, and returns z. -func (z nat) setBytes(buf []byte) nat { - z = z.make((len(buf) + _S - 1) / _S) - - k := 0 - s := uint(0) - var d Word - for i := len(buf); i > 0; i-- { - d |= Word(buf[i-1]) << s - if s += 8; s == _S*8 { - z[k] = d - k++ - s = 0 - d = 0 - } - } - if k < len(z) { - z[k] = d - } - - return z.norm() -} diff --git a/src/cmd/internal/gc/big/nat_test.go b/src/cmd/internal/gc/big/nat_test.go deleted file mode 100644 index b25a89f731..0000000000 --- a/src/cmd/internal/gc/big/nat_test.go +++ /dev/null @@ -1,518 +0,0 @@ -// Copyright 2009 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 big - -import ( - "runtime" - "strings" - "testing" -) - -var cmpTests = []struct { - x, y nat - r int -}{ - {nil, nil, 0}, - {nil, nat(nil), 0}, - {nat(nil), nil, 0}, - {nat(nil), nat(nil), 0}, - {nat{0}, nat{0}, 0}, - {nat{0}, nat{1}, -1}, - {nat{1}, nat{0}, 1}, - {nat{1}, nat{1}, 0}, - {nat{0, _M}, nat{1}, 1}, - {nat{1}, nat{0, _M}, -1}, - {nat{1, _M}, nat{0, _M}, 1}, - {nat{0, _M}, nat{1, _M}, -1}, - {nat{16, 571956, 8794, 68}, nat{837, 9146, 1, 754489}, -1}, - {nat{34986, 41, 105, 1957}, nat{56, 7458, 104, 1957}, 1}, -} - -func TestCmp(t *testing.T) { - for i, a := range cmpTests { - r := a.x.cmp(a.y) - if r != a.r { - t.Errorf("#%d got r = %v; want %v", i, r, a.r) - } - } -} - -type funNN func(z, x, y nat) nat -type argNN struct { - z, x, y nat -} - -var sumNN = []argNN{ - {}, - {nat{1}, nil, nat{1}}, - {nat{1111111110}, nat{123456789}, nat{987654321}}, - {nat{0, 0, 0, 1}, nil, nat{0, 0, 0, 1}}, - {nat{0, 0, 0, 1111111110}, nat{0, 0, 0, 123456789}, nat{0, 0, 0, 987654321}}, - {nat{0, 0, 0, 1}, nat{0, 0, _M}, nat{0, 0, 1}}, -} - -var prodNN = []argNN{ - {}, - {nil, nil, nil}, - {nil, nat{991}, nil}, - {nat{991}, nat{991}, nat{1}}, - {nat{991 * 991}, nat{991}, nat{991}}, - {nat{0, 0, 991 * 991}, nat{0, 991}, nat{0, 991}}, - {nat{1 * 991, 2 * 991, 3 * 991, 4 * 991}, nat{1, 2, 3, 4}, nat{991}}, - {nat{4, 11, 20, 30, 20, 11, 4}, nat{1, 2, 3, 4}, nat{4, 3, 2, 1}}, - // 3^100 * 3^28 = 3^128 - { - natFromString("11790184577738583171520872861412518665678211592275841109096961"), - natFromString("515377520732011331036461129765621272702107522001"), - natFromString("22876792454961"), - }, - // z = 111....1 (70000 digits) - // x = 10^(99*700) + ... + 10^1400 + 10^700 + 1 - // y = 111....1 (700 digits, larger than Karatsuba threshold on 32-bit and 64-bit) - { - natFromString(strings.Repeat("1", 70000)), - natFromString("1" + strings.Repeat(strings.Repeat("0", 699)+"1", 99)), - natFromString(strings.Repeat("1", 700)), - }, - // z = 111....1 (20000 digits) - // x = 10^10000 + 1 - // y = 111....1 (10000 digits) - { - natFromString(strings.Repeat("1", 20000)), - natFromString("1" + strings.Repeat("0", 9999) + "1"), - natFromString(strings.Repeat("1", 10000)), - }, -} - -func natFromString(s string) nat { - x, _, _, err := nat(nil).scan(strings.NewReader(s), 0, false) - if err != nil { - panic(err) - } - return x -} - -func TestSet(t *testing.T) { - for _, a := range sumNN { - z := nat(nil).set(a.z) - if z.cmp(a.z) != 0 { - t.Errorf("got z = %v; want %v", z, a.z) - } - } -} - -func testFunNN(t *testing.T, msg string, f funNN, a argNN) { - z := f(nil, a.x, a.y) - if z.cmp(a.z) != 0 { - t.Errorf("%s%+v\n\tgot z = %v; want %v", msg, a, z, a.z) - } -} - -func TestFunNN(t *testing.T) { - for _, a := range sumNN { - arg := a - testFunNN(t, "add", nat.add, arg) - - arg = argNN{a.z, a.y, a.x} - testFunNN(t, "add symmetric", nat.add, arg) - - arg = argNN{a.x, a.z, a.y} - testFunNN(t, "sub", nat.sub, arg) - - arg = argNN{a.y, a.z, a.x} - testFunNN(t, "sub symmetric", nat.sub, arg) - } - - for _, a := range prodNN { - arg := a - testFunNN(t, "mul", nat.mul, arg) - - arg = argNN{a.z, a.y, a.x} - testFunNN(t, "mul symmetric", nat.mul, arg) - } -} - -var mulRangesN = []struct { - a, b uint64 - prod string -}{ - {0, 0, "0"}, - {1, 1, "1"}, - {1, 2, "2"}, - {1, 3, "6"}, - {10, 10, "10"}, - {0, 100, "0"}, - {0, 1e9, "0"}, - {1, 0, "1"}, // empty range - {100, 1, "1"}, // empty range - {1, 10, "3628800"}, // 10! - {1, 20, "2432902008176640000"}, // 20! - {1, 100, - "933262154439441526816992388562667004907159682643816214685929" + - "638952175999932299156089414639761565182862536979208272237582" + - "51185210916864000000000000000000000000", // 100! - }, -} - -func TestMulRangeN(t *testing.T) { - for i, r := range mulRangesN { - prod := nat(nil).mulRange(r.a, r.b).decimalString() - if prod != r.prod { - t.Errorf("#%d: got %s; want %s", i, prod, r.prod) - } - } -} - -// allocBytes returns the number of bytes allocated by invoking f. -func allocBytes(f func()) uint64 { - var stats runtime.MemStats - runtime.ReadMemStats(&stats) - t := stats.TotalAlloc - f() - runtime.ReadMemStats(&stats) - return stats.TotalAlloc - t -} - -// TestMulUnbalanced tests that multiplying numbers of different lengths -// does not cause deep recursion and in turn allocate too much memory. -// Test case for issue 3807. -func TestMulUnbalanced(t *testing.T) { - defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) - x := rndNat(50000) - y := rndNat(40) - allocSize := allocBytes(func() { - nat(nil).mul(x, y) - }) - inputSize := uint64(len(x)+len(y)) * _S - if ratio := allocSize / uint64(inputSize); ratio > 10 { - t.Errorf("multiplication uses too much memory (%d > %d times the size of inputs)", allocSize, ratio) - } -} - -func rndNat(n int) nat { - return nat(rndV(n)).norm() -} - -func BenchmarkMul(b *testing.B) { - mulx := rndNat(1e4) - muly := rndNat(1e4) - b.ResetTimer() - for i := 0; i < b.N; i++ { - var z nat - z.mul(mulx, muly) - } -} - -func TestLeadingZeros(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) - } - x >>= 1 - } -} - -type shiftTest struct { - in nat - shift uint - out nat -} - -var leftShiftTests = []shiftTest{ - {nil, 0, nil}, - {nil, 1, nil}, - {natOne, 0, natOne}, - {natOne, 1, natTwo}, - {nat{1 << (_W - 1)}, 1, nat{0}}, - {nat{1 << (_W - 1), 0}, 1, nat{0, 1}}, -} - -func TestShiftLeft(t *testing.T) { - for i, test := range leftShiftTests { - var z nat - z = z.shl(test.in, test.shift) - for j, d := range test.out { - if j >= len(z) || z[j] != d { - t.Errorf("#%d: got: %v want: %v", i, z, test.out) - break - } - } - } -} - -var rightShiftTests = []shiftTest{ - {nil, 0, nil}, - {nil, 1, nil}, - {natOne, 0, natOne}, - {natOne, 1, nil}, - {natTwo, 1, natOne}, - {nat{0, 1}, 1, nat{1 << (_W - 1)}}, - {nat{2, 1, 1}, 1, nat{1<<(_W-1) + 1, 1 << (_W - 1)}}, -} - -func TestShiftRight(t *testing.T) { - for i, test := range rightShiftTests { - var z nat - z = z.shr(test.in, test.shift) - for j, d := range test.out { - if j >= len(z) || z[j] != d { - t.Errorf("#%d: got: %v want: %v", i, z, test.out) - break - } - } - } -} - -type modWTest struct { - in string - dividend string - out string -} - -var modWTests32 = []modWTest{ - {"23492635982634928349238759823742", "252341", "220170"}, -} - -var modWTests64 = []modWTest{ - {"6527895462947293856291561095690465243862946", "524326975699234", "375066989628668"}, -} - -func runModWTests(t *testing.T, tests []modWTest) { - for i, test := range tests { - in, _ := new(Int).SetString(test.in, 10) - d, _ := new(Int).SetString(test.dividend, 10) - out, _ := new(Int).SetString(test.out, 10) - - r := in.abs.modW(d.abs[0]) - if r != out.abs[0] { - t.Errorf("#%d failed: got %d want %s", i, r, out) - } - } -} - -func TestModW(t *testing.T) { - if _W >= 32 { - runModWTests(t, modWTests32) - } - if _W >= 64 { - runModWTests(t, modWTests64) - } -} - -func TestTrailingZeroBits(t *testing.T) { - // test 0 case explicitly - if n := trailingZeroBits(0); n != 0 { - t.Errorf("got trailingZeroBits(0) = %d; want 0", n) - } - - x := Word(1) - for i := uint(0); i < _W; i++ { - n := trailingZeroBits(x) - if n != i { - t.Errorf("got trailingZeroBits(%#x) = %d; want %d", x, n, i%_W) - } - x <<= 1 - } - - // test 0 case explicitly - if n := nat(nil).trailingZeroBits(); n != 0 { - t.Errorf("got nat(nil).trailingZeroBits() = %d; want 0", n) - } - - y := nat(nil).set(natOne) - for i := uint(0); i <= 3*_W; i++ { - n := y.trailingZeroBits() - if n != i { - t.Errorf("got 0x%s.trailingZeroBits() = %d; want %d", y.hexString(), n, i) - } - y = y.shl(y, 1) - } -} - -var expNNTests = []struct { - x, y, m string - out string -}{ - {"0", "0", "0", "1"}, - {"0", "0", "1", "0"}, - {"1", "1", "1", "0"}, - {"2", "1", "1", "0"}, - {"2", "2", "1", "0"}, - {"10", "100000000000", "1", "0"}, - {"0x8000000000000000", "2", "", "0x40000000000000000000000000000000"}, - {"0x8000000000000000", "2", "6719", "4944"}, - {"0x8000000000000000", "3", "6719", "5447"}, - {"0x8000000000000000", "1000", "6719", "1603"}, - {"0x8000000000000000", "1000000", "6719", "3199"}, - { - "2938462938472983472983659726349017249287491026512746239764525612965293865296239471239874193284792387498274256129746192347", - "298472983472983471903246121093472394872319615612417471234712061", - "29834729834729834729347290846729561262544958723956495615629569234729836259263598127342374289365912465901365498236492183464", - "23537740700184054162508175125554701713153216681790245129157191391322321508055833908509185839069455749219131480588829346291", - }, -} - -func TestExpNN(t *testing.T) { - for i, test := range expNNTests { - x := natFromString(test.x) - y := natFromString(test.y) - out := natFromString(test.out) - - var m nat - if len(test.m) > 0 { - m = natFromString(test.m) - } - - z := nat(nil).expNN(x, y, m) - if z.cmp(out) != 0 { - t.Errorf("#%d got %s want %s", i, z.decimalString(), out.decimalString()) - } - } -} - -func ExpHelper(b *testing.B, x, y Word) { - var z nat - for i := 0; i < b.N; i++ { - z.expWW(x, y) - } -} - -func BenchmarkExp3Power0x10(b *testing.B) { ExpHelper(b, 3, 0x10) } -func BenchmarkExp3Power0x40(b *testing.B) { ExpHelper(b, 3, 0x40) } -func BenchmarkExp3Power0x100(b *testing.B) { ExpHelper(b, 3, 0x100) } -func BenchmarkExp3Power0x400(b *testing.B) { ExpHelper(b, 3, 0x400) } -func BenchmarkExp3Power0x1000(b *testing.B) { ExpHelper(b, 3, 0x1000) } -func BenchmarkExp3Power0x4000(b *testing.B) { ExpHelper(b, 3, 0x4000) } -func BenchmarkExp3Power0x10000(b *testing.B) { ExpHelper(b, 3, 0x10000) } -func BenchmarkExp3Power0x40000(b *testing.B) { ExpHelper(b, 3, 0x40000) } -func BenchmarkExp3Power0x100000(b *testing.B) { ExpHelper(b, 3, 0x100000) } -func BenchmarkExp3Power0x400000(b *testing.B) { ExpHelper(b, 3, 0x400000) } - -func fibo(n int) nat { - switch n { - case 0: - return nil - case 1: - return nat{1} - } - f0 := fibo(0) - f1 := fibo(1) - var f2 nat - for i := 1; i < n; i++ { - f2 = f2.add(f0, f1) - f0, f1, f2 = f1, f2, f0 - } - return f1 -} - -var fiboNums = []string{ - "0", - "55", - "6765", - "832040", - "102334155", - "12586269025", - "1548008755920", - "190392490709135", - "23416728348467685", - "2880067194370816120", - "354224848179261915075", -} - -func TestFibo(t *testing.T) { - for i, want := range fiboNums { - n := i * 10 - got := fibo(n).decimalString() - if got != want { - t.Errorf("fibo(%d) failed: got %s want %s", n, got, want) - } - } -} - -func BenchmarkFibo(b *testing.B) { - for i := 0; i < b.N; i++ { - fibo(1e0) - fibo(1e1) - fibo(1e2) - fibo(1e3) - fibo(1e4) - fibo(1e5) - } -} - -var bitTests = []struct { - x string - i uint - want uint -}{ - {"0", 0, 0}, - {"0", 1, 0}, - {"0", 1000, 0}, - - {"0x1", 0, 1}, - {"0x10", 0, 0}, - {"0x10", 3, 0}, - {"0x10", 4, 1}, - {"0x10", 5, 0}, - - {"0x8000000000000000", 62, 0}, - {"0x8000000000000000", 63, 1}, - {"0x8000000000000000", 64, 0}, - - {"0x3" + strings.Repeat("0", 32), 127, 0}, - {"0x3" + strings.Repeat("0", 32), 128, 1}, - {"0x3" + strings.Repeat("0", 32), 129, 1}, - {"0x3" + strings.Repeat("0", 32), 130, 0}, -} - -func TestBit(t *testing.T) { - for i, test := range bitTests { - x := natFromString(test.x) - if got := x.bit(test.i); got != test.want { - t.Errorf("#%d: %s.bit(%d) = %v; want %v", i, test.x, test.i, got, test.want) - } - } -} - -var stickyTests = []struct { - x string - i uint - want uint -}{ - {"0", 0, 0}, - {"0", 1, 0}, - {"0", 1000, 0}, - - {"0x1", 0, 0}, - {"0x1", 1, 1}, - - {"0x1350", 0, 0}, - {"0x1350", 4, 0}, - {"0x1350", 5, 1}, - - {"0x8000000000000000", 63, 0}, - {"0x8000000000000000", 64, 1}, - - {"0x1" + strings.Repeat("0", 100), 400, 0}, - {"0x1" + strings.Repeat("0", 100), 401, 1}, -} - -func TestSticky(t *testing.T) { - for i, test := range stickyTests { - x := natFromString(test.x) - if got := x.sticky(test.i); got != test.want { - t.Errorf("#%d: %s.sticky(%d) = %v; want %v", i, test.x, test.i, got, test.want) - } - if test.want == 1 { - // all subsequent i's should also return 1 - for d := uint(1); d <= 3; d++ { - if got := x.sticky(test.i + d); got != 1 { - t.Errorf("#%d: %s.sticky(%d) = %v; want %v", i, test.x, test.i+d, got, 1) - } - } - } - } -} diff --git a/src/cmd/internal/gc/big/natconv.go b/src/cmd/internal/gc/big/natconv.go deleted file mode 100644 index 022dcfe38c..0000000000 --- a/src/cmd/internal/gc/big/natconv.go +++ /dev/null @@ -1,495 +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. - -// This file implements nat-to-string conversion functions. - -package big - -import ( - "errors" - "fmt" - "io" - "math" - "sync" -) - -// MaxBase is the largest number base accepted for string conversions. -const MaxBase = 'z' - 'a' + 10 + 1 - -// maxPow returns (b**n, n) such that b**n is the largest power b**n <= _M. -// For instance maxPow(10) == (1e19, 19) for 19 decimal digits in a 64bit Word. -// In other words, at most n digits in base b fit into a Word. -// TODO(gri) replace this with a table, generated at build time. -func maxPow(b Word) (p Word, n int) { - p, n = b, 1 // assuming b <= _M - for max := _M / b; p <= max; { - // p == b**n && p <= max - p *= b - n++ - } - // p == b**n && p <= _M - return -} - -// pow returns x**n for n > 0, and 1 otherwise. -func pow(x Word, n int) (p Word) { - // n == sum of bi * 2**i, for 0 <= i < imax, and bi is 0 or 1 - // thus x**n == product of x**(2**i) for all i where bi == 1 - // (Russian Peasant Method for exponentiation) - p = 1 - for n > 0 { - if n&1 != 0 { - p *= x - } - x *= x - n >>= 1 - } - return -} - -// scan scans the number corresponding to the longest possible prefix -// from r representing an unsigned number in a given conversion base. -// It returns the corresponding natural number res, the actual base b, -// a digit count, and a read or syntax error err, if any. -// -// number = [ prefix ] mantissa . -// prefix = "0" [ "x" | "X" | "b" | "B" ] . -// mantissa = digits | digits "." [ digits ] | "." digits . -// digits = digit { digit } . -// digit = "0" ... "9" | "a" ... "z" | "A" ... "Z" . -// -// Unless fracOk is set, the base argument must be 0 or a value between -// 2 and MaxBase. If fracOk is set, the base argument must be one of -// 0, 2, 10, or 16. Providing an invalid base argument leads to a run- -// time panic. -// -// For base 0, the number prefix determines the actual base: A prefix of -// ``0x'' or ``0X'' selects base 16; if fracOk is not set, the ``0'' prefix -// selects base 8, and a ``0b'' or ``0B'' prefix selects base 2. Otherwise -// the selected base is 10 and no prefix is accepted. -// -// If fracOk is set, an octal prefix is ignored (a leading ``0'' simply -// stands for a zero digit), and a period followed by a fractional part -// is permitted. The result value is computed as if there were no period -// present; and the count value is used to determine the fractional part. -// -// A result digit count > 0 corresponds to the number of (non-prefix) digits -// parsed. A digit count <= 0 indicates the presence of a period (if fracOk -// is set, only), and -count is the number of fractional digits found. -// In this case, the actual value of the scanned number is res * b**count. -// -func (z nat) scan(r io.ByteScanner, base int, fracOk bool) (res nat, b, count int, err error) { - // reject illegal bases - baseOk := base == 0 || - !fracOk && 2 <= base && base <= MaxBase || - fracOk && (base == 2 || base == 10 || base == 16) - if !baseOk { - panic(fmt.Sprintf("illegal number base %d", base)) - } - - // one char look-ahead - ch, err := r.ReadByte() - if err != nil { - return - } - - // determine actual base - b = base - if base == 0 { - // actual base is 10 unless there's a base prefix - b = 10 - if ch == '0' { - count = 1 - switch ch, err = r.ReadByte(); err { - case nil: - // possibly one of 0x, 0X, 0b, 0B - if !fracOk { - b = 8 - } - switch ch { - case 'x', 'X': - b = 16 - case 'b', 'B': - b = 2 - } - switch b { - case 16, 2: - count = 0 // prefix is not counted - if ch, err = r.ReadByte(); err != nil { - // io.EOF is also an error in this case - return - } - case 8: - count = 0 // prefix is not counted - } - case io.EOF: - // input is "0" - res = z[:0] - err = nil - return - default: - // read error - return - } - } - } - - // convert string - // Algorithm: Collect digits in groups of at most n digits in di - // and then use mulAddWW for every such group to add them to the - // result. - z = z[:0] - b1 := Word(b) - bn, n := maxPow(b1) // at most n digits in base b1 fit into Word - di := Word(0) // 0 <= di < b1**i < bn - i := 0 // 0 <= i < n - dp := -1 // position of decimal point - for { - if fracOk && ch == '.' { - fracOk = false - dp = count - // advance - if ch, err = r.ReadByte(); err != nil { - if err == io.EOF { - err = nil - break - } - return - } - } - - // convert rune into digit value d1 - var d1 Word - switch { - case '0' <= ch && ch <= '9': - d1 = Word(ch - '0') - case 'a' <= ch && ch <= 'z': - d1 = Word(ch - 'a' + 10) - case 'A' <= ch && ch <= 'Z': - d1 = Word(ch - 'A' + 10) - default: - d1 = MaxBase + 1 - } - if d1 >= b1 { - r.UnreadByte() // ch does not belong to number anymore - break - } - count++ - - // collect d1 in di - di = di*b1 + d1 - i++ - - // if di is "full", add it to the result - if i == n { - z = z.mulAddWW(z, bn, di) - di = 0 - i = 0 - } - - // advance - if ch, err = r.ReadByte(); err != nil { - if err == io.EOF { - err = nil - break - } - return - } - } - - if count == 0 { - // no digits found - switch { - case base == 0 && b == 8: - // there was only the octal prefix 0 (possibly followed by digits > 7); - // count as one digit and return base 10, not 8 - count = 1 - b = 10 - case base != 0 || b != 8: - // there was neither a mantissa digit nor the octal prefix 0 - err = errors.New("syntax error scanning number") - } - return - } - // count > 0 - - // add remaining digits to result - if i > 0 { - z = z.mulAddWW(z, pow(b1, i), di) - } - res = z.norm() - - // adjust for fraction, if any - if dp >= 0 { - // 0 <= dp <= count > 0 - count = dp - count - } - - return -} - -// Character sets for string conversion. -const ( - lowercaseDigits = "0123456789abcdefghijklmnopqrstuvwxyz" - uppercaseDigits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" -) - -// decimalString returns a decimal representation of x. -// It calls x.string with the charset "0123456789". -func (x nat) decimalString() string { - return x.string(lowercaseDigits[:10]) -} - -// hexString returns a hexadecimal representation of x. -// It calls x.string with the charset "0123456789abcdef". -func (x nat) hexString() string { - return x.string(lowercaseDigits[:16]) -} - -// string converts x to a string using digits from a charset; a digit with -// value d is represented by charset[d]. The conversion base is determined -// by len(charset), which must be >= 2 and <= 256. -func (x nat) string(charset string) string { - b := Word(len(charset)) - - // special cases - switch { - case b < 2 || b > 256: - panic("invalid character set length") - case len(x) == 0: - return string(charset[0]) - } - - // allocate buffer for conversion - i := int(float64(x.bitLen())/math.Log2(float64(b))) + 1 // off by one at most - s := make([]byte, i) - - // convert power of two and non power of two bases separately - if b == b&-b { - // shift is base-b digit size in bits - shift := trailingZeroBits(b) // shift > 0 because b >= 2 - mask := Word(1)<= shift { - i-- - s[i] = charset[w&mask] - w >>= shift - nbits -= shift - } - - // convert any partial leading digit and advance to next word - if nbits == 0 { - // no partial digit remaining, just advance - w = x[k] - nbits = _W - } else { - // partial digit in current (k-1) and next (k) word - w |= x[k] << nbits - i-- - s[i] = charset[w&mask] - - // advance - w = x[k] >> (shift - nbits) - nbits = _W - (shift - nbits) - } - } - - // convert digits of most-significant word (omit leading zeros) - for nbits >= 0 && w != 0 { - i-- - s[i] = charset[w&mask] - w >>= shift - nbits -= shift - } - - } else { - bb, ndigits := maxPow(Word(b)) - - // construct table of successive squares of bb*leafSize to use in subdivisions - // result (table != nil) <=> (len(x) > leafSize > 0) - table := divisors(len(x), b, ndigits, bb) - - // preserve x, create local copy for use by convertWords - q := nat(nil).set(x) - - // convert q to string s in base b - q.convertWords(s, charset, b, ndigits, bb, table) - - // strip leading zeros - // (x != 0; thus s must contain at least one non-zero digit - // and the loop will terminate) - i = 0 - for zero := charset[0]; s[i] == zero; { - i++ - } - } - - return string(s[i:]) -} - -// Convert words of q to base b digits in s. If q is large, it is recursively "split in half" -// by nat/nat division using tabulated divisors. Otherwise, it is converted iteratively using -// repeated nat/Word division. -// -// The iterative method processes n Words by n divW() calls, each of which visits every Word in the -// incrementally shortened q for a total of n + (n-1) + (n-2) ... + 2 + 1, or n(n+1)/2 divW()'s. -// Recursive conversion divides q by its approximate square root, yielding two parts, each half -// the size of q. Using the iterative method on both halves means 2 * (n/2)(n/2 + 1)/2 divW()'s -// plus the expensive long div(). Asymptotically, the ratio is favorable at 1/2 the divW()'s, and -// is made better by splitting the subblocks recursively. Best is to split blocks until one more -// split would take longer (because of the nat/nat div()) than the twice as many divW()'s of the -// iterative approach. This threshold is represented by leafSize. Benchmarking of leafSize in the -// range 2..64 shows that values of 8 and 16 work well, with a 4x speedup at medium lengths and -// ~30x for 20000 digits. Use nat_test.go's BenchmarkLeafSize tests to optimize leafSize for -// specific hardware. -// -func (q nat) convertWords(s []byte, charset string, b Word, ndigits int, bb Word, table []divisor) { - // split larger blocks recursively - if table != nil { - // len(q) > leafSize > 0 - var r nat - index := len(table) - 1 - for len(q) > leafSize { - // find divisor close to sqrt(q) if possible, but in any case < q - maxLength := q.bitLen() // ~= log2 q, or at of least largest possible q of this bit length - minLength := maxLength >> 1 // ~= log2 sqrt(q) - for index > 0 && table[index-1].nbits > minLength { - index-- // desired - } - if table[index].nbits >= maxLength && table[index].bbb.cmp(q) >= 0 { - index-- - if index < 0 { - panic("internal inconsistency") - } - } - - // split q into the two digit number (q'*bbb + r) to form independent subblocks - q, r = q.div(r, q, table[index].bbb) - - // convert subblocks and collect results in s[:h] and s[h:] - h := len(s) - table[index].ndigits - r.convertWords(s[h:], charset, b, ndigits, bb, table[0:index]) - s = s[:h] // == q.convertWords(s, charset, b, ndigits, bb, table[0:index+1]) - } - } - - // having split any large blocks now process the remaining (small) block iteratively - i := len(s) - var r Word - if b == 10 { - // hard-coding for 10 here speeds this up by 1.25x (allows for / and % by constants) - for len(q) > 0 { - // extract least significant, base bb "digit" - q, r = q.divW(q, bb) - for j := 0; j < ndigits && i > 0; j++ { - i-- - // avoid % computation since r%10 == r - int(r/10)*10; - // this appears to be faster for BenchmarkString10000Base10 - // and smaller strings (but a bit slower for larger ones) - t := r / 10 - s[i] = charset[r-t<<3-t-t] // TODO(gri) replace w/ t*10 once compiler produces better code - r = t - } - } - } else { - for len(q) > 0 { - // extract least significant, base bb "digit" - q, r = q.divW(q, bb) - for j := 0; j < ndigits && i > 0; j++ { - i-- - s[i] = charset[r%b] - r /= b - } - } - } - - // prepend high-order zeroes - zero := charset[0] - for i > 0 { // while need more leading zeroes - i-- - s[i] = zero - } -} - -// Split blocks greater than leafSize Words (or set to 0 to disable recursive conversion) -// Benchmark and configure leafSize using: go test -bench="Leaf" -// 8 and 16 effective on 3.0 GHz Xeon "Clovertown" CPU (128 byte cache lines) -// 8 and 16 effective on 2.66 GHz Core 2 Duo "Penryn" CPU -var leafSize int = 8 // number of Word-size binary values treat as a monolithic block - -type divisor struct { - bbb nat // divisor - nbits int // bit length of divisor (discounting leading zeroes) ~= log2(bbb) - ndigits int // digit length of divisor in terms of output base digits -} - -var cacheBase10 struct { - sync.Mutex - table [64]divisor // cached divisors for base 10 -} - -// expWW computes x**y -func (z nat) expWW(x, y Word) nat { - return z.expNN(nat(nil).setWord(x), nat(nil).setWord(y), nil) -} - -// construct table of powers of bb*leafSize to use in subdivisions -func divisors(m int, b Word, ndigits int, bb Word) []divisor { - // only compute table when recursive conversion is enabled and x is large - if leafSize == 0 || m <= leafSize { - return nil - } - - // determine k where (bb**leafSize)**(2**k) >= sqrt(x) - k := 1 - for words := leafSize; words < m>>1 && k < len(cacheBase10.table); words <<= 1 { - k++ - } - - // reuse and extend existing table of divisors or create new table as appropriate - var table []divisor // for b == 10, table overlaps with cacheBase10.table - if b == 10 { - cacheBase10.Lock() - table = cacheBase10.table[0:k] // reuse old table for this conversion - } else { - table = make([]divisor, k) // create new table for this conversion - } - - // extend table - if table[k-1].ndigits == 0 { - // add new entries as needed - var larger nat - for i := 0; i < k; i++ { - if table[i].ndigits == 0 { - if i == 0 { - table[0].bbb = nat(nil).expWW(bb, Word(leafSize)) - table[0].ndigits = ndigits * leafSize - } else { - table[i].bbb = nat(nil).mul(table[i-1].bbb, table[i-1].bbb) - table[i].ndigits = 2 * table[i-1].ndigits - } - - // optimization: exploit aggregated extra bits in macro blocks - larger = nat(nil).set(table[i].bbb) - for mulAddVWW(larger, larger, b, 0) == 0 { - table[i].bbb = table[i].bbb.set(larger) - table[i].ndigits++ - } - - table[i].nbits = table[i].bbb.bitLen() - } - } - } - - if b == 10 { - cacheBase10.Unlock() - } - - return table -} diff --git a/src/cmd/internal/gc/big/natconv_test.go b/src/cmd/internal/gc/big/natconv_test.go deleted file mode 100644 index f321fbc2df..0000000000 --- a/src/cmd/internal/gc/big/natconv_test.go +++ /dev/null @@ -1,425 +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 big - -import ( - "io" - "strings" - "testing" -) - -func toString(x nat, charset string) string { - base := len(charset) - - // special cases - switch { - case base < 2: - panic("illegal base") - case len(x) == 0: - return string(charset[0]) - } - - // allocate buffer for conversion - i := x.bitLen()/log2(Word(base)) + 1 // +1: round up - s := make([]byte, i) - - // don't destroy x - q := nat(nil).set(x) - - // convert - for len(q) > 0 { - i-- - var r Word - q, r = q.divW(q, Word(base)) - s[i] = charset[r] - } - - return string(s[i:]) -} - -var strTests = []struct { - x nat // nat value to be converted - c string // conversion charset - s string // expected result -}{ - {nil, "01", "0"}, - {nat{1}, "01", "1"}, - {nat{0xc5}, "01", "11000101"}, - {nat{03271}, lowercaseDigits[:8], "3271"}, - {nat{10}, lowercaseDigits[:10], "10"}, - {nat{1234567890}, uppercaseDigits[:10], "1234567890"}, - {nat{0xdeadbeef}, lowercaseDigits[:16], "deadbeef"}, - {nat{0xdeadbeef}, uppercaseDigits[:16], "DEADBEEF"}, - {nat{0x229be7}, lowercaseDigits[:17], "1a2b3c"}, - {nat{0x309663e6}, uppercaseDigits[:32], "O9COV6"}, -} - -func TestString(t *testing.T) { - // test invalid character set explicitly - var panicStr string - func() { - defer func() { - panicStr = recover().(string) - }() - natOne.string("0") - }() - if panicStr != "invalid character set length" { - t.Errorf("expected panic for invalid character set") - } - - for _, a := range strTests { - s := a.x.string(a.c) - if s != a.s { - t.Errorf("string%+v\n\tgot s = %s; want %s", a, s, a.s) - } - - x, b, _, err := nat(nil).scan(strings.NewReader(a.s), len(a.c), false) - if x.cmp(a.x) != 0 { - t.Errorf("scan%+v\n\tgot z = %v; want %v", a, x, a.x) - } - if b != len(a.c) { - t.Errorf("scan%+v\n\tgot b = %d; want %d", a, b, len(a.c)) - } - if err != nil { - t.Errorf("scan%+v\n\tgot error = %s", a, err) - } - } -} - -var natScanTests = []struct { - s string // string to be scanned - base int // input base - frac bool // fraction ok - x nat // expected nat - b int // expected base - count int // expected digit count - ok bool // expected success - next rune // next character (or 0, if at EOF) -}{ - // error: no mantissa - {}, - {s: "?"}, - {base: 10}, - {base: 36}, - {s: "?", base: 10}, - {s: "0x"}, - {s: "345", base: 2}, - - // error: incorrect use of decimal point - {s: ".0"}, - {s: ".0", base: 10}, - {s: ".", base: 0}, - {s: "0x.0"}, - - // no errors - {"0", 0, false, nil, 10, 1, true, 0}, - {"0", 10, false, nil, 10, 1, true, 0}, - {"0", 36, false, nil, 36, 1, true, 0}, - {"1", 0, false, nat{1}, 10, 1, true, 0}, - {"1", 10, false, nat{1}, 10, 1, true, 0}, - {"0 ", 0, false, nil, 10, 1, true, ' '}, - {"08", 0, false, nil, 10, 1, true, '8'}, - {"08", 10, false, nat{8}, 10, 2, true, 0}, - {"018", 0, false, nat{1}, 8, 1, true, '8'}, - {"0b1", 0, false, nat{1}, 2, 1, true, 0}, - {"0b11000101", 0, false, nat{0xc5}, 2, 8, true, 0}, - {"03271", 0, false, nat{03271}, 8, 4, true, 0}, - {"10ab", 0, false, nat{10}, 10, 2, true, 'a'}, - {"1234567890", 0, false, nat{1234567890}, 10, 10, true, 0}, - {"xyz", 36, false, nat{(33*36+34)*36 + 35}, 36, 3, true, 0}, - {"xyz?", 36, false, nat{(33*36+34)*36 + 35}, 36, 3, true, '?'}, - {"0x", 16, false, nil, 16, 1, true, 'x'}, - {"0xdeadbeef", 0, false, nat{0xdeadbeef}, 16, 8, true, 0}, - {"0XDEADBEEF", 0, false, nat{0xdeadbeef}, 16, 8, true, 0}, - - // no errors, decimal point - {"0.", 0, false, nil, 10, 1, true, '.'}, - {"0.", 10, true, nil, 10, 0, true, 0}, - {"0.1.2", 10, true, nat{1}, 10, -1, true, '.'}, - {".000", 10, true, nil, 10, -3, true, 0}, - {"12.3", 10, true, nat{123}, 10, -1, true, 0}, - {"012.345", 10, true, nat{12345}, 10, -3, true, 0}, -} - -func TestScanBase(t *testing.T) { - for _, a := range natScanTests { - r := strings.NewReader(a.s) - x, b, count, err := nat(nil).scan(r, a.base, a.frac) - if err == nil && !a.ok { - t.Errorf("scan%+v\n\texpected error", a) - } - if err != nil { - if a.ok { - t.Errorf("scan%+v\n\tgot error = %s", a, err) - } - continue - } - if x.cmp(a.x) != 0 { - t.Errorf("scan%+v\n\tgot z = %v; want %v", a, x, a.x) - } - if b != a.b { - t.Errorf("scan%+v\n\tgot b = %d; want %d", a, b, a.base) - } - if count != a.count { - t.Errorf("scan%+v\n\tgot count = %d; want %d", a, count, a.count) - } - next, _, err := r.ReadRune() - if err == io.EOF { - next = 0 - err = nil - } - if err == nil && next != a.next { - t.Errorf("scan%+v\n\tgot next = %q; want %q", a, next, a.next) - } - } -} - -var pi = "3" + - "14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651" + - "32823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461" + - "28475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920" + - "96282925409171536436789259036001133053054882046652138414695194151160943305727036575959195309218611738193261179" + - "31051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798" + - "60943702770539217176293176752384674818467669405132000568127145263560827785771342757789609173637178721468440901" + - "22495343014654958537105079227968925892354201995611212902196086403441815981362977477130996051870721134999999837" + - "29780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083" + - "81420617177669147303598253490428755468731159562863882353787593751957781857780532171226806613001927876611195909" + - "21642019893809525720106548586327886593615338182796823030195203530185296899577362259941389124972177528347913151" + - "55748572424541506959508295331168617278558890750983817546374649393192550604009277016711390098488240128583616035" + - "63707660104710181942955596198946767837449448255379774726847104047534646208046684259069491293313677028989152104" + - "75216205696602405803815019351125338243003558764024749647326391419927260426992279678235478163600934172164121992" + - "45863150302861829745557067498385054945885869269956909272107975093029553211653449872027559602364806654991198818" + - "34797753566369807426542527862551818417574672890977772793800081647060016145249192173217214772350141441973568548" + - "16136115735255213347574184946843852332390739414333454776241686251898356948556209921922218427255025425688767179" + - "04946016534668049886272327917860857843838279679766814541009538837863609506800642251252051173929848960841284886" + - "26945604241965285022210661186306744278622039194945047123713786960956364371917287467764657573962413890865832645" + - "99581339047802759009946576407895126946839835259570982582262052248940772671947826848260147699090264013639443745" + - "53050682034962524517493996514314298091906592509372216964615157098583874105978859597729754989301617539284681382" + - "68683868942774155991855925245953959431049972524680845987273644695848653836736222626099124608051243884390451244" + - "13654976278079771569143599770012961608944169486855584840635342207222582848864815845602850601684273945226746767" + - "88952521385225499546667278239864565961163548862305774564980355936345681743241125150760694794510965960940252288" + - "79710893145669136867228748940560101503308617928680920874760917824938589009714909675985261365549781893129784821" + - "68299894872265880485756401427047755513237964145152374623436454285844479526586782105114135473573952311342716610" + - "21359695362314429524849371871101457654035902799344037420073105785390621983874478084784896833214457138687519435" + - "06430218453191048481005370614680674919278191197939952061419663428754440643745123718192179998391015919561814675" + - "14269123974894090718649423196156794520809514655022523160388193014209376213785595663893778708303906979207734672" + - "21825625996615014215030680384477345492026054146659252014974428507325186660021324340881907104863317346496514539" + - "05796268561005508106658796998163574736384052571459102897064140110971206280439039759515677157700420337869936007" + - "23055876317635942187312514712053292819182618612586732157919841484882916447060957527069572209175671167229109816" + - "90915280173506712748583222871835209353965725121083579151369882091444210067510334671103141267111369908658516398" + - "31501970165151168517143765761835155650884909989859982387345528331635507647918535893226185489632132933089857064" + - "20467525907091548141654985946163718027098199430992448895757128289059232332609729971208443357326548938239119325" + - "97463667305836041428138830320382490375898524374417029132765618093773444030707469211201913020330380197621101100" + - "44929321516084244485963766983895228684783123552658213144957685726243344189303968642624341077322697802807318915" + - "44110104468232527162010526522721116603966655730925471105578537634668206531098965269186205647693125705863566201" + - "85581007293606598764861179104533488503461136576867532494416680396265797877185560845529654126654085306143444318" + - "58676975145661406800700237877659134401712749470420562230538994561314071127000407854733269939081454664645880797" + - "27082668306343285878569830523580893306575740679545716377525420211495576158140025012622859413021647155097925923" + - "09907965473761255176567513575178296664547791745011299614890304639947132962107340437518957359614589019389713111" + - "79042978285647503203198691514028708085990480109412147221317947647772622414254854540332157185306142288137585043" + - "06332175182979866223717215916077166925474873898665494945011465406284336639379003976926567214638530673609657120" + - "91807638327166416274888800786925602902284721040317211860820419000422966171196377921337575114959501566049631862" + - "94726547364252308177036751590673502350728354056704038674351362222477158915049530984448933309634087807693259939" + - "78054193414473774418426312986080998886874132604721569516239658645730216315981931951673538129741677294786724229" + - "24654366800980676928238280689964004824354037014163149658979409243237896907069779422362508221688957383798623001" + - "59377647165122893578601588161755782973523344604281512627203734314653197777416031990665541876397929334419521541" + - "34189948544473456738316249934191318148092777710386387734317720754565453220777092120190516609628049092636019759" + - "88281613323166636528619326686336062735676303544776280350450777235547105859548702790814356240145171806246436267" + - "94561275318134078330336254232783944975382437205835311477119926063813346776879695970309833913077109870408591337" - -// Test case for BenchmarkScanPi. -func TestScanPi(t *testing.T) { - var x nat - z, _, _, err := x.scan(strings.NewReader(pi), 10, false) - if err != nil { - t.Errorf("scanning pi: %s", err) - } - if s := z.decimalString(); s != pi { - t.Errorf("scanning pi: got %s", s) - } -} - -func TestScanPiParallel(t *testing.T) { - const n = 2 - c := make(chan int) - for i := 0; i < n; i++ { - go func() { - TestScanPi(t) - c <- 0 - }() - } - for i := 0; i < n; i++ { - <-c - } -} - -func BenchmarkScanPi(b *testing.B) { - for i := 0; i < b.N; i++ { - var x nat - x.scan(strings.NewReader(pi), 10, false) - } -} - -func BenchmarkStringPiParallel(b *testing.B) { - var x nat - x, _, _, _ = x.scan(strings.NewReader(pi), 0, false) - if x.decimalString() != pi { - panic("benchmark incorrect: conversion failed") - } - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - x.decimalString() - } - }) -} - -func BenchmarkScan10Base2(b *testing.B) { ScanHelper(b, 2, 10, 10) } -func BenchmarkScan100Base2(b *testing.B) { ScanHelper(b, 2, 10, 100) } -func BenchmarkScan1000Base2(b *testing.B) { ScanHelper(b, 2, 10, 1000) } -func BenchmarkScan10000Base2(b *testing.B) { ScanHelper(b, 2, 10, 10000) } -func BenchmarkScan100000Base2(b *testing.B) { ScanHelper(b, 2, 10, 100000) } - -func BenchmarkScan10Base8(b *testing.B) { ScanHelper(b, 8, 10, 10) } -func BenchmarkScan100Base8(b *testing.B) { ScanHelper(b, 8, 10, 100) } -func BenchmarkScan1000Base8(b *testing.B) { ScanHelper(b, 8, 10, 1000) } -func BenchmarkScan10000Base8(b *testing.B) { ScanHelper(b, 8, 10, 10000) } -func BenchmarkScan100000Base8(b *testing.B) { ScanHelper(b, 8, 10, 100000) } - -func BenchmarkScan10Base10(b *testing.B) { ScanHelper(b, 10, 10, 10) } -func BenchmarkScan100Base10(b *testing.B) { ScanHelper(b, 10, 10, 100) } -func BenchmarkScan1000Base10(b *testing.B) { ScanHelper(b, 10, 10, 1000) } -func BenchmarkScan10000Base10(b *testing.B) { ScanHelper(b, 10, 10, 10000) } -func BenchmarkScan100000Base10(b *testing.B) { ScanHelper(b, 10, 10, 100000) } - -func BenchmarkScan10Base16(b *testing.B) { ScanHelper(b, 16, 10, 10) } -func BenchmarkScan100Base16(b *testing.B) { ScanHelper(b, 16, 10, 100) } -func BenchmarkScan1000Base16(b *testing.B) { ScanHelper(b, 16, 10, 1000) } -func BenchmarkScan10000Base16(b *testing.B) { ScanHelper(b, 16, 10, 10000) } -func BenchmarkScan100000Base16(b *testing.B) { ScanHelper(b, 16, 10, 100000) } - -func ScanHelper(b *testing.B, base int, x, y Word) { - b.StopTimer() - var z nat - z = z.expWW(x, y) - - var s string - s = z.string(lowercaseDigits[:base]) - if t := toString(z, lowercaseDigits[:base]); t != s { - b.Fatalf("scanning: got %s; want %s", s, t) - } - b.StartTimer() - - for i := 0; i < b.N; i++ { - z.scan(strings.NewReader(s), base, false) - } -} - -func BenchmarkString10Base2(b *testing.B) { StringHelper(b, 2, 10, 10) } -func BenchmarkString100Base2(b *testing.B) { StringHelper(b, 2, 10, 100) } -func BenchmarkString1000Base2(b *testing.B) { StringHelper(b, 2, 10, 1000) } -func BenchmarkString10000Base2(b *testing.B) { StringHelper(b, 2, 10, 10000) } -func BenchmarkString100000Base2(b *testing.B) { StringHelper(b, 2, 10, 100000) } - -func BenchmarkString10Base8(b *testing.B) { StringHelper(b, 8, 10, 10) } -func BenchmarkString100Base8(b *testing.B) { StringHelper(b, 8, 10, 100) } -func BenchmarkString1000Base8(b *testing.B) { StringHelper(b, 8, 10, 1000) } -func BenchmarkString10000Base8(b *testing.B) { StringHelper(b, 8, 10, 10000) } -func BenchmarkString100000Base8(b *testing.B) { StringHelper(b, 8, 10, 100000) } - -func BenchmarkString10Base10(b *testing.B) { StringHelper(b, 10, 10, 10) } -func BenchmarkString100Base10(b *testing.B) { StringHelper(b, 10, 10, 100) } -func BenchmarkString1000Base10(b *testing.B) { StringHelper(b, 10, 10, 1000) } -func BenchmarkString10000Base10(b *testing.B) { StringHelper(b, 10, 10, 10000) } -func BenchmarkString100000Base10(b *testing.B) { StringHelper(b, 10, 10, 100000) } - -func BenchmarkString10Base16(b *testing.B) { StringHelper(b, 16, 10, 10) } -func BenchmarkString100Base16(b *testing.B) { StringHelper(b, 16, 10, 100) } -func BenchmarkString1000Base16(b *testing.B) { StringHelper(b, 16, 10, 1000) } -func BenchmarkString10000Base16(b *testing.B) { StringHelper(b, 16, 10, 10000) } -func BenchmarkString100000Base16(b *testing.B) { StringHelper(b, 16, 10, 100000) } - -func StringHelper(b *testing.B, base int, x, y Word) { - b.StopTimer() - var z nat - z = z.expWW(x, y) - z.string(lowercaseDigits[:base]) // warm divisor cache - b.StartTimer() - - for i := 0; i < b.N; i++ { - _ = z.string(lowercaseDigits[:base]) - } -} - -func BenchmarkLeafSize0(b *testing.B) { LeafSizeHelper(b, 10, 0) } // test without splitting -func BenchmarkLeafSize1(b *testing.B) { LeafSizeHelper(b, 10, 1) } -func BenchmarkLeafSize2(b *testing.B) { LeafSizeHelper(b, 10, 2) } -func BenchmarkLeafSize3(b *testing.B) { LeafSizeHelper(b, 10, 3) } -func BenchmarkLeafSize4(b *testing.B) { LeafSizeHelper(b, 10, 4) } -func BenchmarkLeafSize5(b *testing.B) { LeafSizeHelper(b, 10, 5) } -func BenchmarkLeafSize6(b *testing.B) { LeafSizeHelper(b, 10, 6) } -func BenchmarkLeafSize7(b *testing.B) { LeafSizeHelper(b, 10, 7) } -func BenchmarkLeafSize8(b *testing.B) { LeafSizeHelper(b, 10, 8) } -func BenchmarkLeafSize9(b *testing.B) { LeafSizeHelper(b, 10, 9) } -func BenchmarkLeafSize10(b *testing.B) { LeafSizeHelper(b, 10, 10) } -func BenchmarkLeafSize11(b *testing.B) { LeafSizeHelper(b, 10, 11) } -func BenchmarkLeafSize12(b *testing.B) { LeafSizeHelper(b, 10, 12) } -func BenchmarkLeafSize13(b *testing.B) { LeafSizeHelper(b, 10, 13) } -func BenchmarkLeafSize14(b *testing.B) { LeafSizeHelper(b, 10, 14) } -func BenchmarkLeafSize15(b *testing.B) { LeafSizeHelper(b, 10, 15) } -func BenchmarkLeafSize16(b *testing.B) { LeafSizeHelper(b, 10, 16) } -func BenchmarkLeafSize32(b *testing.B) { LeafSizeHelper(b, 10, 32) } // try some large lengths -func BenchmarkLeafSize64(b *testing.B) { LeafSizeHelper(b, 10, 64) } - -func LeafSizeHelper(b *testing.B, base Word, size int) { - b.StopTimer() - originalLeafSize := leafSize - resetTable(cacheBase10.table[:]) - leafSize = size - b.StartTimer() - - for d := 1; d <= 10000; d *= 10 { - b.StopTimer() - var z nat - z = z.expWW(base, Word(d)) // build target number - _ = z.string(lowercaseDigits[:base]) // warm divisor cache - b.StartTimer() - - for i := 0; i < b.N; i++ { - _ = z.string(lowercaseDigits[:base]) - } - } - - b.StopTimer() - resetTable(cacheBase10.table[:]) - leafSize = originalLeafSize - b.StartTimer() -} - -func resetTable(table []divisor) { - if table != nil && table[0].bbb != nil { - for i := 0; i < len(table); i++ { - table[i].bbb = nil - table[i].nbits = 0 - table[i].ndigits = 0 - } - } -} - -func TestStringPowers(t *testing.T) { - var b, p Word - for b = 2; b <= 16; b++ { - for p = 0; p <= 512; p++ { - x := nat(nil).expWW(b, p) - xs := x.string(lowercaseDigits[:b]) - xs2 := toString(x, lowercaseDigits[:b]) - if xs != xs2 { - t.Errorf("failed at %d ** %d in base %d: %s != %s", b, p, b, xs, xs2) - } - } - if b >= 3 && testing.Short() { - break - } - } -} diff --git a/src/cmd/internal/gc/big/rat.go b/src/cmd/internal/gc/big/rat.go deleted file mode 100644 index fb16f18a96..0000000000 --- a/src/cmd/internal/gc/big/rat.go +++ /dev/null @@ -1,570 +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. - -// This file implements multi-precision rational numbers. - -package big - -import ( - "encoding/binary" - "errors" - "fmt" - "math" -) - -// A Rat represents a quotient a/b of arbitrary precision. -// The zero value for a Rat represents the value 0. -type Rat struct { - // To make zero values for Rat work w/o initialization, - // a zero value of b (len(b) == 0) acts like b == 1. - // a.neg determines the sign of the Rat, b.neg is ignored. - a, b Int -} - -// NewRat creates a new Rat with numerator a and denominator b. -func NewRat(a, b int64) *Rat { - return new(Rat).SetFrac64(a, b) -} - -// SetFloat64 sets z to exactly f and returns z. -// If f is not finite, SetFloat returns nil. -func (z *Rat) SetFloat64(f float64) *Rat { - const expMask = 1<<11 - 1 - bits := math.Float64bits(f) - mantissa := bits & (1<<52 - 1) - exp := int((bits >> 52) & expMask) - switch exp { - case expMask: // non-finite - return nil - case 0: // denormal - exp -= 1022 - default: // normal - mantissa |= 1 << 52 - exp -= 1023 - } - - shift := 52 - exp - - // Optimization (?): partially pre-normalise. - for mantissa&1 == 0 && shift > 0 { - mantissa >>= 1 - shift-- - } - - z.a.SetUint64(mantissa) - z.a.neg = f < 0 - z.b.Set(intOne) - if shift > 0 { - z.b.Lsh(&z.b, uint(shift)) - } else { - z.a.Lsh(&z.a, uint(-shift)) - } - return z.norm() -} - -// quotToFloat32 returns the non-negative float32 value -// nearest to the quotient a/b, using round-to-even in -// halfway cases. It does not mutate its arguments. -// Preconditions: b is non-zero; a and b have no common factors. -func quotToFloat32(a, b nat) (f float32, exact bool) { - const ( - // float size in bits - Fsize = 32 - - // mantissa - Msize = 23 - Msize1 = Msize + 1 // incl. implicit 1 - Msize2 = Msize1 + 1 - - // exponent - Esize = Fsize - Msize1 - Ebias = 1<<(Esize-1) - 1 - Emin = 1 - Ebias - Emax = Ebias - ) - - // TODO(adonovan): specialize common degenerate cases: 1.0, integers. - alen := a.bitLen() - if alen == 0 { - return 0, true - } - blen := b.bitLen() - if blen == 0 { - panic("division by zero") - } - - // 1. Left-shift A or B such that quotient A/B is in [1<= B). - // This is 2 or 3 more than the float32 mantissa field width of Msize: - // - the optional extra bit is shifted away in step 3 below. - // - the high-order 1 is omitted in "normal" representation; - // - the low-order 1 will be used during rounding then discarded. - exp := alen - blen - var a2, b2 nat - a2 = a2.set(a) - b2 = b2.set(b) - if shift := Msize2 - exp; shift > 0 { - a2 = a2.shl(a2, uint(shift)) - } else if shift < 0 { - b2 = b2.shl(b2, uint(-shift)) - } - - // 2. Compute quotient and remainder (q, r). NB: due to the - // extra shift, the low-order bit of q is logically the - // high-order bit of r. - var q nat - q, r := q.div(a2, a2, b2) // (recycle a2) - mantissa := low32(q) - haveRem := len(r) > 0 // mantissa&1 && !haveRem => remainder is exactly half - - // 3. If quotient didn't fit in Msize2 bits, redo division by b2<<1 - // (in effect---we accomplish this incrementally). - if mantissa>>Msize2 == 1 { - if mantissa&1 == 1 { - haveRem = true - } - mantissa >>= 1 - exp++ - } - if mantissa>>Msize1 != 1 { - panic(fmt.Sprintf("expected exactly %d bits of result", Msize2)) - } - - // 4. Rounding. - if Emin-Msize <= exp && exp <= Emin { - // Denormal case; lose 'shift' bits of precision. - shift := uint(Emin - (exp - 1)) // [1..Esize1) - lostbits := mantissa & (1<>= shift - exp = 2 - Ebias // == exp + shift - } - // Round q using round-half-to-even. - exact = !haveRem - if mantissa&1 != 0 { - exact = false - if haveRem || mantissa&2 != 0 { - if mantissa++; mantissa >= 1< 100...0, so shift is safe - mantissa >>= 1 - exp++ - } - } - } - mantissa >>= 1 // discard rounding bit. Mantissa now scaled by 1<= B). - // This is 2 or 3 more than the float64 mantissa field width of Msize: - // - the optional extra bit is shifted away in step 3 below. - // - the high-order 1 is omitted in "normal" representation; - // - the low-order 1 will be used during rounding then discarded. - exp := alen - blen - var a2, b2 nat - a2 = a2.set(a) - b2 = b2.set(b) - if shift := Msize2 - exp; shift > 0 { - a2 = a2.shl(a2, uint(shift)) - } else if shift < 0 { - b2 = b2.shl(b2, uint(-shift)) - } - - // 2. Compute quotient and remainder (q, r). NB: due to the - // extra shift, the low-order bit of q is logically the - // high-order bit of r. - var q nat - q, r := q.div(a2, a2, b2) // (recycle a2) - mantissa := low64(q) - haveRem := len(r) > 0 // mantissa&1 && !haveRem => remainder is exactly half - - // 3. If quotient didn't fit in Msize2 bits, redo division by b2<<1 - // (in effect---we accomplish this incrementally). - if mantissa>>Msize2 == 1 { - if mantissa&1 == 1 { - haveRem = true - } - mantissa >>= 1 - exp++ - } - if mantissa>>Msize1 != 1 { - panic(fmt.Sprintf("expected exactly %d bits of result", Msize2)) - } - - // 4. Rounding. - if Emin-Msize <= exp && exp <= Emin { - // Denormal case; lose 'shift' bits of precision. - shift := uint(Emin - (exp - 1)) // [1..Esize1) - lostbits := mantissa & (1<>= shift - exp = 2 - Ebias // == exp + shift - } - // Round q using round-half-to-even. - exact = !haveRem - if mantissa&1 != 0 { - exact = false - if haveRem || mantissa&2 != 0 { - if mantissa++; mantissa >= 1< 100...0, so shift is safe - mantissa >>= 1 - exp++ - } - } - } - mantissa >>= 1 // discard rounding bit. Mantissa now scaled by 1< 0 && !z.a.neg // 0 has no sign - return z -} - -// Inv sets z to 1/x and returns z. -func (z *Rat) Inv(x *Rat) *Rat { - if len(x.a.abs) == 0 { - panic("division by zero") - } - z.Set(x) - a := z.b.abs - if len(a) == 0 { - a = a.set(natOne) // materialize numerator - } - b := z.a.abs - if b.cmp(natOne) == 0 { - b = b[:0] // normalize denominator - } - z.a.abs, z.b.abs = a, b // sign doesn't change - return z -} - -// Sign returns: -// -// -1 if x < 0 -// 0 if x == 0 -// +1 if x > 0 -// -func (x *Rat) Sign() int { - return x.a.Sign() -} - -// IsInt reports whether the denominator of x is 1. -func (x *Rat) IsInt() bool { - return len(x.b.abs) == 0 || x.b.abs.cmp(natOne) == 0 -} - -// Num returns the numerator of x; it may be <= 0. -// The result is a reference to x's numerator; it -// may change if a new value is assigned to x, and vice versa. -// The sign of the numerator corresponds to the sign of x. -func (x *Rat) Num() *Int { - return &x.a -} - -// Denom returns the denominator of x; it is always > 0. -// The result is a reference to x's denominator; it -// may change if a new value is assigned to x, and vice versa. -func (x *Rat) Denom() *Int { - x.b.neg = false // the result is always >= 0 - if len(x.b.abs) == 0 { - x.b.abs = x.b.abs.set(natOne) // materialize denominator - } - return &x.b -} - -func (z *Rat) norm() *Rat { - switch { - case len(z.a.abs) == 0: - // z == 0 - normalize sign and denominator - z.a.neg = false - z.b.abs = z.b.abs[:0] - case len(z.b.abs) == 0: - // z is normalized int - nothing to do - case z.b.abs.cmp(natOne) == 0: - // z is int - normalize denominator - z.b.abs = z.b.abs[:0] - default: - neg := z.a.neg - z.a.neg = false - z.b.neg = false - if f := NewInt(0).binaryGCD(&z.a, &z.b); f.Cmp(intOne) != 0 { - z.a.abs, _ = z.a.abs.div(nil, z.a.abs, f.abs) - z.b.abs, _ = z.b.abs.div(nil, z.b.abs, f.abs) - if z.b.abs.cmp(natOne) == 0 { - // z is int - normalize denominator - z.b.abs = z.b.abs[:0] - } - } - z.a.neg = neg - } - return z -} - -// mulDenom sets z to the denominator product x*y (by taking into -// account that 0 values for x or y must be interpreted as 1) and -// returns z. -func mulDenom(z, x, y nat) nat { - switch { - case len(x) == 0: - return z.set(y) - case len(y) == 0: - return z.set(x) - } - return z.mul(x, y) -} - -// scaleDenom computes x*f. -// If f == 0 (zero value of denominator), the result is (a copy of) x. -func scaleDenom(x *Int, f nat) *Int { - var z Int - if len(f) == 0 { - return z.Set(x) - } - z.abs = z.abs.mul(x.abs, f) - z.neg = x.neg - return &z -} - -// Cmp compares x and y and returns: -// -// -1 if x < y -// 0 if x == y -// +1 if x > y -// -func (x *Rat) Cmp(y *Rat) int { - return scaleDenom(&x.a, y.b.abs).Cmp(scaleDenom(&y.a, x.b.abs)) -} - -// Add sets z to the sum x+y and returns z. -func (z *Rat) Add(x, y *Rat) *Rat { - a1 := scaleDenom(&x.a, y.b.abs) - a2 := scaleDenom(&y.a, x.b.abs) - z.a.Add(a1, a2) - z.b.abs = mulDenom(z.b.abs, x.b.abs, y.b.abs) - return z.norm() -} - -// Sub sets z to the difference x-y and returns z. -func (z *Rat) Sub(x, y *Rat) *Rat { - a1 := scaleDenom(&x.a, y.b.abs) - a2 := scaleDenom(&y.a, x.b.abs) - z.a.Sub(a1, a2) - z.b.abs = mulDenom(z.b.abs, x.b.abs, y.b.abs) - return z.norm() -} - -// Mul sets z to the product x*y and returns z. -func (z *Rat) Mul(x, y *Rat) *Rat { - z.a.Mul(&x.a, &y.a) - z.b.abs = mulDenom(z.b.abs, x.b.abs, y.b.abs) - return z.norm() -} - -// Quo sets z to the quotient x/y and returns z. -// If y == 0, a division-by-zero run-time panic occurs. -func (z *Rat) Quo(x, y *Rat) *Rat { - if len(y.a.abs) == 0 { - panic("division by zero") - } - a := scaleDenom(&x.a, y.b.abs) - b := scaleDenom(&y.a, x.b.abs) - z.a.abs = a.abs - z.b.abs = b.abs - z.a.neg = a.neg != b.neg - return z.norm() -} - -// Gob codec version. Permits backward-compatible changes to the encoding. -const ratGobVersion byte = 1 - -// GobEncode implements the gob.GobEncoder interface. -func (x *Rat) GobEncode() ([]byte, error) { - if x == nil { - return nil, nil - } - buf := make([]byte, 1+4+(len(x.a.abs)+len(x.b.abs))*_S) // extra bytes for version and sign bit (1), and numerator length (4) - i := x.b.abs.bytes(buf) - j := x.a.abs.bytes(buf[:i]) - n := i - j - if int(uint32(n)) != n { - // this should never happen - return nil, errors.New("Rat.GobEncode: numerator too large") - } - binary.BigEndian.PutUint32(buf[j-4:j], uint32(n)) - j -= 1 + 4 - b := ratGobVersion << 1 // make space for sign bit - if x.a.neg { - b |= 1 - } - buf[j] = b - return buf[j:], nil -} - -// GobDecode implements the gob.GobDecoder interface. -func (z *Rat) GobDecode(buf []byte) error { - if len(buf) == 0 { - // Other side sent a nil or default value. - *z = Rat{} - return nil - } - b := buf[0] - if b>>1 != ratGobVersion { - return fmt.Errorf("Rat.GobDecode: encoding version %d not supported", b>>1) - } - const j = 1 + 4 - i := j + binary.BigEndian.Uint32(buf[j-4:j]) - z.a.neg = b&1 != 0 - z.a.abs = z.a.abs.setBytes(buf[j:i]) - z.b.abs = z.b.abs.setBytes(buf[i:]) - return nil -} - -// MarshalText implements the encoding.TextMarshaler interface. -func (r *Rat) MarshalText() (text []byte, err error) { - return []byte(r.RatString()), nil -} - -// UnmarshalText implements the encoding.TextUnmarshaler interface. -func (r *Rat) UnmarshalText(text []byte) error { - if _, ok := r.SetString(string(text)); !ok { - return fmt.Errorf("math/big: cannot unmarshal %q into a *big.Rat", text) - } - return nil -} diff --git a/src/cmd/internal/gc/big/rat_test.go b/src/cmd/internal/gc/big/rat_test.go deleted file mode 100644 index 012d0c47ec..0000000000 --- a/src/cmd/internal/gc/big/rat_test.go +++ /dev/null @@ -1,736 +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. - -package big - -import ( - "bytes" - "encoding/gob" - "encoding/json" - "encoding/xml" - "math" - "testing" -) - -func TestZeroRat(t *testing.T) { - var x, y, z Rat - y.SetFrac64(0, 42) - - if x.Cmp(&y) != 0 { - t.Errorf("x and y should be both equal and zero") - } - - if s := x.String(); s != "0/1" { - t.Errorf("got x = %s, want 0/1", s) - } - - if s := x.RatString(); s != "0" { - t.Errorf("got x = %s, want 0", s) - } - - z.Add(&x, &y) - if s := z.RatString(); s != "0" { - t.Errorf("got x+y = %s, want 0", s) - } - - z.Sub(&x, &y) - if s := z.RatString(); s != "0" { - t.Errorf("got x-y = %s, want 0", s) - } - - z.Mul(&x, &y) - if s := z.RatString(); s != "0" { - t.Errorf("got x*y = %s, want 0", s) - } - - // check for division by zero - defer func() { - if s := recover(); s == nil || s.(string) != "division by zero" { - panic(s) - } - }() - z.Quo(&x, &y) -} - -func TestRatSign(t *testing.T) { - zero := NewRat(0, 1) - for _, a := range setStringTests { - x, ok := new(Rat).SetString(a.in) - if !ok { - continue - } - s := x.Sign() - e := x.Cmp(zero) - if s != e { - t.Errorf("got %d; want %d for z = %v", s, e, &x) - } - } -} - -var ratCmpTests = []struct { - rat1, rat2 string - out int -}{ - {"0", "0/1", 0}, - {"1/1", "1", 0}, - {"-1", "-2/2", 0}, - {"1", "0", 1}, - {"0/1", "1/1", -1}, - {"-5/1434770811533343057144", "-5/1434770811533343057145", -1}, - {"49832350382626108453/8964749413", "49832350382626108454/8964749413", -1}, - {"-37414950961700930/7204075375675961", "37414950961700930/7204075375675961", -1}, - {"37414950961700930/7204075375675961", "74829901923401860/14408150751351922", 0}, -} - -func TestRatCmp(t *testing.T) { - for i, test := range ratCmpTests { - x, _ := new(Rat).SetString(test.rat1) - y, _ := new(Rat).SetString(test.rat2) - - out := x.Cmp(y) - if out != test.out { - t.Errorf("#%d got out = %v; want %v", i, out, test.out) - } - } -} - -func TestIsInt(t *testing.T) { - one := NewInt(1) - for _, a := range setStringTests { - x, ok := new(Rat).SetString(a.in) - if !ok { - continue - } - i := x.IsInt() - e := x.Denom().Cmp(one) == 0 - if i != e { - t.Errorf("got IsInt(%v) == %v; want %v", x, i, e) - } - } -} - -func TestRatAbs(t *testing.T) { - zero := new(Rat) - for _, a := range setStringTests { - x, ok := new(Rat).SetString(a.in) - if !ok { - continue - } - e := new(Rat).Set(x) - if e.Cmp(zero) < 0 { - e.Sub(zero, e) - } - z := new(Rat).Abs(x) - if z.Cmp(e) != 0 { - t.Errorf("got Abs(%v) = %v; want %v", x, z, e) - } - } -} - -func TestRatNeg(t *testing.T) { - zero := new(Rat) - for _, a := range setStringTests { - x, ok := new(Rat).SetString(a.in) - if !ok { - continue - } - e := new(Rat).Sub(zero, x) - z := new(Rat).Neg(x) - if z.Cmp(e) != 0 { - t.Errorf("got Neg(%v) = %v; want %v", x, z, e) - } - } -} - -func TestRatInv(t *testing.T) { - zero := new(Rat) - for _, a := range setStringTests { - x, ok := new(Rat).SetString(a.in) - if !ok { - continue - } - if x.Cmp(zero) == 0 { - continue // avoid division by zero - } - e := new(Rat).SetFrac(x.Denom(), x.Num()) - z := new(Rat).Inv(x) - if z.Cmp(e) != 0 { - t.Errorf("got Inv(%v) = %v; want %v", x, z, e) - } - } -} - -type ratBinFun func(z, x, y *Rat) *Rat -type ratBinArg struct { - x, y, z string -} - -func testRatBin(t *testing.T, i int, name string, f ratBinFun, a ratBinArg) { - x, _ := new(Rat).SetString(a.x) - y, _ := new(Rat).SetString(a.y) - z, _ := new(Rat).SetString(a.z) - out := f(new(Rat), x, y) - - if out.Cmp(z) != 0 { - t.Errorf("%s #%d got %s want %s", name, i, out, z) - } -} - -var ratBinTests = []struct { - x, y string - sum, prod string -}{ - {"0", "0", "0", "0"}, - {"0", "1", "1", "0"}, - {"-1", "0", "-1", "0"}, - {"-1", "1", "0", "-1"}, - {"1", "1", "2", "1"}, - {"1/2", "1/2", "1", "1/4"}, - {"1/4", "1/3", "7/12", "1/12"}, - {"2/5", "-14/3", "-64/15", "-28/15"}, - {"4707/49292519774798173060", "-3367/70976135186689855734", "84058377121001851123459/1749296273614329067191168098769082663020", "-1760941/388732505247628681598037355282018369560"}, - {"-61204110018146728334/3", "-31052192278051565633/2", "-215564796870448153567/6", "950260896245257153059642991192710872711/3"}, - {"-854857841473707320655/4237645934602118692642972629634714039", "-18/31750379913563777419", "-27/133467566250814981", "15387441146526731771790/134546868362786310073779084329032722548987800600710485341"}, - {"618575745270541348005638912139/19198433543745179392300736", "-19948846211000086/637313996471", "27674141753240653/30123979153216", "-6169936206128396568797607742807090270137721977/6117715203873571641674006593837351328"}, - {"-3/26206484091896184128", "5/2848423294177090248", "15310893822118706237/9330894968229805033368778458685147968", "-5/24882386581946146755650075889827061248"}, - {"26946729/330400702820", "41563965/225583428284", "1238218672302860271/4658307703098666660055", "224002580204097/14906584649915733312176"}, - {"-8259900599013409474/7", "-84829337473700364773/56707961321161574960", "-468402123685491748914621885145127724451/396955729248131024720", "350340947706464153265156004876107029701/198477864624065512360"}, - {"575775209696864/1320203974639986246357", "29/712593081308", "410331716733912717985762465/940768218243776489278275419794956", "808/45524274987585732633"}, - {"1786597389946320496771/2066653520653241", "6269770/1992362624741777", "3559549865190272133656109052308126637/4117523232840525481453983149257", "8967230/3296219033"}, - {"-36459180403360509753/32150500941194292113930", "9381566963714/9633539", "301622077145533298008420642898530153/309723104686531919656937098270", "-3784609207827/3426986245"}, -} - -func TestRatBin(t *testing.T) { - for i, test := range ratBinTests { - arg := ratBinArg{test.x, test.y, test.sum} - testRatBin(t, i, "Add", (*Rat).Add, arg) - - arg = ratBinArg{test.y, test.x, test.sum} - testRatBin(t, i, "Add symmetric", (*Rat).Add, arg) - - arg = ratBinArg{test.sum, test.x, test.y} - testRatBin(t, i, "Sub", (*Rat).Sub, arg) - - arg = ratBinArg{test.sum, test.y, test.x} - testRatBin(t, i, "Sub symmetric", (*Rat).Sub, arg) - - arg = ratBinArg{test.x, test.y, test.prod} - testRatBin(t, i, "Mul", (*Rat).Mul, arg) - - arg = ratBinArg{test.y, test.x, test.prod} - testRatBin(t, i, "Mul symmetric", (*Rat).Mul, arg) - - if test.x != "0" { - arg = ratBinArg{test.prod, test.x, test.y} - testRatBin(t, i, "Quo", (*Rat).Quo, arg) - } - - if test.y != "0" { - arg = ratBinArg{test.prod, test.y, test.x} - testRatBin(t, i, "Quo symmetric", (*Rat).Quo, arg) - } - } -} - -func TestIssue820(t *testing.T) { - x := NewRat(3, 1) - y := NewRat(2, 1) - z := y.Quo(x, y) - q := NewRat(3, 2) - if z.Cmp(q) != 0 { - t.Errorf("got %s want %s", z, q) - } - - y = NewRat(3, 1) - x = NewRat(2, 1) - z = y.Quo(x, y) - q = NewRat(2, 3) - if z.Cmp(q) != 0 { - t.Errorf("got %s want %s", z, q) - } - - x = NewRat(3, 1) - z = x.Quo(x, x) - q = NewRat(3, 3) - if z.Cmp(q) != 0 { - t.Errorf("got %s want %s", z, q) - } -} - -var setFrac64Tests = []struct { - a, b int64 - out string -}{ - {0, 1, "0"}, - {0, -1, "0"}, - {1, 1, "1"}, - {-1, 1, "-1"}, - {1, -1, "-1"}, - {-1, -1, "1"}, - {-9223372036854775808, -9223372036854775808, "1"}, -} - -func TestRatSetFrac64Rat(t *testing.T) { - for i, test := range setFrac64Tests { - x := new(Rat).SetFrac64(test.a, test.b) - if x.RatString() != test.out { - t.Errorf("#%d got %s want %s", i, x.RatString(), test.out) - } - } -} - -func TestRatGobEncoding(t *testing.T) { - var medium bytes.Buffer - enc := gob.NewEncoder(&medium) - dec := gob.NewDecoder(&medium) - for _, test := range encodingTests { - medium.Reset() // empty buffer for each test case (in case of failures) - var tx Rat - tx.SetString(test + ".14159265") - if err := enc.Encode(&tx); err != nil { - t.Errorf("encoding of %s failed: %s", &tx, err) - } - var rx Rat - if err := dec.Decode(&rx); err != nil { - t.Errorf("decoding of %s failed: %s", &tx, err) - } - if rx.Cmp(&tx) != 0 { - t.Errorf("transmission of %s failed: got %s want %s", &tx, &rx, &tx) - } - } -} - -// Sending a nil Rat pointer (inside a slice) on a round trip through gob should yield a zero. -// TODO: top-level nils. -func TestGobEncodingNilRatInSlice(t *testing.T) { - buf := new(bytes.Buffer) - enc := gob.NewEncoder(buf) - dec := gob.NewDecoder(buf) - - var in = make([]*Rat, 1) - err := enc.Encode(&in) - if err != nil { - t.Errorf("gob encode failed: %q", err) - } - var out []*Rat - err = dec.Decode(&out) - if err != nil { - t.Fatalf("gob decode failed: %q", err) - } - if len(out) != 1 { - t.Fatalf("wrong len; want 1 got %d", len(out)) - } - var zero Rat - if out[0].Cmp(&zero) != 0 { - t.Errorf("transmission of (*Int)(nill) failed: got %s want 0", out) - } -} - -var ratNums = []string{ - "-141592653589793238462643383279502884197169399375105820974944592307816406286", - "-1415926535897932384626433832795028841971", - "-141592653589793", - "-1", - "0", - "1", - "141592653589793", - "1415926535897932384626433832795028841971", - "141592653589793238462643383279502884197169399375105820974944592307816406286", -} - -var ratDenoms = []string{ - "1", - "718281828459045", - "7182818284590452353602874713526624977572", - "718281828459045235360287471352662497757247093699959574966967627724076630353", -} - -func TestRatJSONEncoding(t *testing.T) { - for _, num := range ratNums { - for _, denom := range ratDenoms { - var tx Rat - tx.SetString(num + "/" + denom) - b, err := json.Marshal(&tx) - if err != nil { - t.Errorf("marshaling of %s failed: %s", &tx, err) - continue - } - var rx Rat - if err := json.Unmarshal(b, &rx); err != nil { - t.Errorf("unmarshaling of %s failed: %s", &tx, err) - continue - } - if rx.Cmp(&tx) != 0 { - t.Errorf("JSON encoding of %s failed: got %s want %s", &tx, &rx, &tx) - } - } - } -} - -func TestRatXMLEncoding(t *testing.T) { - for _, num := range ratNums { - for _, denom := range ratDenoms { - var tx Rat - tx.SetString(num + "/" + denom) - b, err := xml.Marshal(&tx) - if err != nil { - t.Errorf("marshaling of %s failed: %s", &tx, err) - continue - } - var rx Rat - if err := xml.Unmarshal(b, &rx); err != nil { - t.Errorf("unmarshaling of %s failed: %s", &tx, err) - continue - } - if rx.Cmp(&tx) != 0 { - t.Errorf("XML encoding of %s failed: got %s want %s", &tx, &rx, &tx) - } - } - } -} - -func TestIssue2379(t *testing.T) { - // 1) no aliasing - q := NewRat(3, 2) - x := new(Rat) - x.SetFrac(NewInt(3), NewInt(2)) - if x.Cmp(q) != 0 { - t.Errorf("1) got %s want %s", x, q) - } - - // 2) aliasing of numerator - x = NewRat(2, 3) - x.SetFrac(NewInt(3), x.Num()) - if x.Cmp(q) != 0 { - t.Errorf("2) got %s want %s", x, q) - } - - // 3) aliasing of denominator - x = NewRat(2, 3) - x.SetFrac(x.Denom(), NewInt(2)) - if x.Cmp(q) != 0 { - t.Errorf("3) got %s want %s", x, q) - } - - // 4) aliasing of numerator and denominator - x = NewRat(2, 3) - x.SetFrac(x.Denom(), x.Num()) - if x.Cmp(q) != 0 { - t.Errorf("4) got %s want %s", x, q) - } - - // 5) numerator and denominator are the same - q = NewRat(1, 1) - x = new(Rat) - n := NewInt(7) - x.SetFrac(n, n) - if x.Cmp(q) != 0 { - t.Errorf("5) got %s want %s", x, q) - } -} - -func TestIssue3521(t *testing.T) { - a := new(Int) - b := new(Int) - a.SetString("64375784358435883458348587", 0) - b.SetString("4789759874531", 0) - - // 0) a raw zero value has 1 as denominator - zero := new(Rat) - one := NewInt(1) - if zero.Denom().Cmp(one) != 0 { - t.Errorf("0) got %s want %s", zero.Denom(), one) - } - - // 1a) a zero value remains zero independent of denominator - x := new(Rat) - x.Denom().Set(new(Int).Neg(b)) - if x.Cmp(zero) != 0 { - t.Errorf("1a) got %s want %s", x, zero) - } - - // 1b) a zero value may have a denominator != 0 and != 1 - x.Num().Set(a) - qab := new(Rat).SetFrac(a, b) - if x.Cmp(qab) != 0 { - t.Errorf("1b) got %s want %s", x, qab) - } - - // 2a) an integral value becomes a fraction depending on denominator - x.SetFrac64(10, 2) - x.Denom().SetInt64(3) - q53 := NewRat(5, 3) - if x.Cmp(q53) != 0 { - t.Errorf("2a) got %s want %s", x, q53) - } - - // 2b) an integral value becomes a fraction depending on denominator - x = NewRat(10, 2) - x.Denom().SetInt64(3) - if x.Cmp(q53) != 0 { - t.Errorf("2b) got %s want %s", x, q53) - } - - // 3) changing the numerator/denominator of a Rat changes the Rat - x.SetFrac(a, b) - a = x.Num() - b = x.Denom() - a.SetInt64(5) - b.SetInt64(3) - if x.Cmp(q53) != 0 { - t.Errorf("3) got %s want %s", x, q53) - } -} - -func TestFloat32Distribution(t *testing.T) { - // Generate a distribution of (sign, mantissa, exp) values - // broader than the float32 range, and check Rat.Float32() - // always picks the closest float32 approximation. - var add = []int64{ - 0, - 1, - 3, - 5, - 7, - 9, - 11, - } - var winc, einc = uint64(1), 1 // soak test (~1.5s on x86-64) - if testing.Short() { - winc, einc = 5, 15 // quick test (~60ms on x86-64) - } - - for _, sign := range "+-" { - for _, a := range add { - for wid := uint64(0); wid < 30; wid += winc { - b := 1< 0 { - num.Lsh(num, uint(exp)) - } else { - den.Lsh(den, uint(-exp)) - } - r := new(Rat).SetFrac(num, den) - f, _ := r.Float32() - - if !checkIsBestApprox32(t, f, r) { - // Append context information. - t.Errorf("(input was mantissa %#x, exp %d; f = %g (%b); f ~ %g; r = %v)", - b, exp, f, f, math.Ldexp(float64(b), exp), r) - } - - checkNonLossyRoundtrip32(t, f) - } - } - } - } -} - -func TestFloat64Distribution(t *testing.T) { - // Generate a distribution of (sign, mantissa, exp) values - // broader than the float64 range, and check Rat.Float64() - // always picks the closest float64 approximation. - var add = []int64{ - 0, - 1, - 3, - 5, - 7, - 9, - 11, - } - var winc, einc = uint64(1), 1 // soak test (~75s on x86-64) - if testing.Short() { - winc, einc = 10, 500 // quick test (~12ms on x86-64) - } - - for _, sign := range "+-" { - for _, a := range add { - for wid := uint64(0); wid < 60; wid += winc { - b := 1< 0 { - num.Lsh(num, uint(exp)) - } else { - den.Lsh(den, uint(-exp)) - } - r := new(Rat).SetFrac(num, den) - f, _ := r.Float64() - - if !checkIsBestApprox64(t, f, r) { - // Append context information. - t.Errorf("(input was mantissa %#x, exp %d; f = %g (%b); f ~ %g; r = %v)", - b, exp, f, f, math.Ldexp(float64(b), exp), r) - } - - checkNonLossyRoundtrip64(t, f) - } - } - } - } -} - -// TestSetFloat64NonFinite checks that SetFloat64 of a non-finite value -// returns nil. -func TestSetFloat64NonFinite(t *testing.T) { - for _, f := range []float64{math.NaN(), math.Inf(+1), math.Inf(-1)} { - var r Rat - if r2 := r.SetFloat64(f); r2 != nil { - t.Errorf("SetFloat64(%g) was %v, want nil", f, r2) - } - } -} - -// checkNonLossyRoundtrip32 checks that a float->Rat->float roundtrip is -// non-lossy for finite f. -func checkNonLossyRoundtrip32(t *testing.T, f float32) { - if !isFinite(float64(f)) { - return - } - r := new(Rat).SetFloat64(float64(f)) - if r == nil { - t.Errorf("Rat.SetFloat64(float64(%g) (%b)) == nil", f, f) - return - } - f2, exact := r.Float32() - if f != f2 || !exact { - t.Errorf("Rat.SetFloat64(float64(%g)).Float32() = %g (%b), %v, want %g (%b), %v; delta = %b", - f, f2, f2, exact, f, f, true, f2-f) - } -} - -// checkNonLossyRoundtrip64 checks that a float->Rat->float roundtrip is -// non-lossy for finite f. -func checkNonLossyRoundtrip64(t *testing.T, f float64) { - if !isFinite(f) { - return - } - r := new(Rat).SetFloat64(f) - if r == nil { - t.Errorf("Rat.SetFloat64(%g (%b)) == nil", f, f) - return - } - f2, exact := r.Float64() - if f != f2 || !exact { - t.Errorf("Rat.SetFloat64(%g).Float64() = %g (%b), %v, want %g (%b), %v; delta = %b", - f, f2, f2, exact, f, f, true, f2-f) - } -} - -// delta returns the absolute difference between r and f. -func delta(r *Rat, f float64) *Rat { - d := new(Rat).Sub(r, new(Rat).SetFloat64(f)) - return d.Abs(d) -} - -// checkIsBestApprox32 checks that f is the best possible float32 -// approximation of r. -// Returns true on success. -func checkIsBestApprox32(t *testing.T, f float32, r *Rat) bool { - if math.Abs(float64(f)) >= math.MaxFloat32 { - // Cannot check +Inf, -Inf, nor the float next to them (MaxFloat32). - // But we have tests for these special cases. - return true - } - - // r must be strictly between f0 and f1, the floats bracketing f. - f0 := math.Nextafter32(f, float32(math.Inf(-1))) - f1 := math.Nextafter32(f, float32(math.Inf(+1))) - - // For f to be correct, r must be closer to f than to f0 or f1. - df := delta(r, float64(f)) - df0 := delta(r, float64(f0)) - df1 := delta(r, float64(f1)) - if df.Cmp(df0) > 0 { - t.Errorf("Rat(%v).Float32() = %g (%b), but previous float32 %g (%b) is closer", r, f, f, f0, f0) - return false - } - if df.Cmp(df1) > 0 { - t.Errorf("Rat(%v).Float32() = %g (%b), but next float32 %g (%b) is closer", r, f, f, f1, f1) - return false - } - if df.Cmp(df0) == 0 && !isEven32(f) { - t.Errorf("Rat(%v).Float32() = %g (%b); halfway should have rounded to %g (%b) instead", r, f, f, f0, f0) - return false - } - if df.Cmp(df1) == 0 && !isEven32(f) { - t.Errorf("Rat(%v).Float32() = %g (%b); halfway should have rounded to %g (%b) instead", r, f, f, f1, f1) - return false - } - return true -} - -// checkIsBestApprox64 checks that f is the best possible float64 -// approximation of r. -// Returns true on success. -func checkIsBestApprox64(t *testing.T, f float64, r *Rat) bool { - if math.Abs(f) >= math.MaxFloat64 { - // Cannot check +Inf, -Inf, nor the float next to them (MaxFloat64). - // But we have tests for these special cases. - return true - } - - // r must be strictly between f0 and f1, the floats bracketing f. - f0 := math.Nextafter(f, math.Inf(-1)) - f1 := math.Nextafter(f, math.Inf(+1)) - - // For f to be correct, r must be closer to f than to f0 or f1. - df := delta(r, f) - df0 := delta(r, f0) - df1 := delta(r, f1) - if df.Cmp(df0) > 0 { - t.Errorf("Rat(%v).Float64() = %g (%b), but previous float64 %g (%b) is closer", r, f, f, f0, f0) - return false - } - if df.Cmp(df1) > 0 { - t.Errorf("Rat(%v).Float64() = %g (%b), but next float64 %g (%b) is closer", r, f, f, f1, f1) - return false - } - if df.Cmp(df0) == 0 && !isEven64(f) { - t.Errorf("Rat(%v).Float64() = %g (%b); halfway should have rounded to %g (%b) instead", r, f, f, f0, f0) - return false - } - if df.Cmp(df1) == 0 && !isEven64(f) { - t.Errorf("Rat(%v).Float64() = %g (%b); halfway should have rounded to %g (%b) instead", r, f, f, f1, f1) - return false - } - return true -} - -func isEven32(f float32) bool { return math.Float32bits(f)&1 == 0 } -func isEven64(f float64) bool { return math.Float64bits(f)&1 == 0 } - -func TestIsFinite(t *testing.T) { - finites := []float64{ - 1.0 / 3, - 4891559871276714924261e+222, - math.MaxFloat64, - math.SmallestNonzeroFloat64, - -math.MaxFloat64, - -math.SmallestNonzeroFloat64, - } - for _, f := range finites { - if !isFinite(f) { - t.Errorf("!IsFinite(%g (%b))", f, f) - } - } - nonfinites := []float64{ - math.NaN(), - math.Inf(-1), - math.Inf(+1), - } - for _, f := range nonfinites { - if isFinite(f) { - t.Errorf("IsFinite(%g, (%b))", f, f) - } - } -} diff --git a/src/cmd/internal/gc/big/ratconv.go b/src/cmd/internal/gc/big/ratconv.go deleted file mode 100644 index 778077b96e..0000000000 --- a/src/cmd/internal/gc/big/ratconv.go +++ /dev/null @@ -1,251 +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. - -// This file implements rat-to-string conversion functions. - -package big - -import ( - "errors" - "fmt" - "io" - "strconv" - "strings" -) - -func ratTok(ch rune) bool { - return strings.IndexRune("+-/0123456789.eE", ch) >= 0 -} - -// Scan is a support routine for fmt.Scanner. It accepts the formats -// 'e', 'E', 'f', 'F', 'g', 'G', and 'v'. All formats are equivalent. -func (z *Rat) Scan(s fmt.ScanState, ch rune) error { - tok, err := s.Token(true, ratTok) - if err != nil { - return err - } - if strings.IndexRune("efgEFGv", ch) < 0 { - return errors.New("Rat.Scan: invalid verb") - } - if _, ok := z.SetString(string(tok)); !ok { - return errors.New("Rat.Scan: invalid syntax") - } - return nil -} - -// SetString sets z to the value of s and returns z and a boolean indicating -// success. s can be given as a fraction "a/b" or as a floating-point number -// optionally followed by an exponent. If the operation failed, the value of -// z is undefined but the returned value is nil. -func (z *Rat) SetString(s string) (*Rat, bool) { - if len(s) == 0 { - return nil, false - } - // len(s) > 0 - - // parse fraction a/b, if any - if sep := strings.Index(s, "/"); sep >= 0 { - if _, ok := z.a.SetString(s[:sep], 0); !ok { - return nil, false - } - s = s[sep+1:] - var err error - if z.b.abs, _, _, err = z.b.abs.scan(strings.NewReader(s), 0, false); err != nil { - return nil, false - } - if len(z.b.abs) == 0 { - return nil, false - } - return z.norm(), true - } - - // parse floating-point number - r := strings.NewReader(s) - - // sign - neg, err := scanSign(r) - if err != nil { - return nil, false - } - - // mantissa - var ecorr int - z.a.abs, _, ecorr, err = z.a.abs.scan(r, 10, true) - if err != nil { - return nil, false - } - - // exponent - var exp int64 - exp, _, err = scanExponent(r, false) - if err != nil { - return nil, false - } - - // there should be no unread characters left - if _, err = r.ReadByte(); err != io.EOF { - return nil, false - } - - // correct exponent - if ecorr < 0 { - exp += int64(ecorr) - } - - // compute exponent power - expabs := exp - if expabs < 0 { - expabs = -expabs - } - powTen := nat(nil).expNN(natTen, nat(nil).setWord(Word(expabs)), nil) - - // complete fraction - if exp < 0 { - z.b.abs = powTen - z.norm() - } else { - z.a.abs = z.a.abs.mul(z.a.abs, powTen) - z.b.abs = z.b.abs[:0] - } - - z.a.neg = neg && len(z.a.abs) > 0 // 0 has no sign - - return z, true -} - -// scanExponent scans the longest possible prefix of r representing a decimal -// ('e', 'E') or binary ('p') exponent, if any. It returns the exponent, the -// exponent base (10 or 2), or a read or syntax error, if any. -// -// exponent = ( "E" | "e" | "p" ) [ sign ] digits . -// sign = "+" | "-" . -// digits = digit { digit } . -// digit = "0" ... "9" . -// -// A binary exponent is only permitted if binExpOk is set. -func scanExponent(r io.ByteScanner, binExpOk bool) (exp int64, base int, err error) { - base = 10 - - var ch byte - if ch, err = r.ReadByte(); err != nil { - if err == io.EOF { - err = nil // no exponent; same as e0 - } - return - } - - switch ch { - case 'e', 'E': - // ok - case 'p': - if binExpOk { - base = 2 - break // ok - } - fallthrough // binary exponent not permitted - default: - r.UnreadByte() - return // no exponent; same as e0 - } - - var neg bool - if neg, err = scanSign(r); err != nil { - return - } - - var digits []byte - if neg { - digits = append(digits, '-') - } - - // no need to use nat.scan for exponent digits - // since we only care about int64 values - the - // from-scratch scan is easy enough and faster - for i := 0; ; i++ { - if ch, err = r.ReadByte(); err != nil { - if err != io.EOF || i == 0 { - return - } - err = nil - break // i > 0 - } - if ch < '0' || '9' < ch { - if i == 0 { - r.UnreadByte() - err = fmt.Errorf("invalid exponent (missing digits)") - return - } - break // i > 0 - } - digits = append(digits, byte(ch)) - } - // i > 0 => we have at least one digit - - exp, err = strconv.ParseInt(string(digits), 10, 64) - return -} - -// String returns a string representation of x in the form "a/b" (even if b == 1). -func (x *Rat) String() string { - s := "/1" - if len(x.b.abs) != 0 { - s = "/" + x.b.abs.decimalString() - } - return x.a.String() + s -} - -// RatString returns a string representation of x in the form "a/b" if b != 1, -// and in the form "a" if b == 1. -func (x *Rat) RatString() string { - if x.IsInt() { - return x.a.String() - } - return x.String() -} - -// FloatString returns a string representation of x in decimal form with prec -// digits of precision after the decimal point and the last digit rounded. -func (x *Rat) FloatString(prec int) string { - if x.IsInt() { - s := x.a.String() - if prec > 0 { - s += "." + strings.Repeat("0", prec) - } - return s - } - // x.b.abs != 0 - - q, r := nat(nil).div(nat(nil), x.a.abs, x.b.abs) - - p := natOne - if prec > 0 { - p = nat(nil).expNN(natTen, nat(nil).setUint64(uint64(prec)), nil) - } - - r = r.mul(r, p) - r, r2 := r.div(nat(nil), r, x.b.abs) - - // see if we need to round up - r2 = r2.add(r2, r2) - if x.b.abs.cmp(r2) <= 0 { - r = r.add(r, natOne) - if r.cmp(p) >= 0 { - q = nat(nil).add(q, natOne) - r = nat(nil).sub(r, p) - } - } - - s := q.decimalString() - if x.a.neg { - s = "-" + s - } - - if prec > 0 { - rs := r.decimalString() - leadingZeros := prec - len(rs) - s += "." + strings.Repeat("0", leadingZeros) + rs - } - - return s -} diff --git a/src/cmd/internal/gc/big/ratconv_test.go b/src/cmd/internal/gc/big/ratconv_test.go deleted file mode 100644 index 16b3a19418..0000000000 --- a/src/cmd/internal/gc/big/ratconv_test.go +++ /dev/null @@ -1,451 +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 big - -import ( - "bytes" - "fmt" - "math" - "strconv" - "strings" - "testing" -) - -type StringTest struct { - in, out string - ok bool -} - -var setStringTests = []StringTest{ - {"0", "0", true}, - {"-0", "0", true}, - {"1", "1", true}, - {"-1", "-1", true}, - {"1.", "1", true}, - {"1e0", "1", true}, - {"1.e1", "10", true}, - {in: "1e"}, - {in: "1.e"}, - {in: "1e+14e-5"}, - {in: "1e4.5"}, - {in: "r"}, - {in: "a/b"}, - {in: "a.b"}, - {"-0.1", "-1/10", true}, - {"-.1", "-1/10", true}, - {"2/4", "1/2", true}, - {".25", "1/4", true}, - {"-1/5", "-1/5", true}, - {"8129567.7690E14", "812956776900000000000", true}, - {"78189e+4", "781890000", true}, - {"553019.8935e+8", "55301989350000", true}, - {"98765432109876543210987654321e-10", "98765432109876543210987654321/10000000000", true}, - {"9877861857500000E-7", "3951144743/4", true}, - {"2169378.417e-3", "2169378417/1000000", true}, - {"884243222337379604041632732738665534", "884243222337379604041632732738665534", true}, - {"53/70893980658822810696", "53/70893980658822810696", true}, - {"106/141787961317645621392", "53/70893980658822810696", true}, - {"204211327800791583.81095", "4084226556015831676219/20000", true}, - {in: "1/0"}, -} - -// These are not supported by fmt.Fscanf. -var setStringTests2 = []StringTest{ - {"0x10", "16", true}, - {"-010/1", "-8", true}, // TODO(gri) should we even permit octal here? - {"-010.", "-10", true}, - {"0x10/0x20", "1/2", true}, - {"0b1000/3", "8/3", true}, - // TODO(gri) add more tests -} - -func TestRatSetString(t *testing.T) { - var tests []StringTest - tests = append(tests, setStringTests...) - tests = append(tests, setStringTests2...) - - for i, test := range tests { - x, ok := new(Rat).SetString(test.in) - - if ok { - if !test.ok { - t.Errorf("#%d SetString(%q) expected failure", i, test.in) - } else if x.RatString() != test.out { - t.Errorf("#%d SetString(%q) got %s want %s", i, test.in, x.RatString(), test.out) - } - } else if x != nil { - t.Errorf("#%d SetString(%q) got %p want nil", i, test.in, x) - } - } -} - -func TestRatScan(t *testing.T) { - var buf bytes.Buffer - for i, test := range setStringTests { - x := new(Rat) - buf.Reset() - buf.WriteString(test.in) - - _, err := fmt.Fscanf(&buf, "%v", x) - if err == nil != test.ok { - if test.ok { - t.Errorf("#%d (%s) error: %s", i, test.in, err) - } else { - t.Errorf("#%d (%s) expected error", i, test.in) - } - continue - } - if err == nil && x.RatString() != test.out { - t.Errorf("#%d got %s want %s", i, x.RatString(), test.out) - } - } -} - -var floatStringTests = []struct { - in string - prec int - out string -}{ - {"0", 0, "0"}, - {"0", 4, "0.0000"}, - {"1", 0, "1"}, - {"1", 2, "1.00"}, - {"-1", 0, "-1"}, - {".25", 2, "0.25"}, - {".25", 1, "0.3"}, - {".25", 3, "0.250"}, - {"-1/3", 3, "-0.333"}, - {"-2/3", 4, "-0.6667"}, - {"0.96", 1, "1.0"}, - {"0.999", 2, "1.00"}, - {"0.9", 0, "1"}, - {".25", -1, "0"}, - {".55", -1, "1"}, -} - -func TestFloatString(t *testing.T) { - for i, test := range floatStringTests { - x, _ := new(Rat).SetString(test.in) - - if x.FloatString(test.prec) != test.out { - t.Errorf("#%d got %s want %s", i, x.FloatString(test.prec), test.out) - } - } -} - -// Test inputs to Rat.SetString. The prefix "long:" causes the test -// to be skipped in --test.short mode. (The threshold is about 500us.) -var float64inputs = []string{ - // Constants plundered from strconv/testfp.txt. - - // Table 1: Stress Inputs for Conversion to 53-bit Binary, < 1/2 ULP - "5e+125", - "69e+267", - "999e-026", - "7861e-034", - "75569e-254", - "928609e-261", - "9210917e+080", - "84863171e+114", - "653777767e+273", - "5232604057e-298", - "27235667517e-109", - "653532977297e-123", - "3142213164987e-294", - "46202199371337e-072", - "231010996856685e-073", - "9324754620109615e+212", - "78459735791271921e+049", - "272104041512242479e+200", - "6802601037806061975e+198", - "20505426358836677347e-221", - "836168422905420598437e-234", - "4891559871276714924261e+222", - - // Table 2: Stress Inputs for Conversion to 53-bit Binary, > 1/2 ULP - "9e-265", - "85e-037", - "623e+100", - "3571e+263", - "81661e+153", - "920657e-023", - "4603285e-024", - "87575437e-309", - "245540327e+122", - "6138508175e+120", - "83356057653e+193", - "619534293513e+124", - "2335141086879e+218", - "36167929443327e-159", - "609610927149051e-255", - "3743626360493413e-165", - "94080055902682397e-242", - "899810892172646163e+283", - "7120190517612959703e+120", - "25188282901709339043e-252", - "308984926168550152811e-052", - "6372891218502368041059e+064", - - // Table 14: Stress Inputs for Conversion to 24-bit Binary, <1/2 ULP - "5e-20", - "67e+14", - "985e+15", - "7693e-42", - "55895e-16", - "996622e-44", - "7038531e-32", - "60419369e-46", - "702990899e-20", - "6930161142e-48", - "25933168707e+13", - "596428896559e+20", - - // Table 15: Stress Inputs for Conversion to 24-bit Binary, >1/2 ULP - "3e-23", - "57e+18", - "789e-35", - "2539e-18", - "76173e+28", - "887745e-11", - "5382571e-37", - "82381273e-35", - "750486563e-38", - "3752432815e-39", - "75224575729e-45", - "459926601011e+15", - - // Constants plundered from strconv/atof_test.go. - - "0", - "1", - "+1", - "1e23", - "1E23", - "100000000000000000000000", - "1e-100", - "123456700", - "99999999999999974834176", - "100000000000000000000001", - "100000000000000008388608", - "100000000000000016777215", - "100000000000000016777216", - "-1", - "-0.1", - "-0", // NB: exception made for this input - "1e-20", - "625e-3", - - // largest float64 - "1.7976931348623157e308", - "-1.7976931348623157e308", - // next float64 - too large - "1.7976931348623159e308", - "-1.7976931348623159e308", - // the border is ...158079 - // borderline - okay - "1.7976931348623158e308", - "-1.7976931348623158e308", - // borderline - too large - "1.797693134862315808e308", - "-1.797693134862315808e308", - - // a little too large - "1e308", - "2e308", - "1e309", - - // way too large - "1e310", - "-1e310", - "1e400", - "-1e400", - "long:1e400000", - "long:-1e400000", - - // denormalized - "1e-305", - "1e-306", - "1e-307", - "1e-308", - "1e-309", - "1e-310", - "1e-322", - // smallest denormal - "5e-324", - "4e-324", - "3e-324", - // too small - "2e-324", - // way too small - "1e-350", - "long:1e-400000", - // way too small, negative - "-1e-350", - "long:-1e-400000", - - // try to overflow exponent - // [Disabled: too slow and memory-hungry with rationals.] - // "1e-4294967296", - // "1e+4294967296", - // "1e-18446744073709551616", - // "1e+18446744073709551616", - - // http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/ - "2.2250738585072012e-308", - // http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/ - "2.2250738585072011e-308", - - // A very large number (initially wrongly parsed by the fast algorithm). - "4.630813248087435e+307", - - // A different kind of very large number. - "22.222222222222222", - "long:2." + strings.Repeat("2", 4000) + "e+1", - - // Exactly halfway between 1 and math.Nextafter(1, 2). - // Round to even (down). - "1.00000000000000011102230246251565404236316680908203125", - // Slightly lower; still round down. - "1.00000000000000011102230246251565404236316680908203124", - // Slightly higher; round up. - "1.00000000000000011102230246251565404236316680908203126", - // Slightly higher, but you have to read all the way to the end. - "long:1.00000000000000011102230246251565404236316680908203125" + strings.Repeat("0", 10000) + "1", - - // Smallest denormal, 2^(-1022-52) - "4.940656458412465441765687928682213723651e-324", - // Half of smallest denormal, 2^(-1022-53) - "2.470328229206232720882843964341106861825e-324", - // A little more than the exact half of smallest denormal - // 2^-1075 + 2^-1100. (Rounds to 1p-1074.) - "2.470328302827751011111470718709768633275e-324", - // The exact halfway between smallest normal and largest denormal: - // 2^-1022 - 2^-1075. (Rounds to 2^-1022.) - "2.225073858507201136057409796709131975935e-308", - - "1152921504606846975", // 1<<60 - 1 - "-1152921504606846975", // -(1<<60 - 1) - "1152921504606846977", // 1<<60 + 1 - "-1152921504606846977", // -(1<<60 + 1) - - "1/3", -} - -// isFinite reports whether f represents a finite rational value. -// It is equivalent to !math.IsNan(f) && !math.IsInf(f, 0). -func isFinite(f float64) bool { - return math.Abs(f) <= math.MaxFloat64 -} - -func TestFloat32SpecialCases(t *testing.T) { - for _, input := range float64inputs { - if strings.HasPrefix(input, "long:") { - if testing.Short() { - continue - } - input = input[len("long:"):] - } - - r, ok := new(Rat).SetString(input) - if !ok { - t.Errorf("Rat.SetString(%q) failed", input) - continue - } - f, exact := r.Float32() - - // 1. Check string -> Rat -> float32 conversions are - // consistent with strconv.ParseFloat. - // Skip this check if the input uses "a/b" rational syntax. - if !strings.Contains(input, "/") { - e64, _ := strconv.ParseFloat(input, 32) - e := float32(e64) - - // Careful: negative Rats too small for - // float64 become -0, but Rat obviously cannot - // preserve the sign from SetString("-0"). - switch { - case math.Float32bits(e) == math.Float32bits(f): - // Ok: bitwise equal. - case f == 0 && r.Num().BitLen() == 0: - // Ok: Rat(0) is equivalent to both +/- float64(0). - default: - t.Errorf("strconv.ParseFloat(%q) = %g (%b), want %g (%b); delta = %g", input, e, e, f, f, f-e) - } - } - - if !isFinite(float64(f)) { - continue - } - - // 2. Check f is best approximation to r. - if !checkIsBestApprox32(t, f, r) { - // Append context information. - t.Errorf("(input was %q)", input) - } - - // 3. Check f->R->f roundtrip is non-lossy. - checkNonLossyRoundtrip32(t, f) - - // 4. Check exactness using slow algorithm. - if wasExact := new(Rat).SetFloat64(float64(f)).Cmp(r) == 0; wasExact != exact { - t.Errorf("Rat.SetString(%q).Float32().exact = %t, want %t", input, exact, wasExact) - } - } -} - -func TestFloat64SpecialCases(t *testing.T) { - for _, input := range float64inputs { - if strings.HasPrefix(input, "long:") { - if testing.Short() { - continue - } - input = input[len("long:"):] - } - - r, ok := new(Rat).SetString(input) - if !ok { - t.Errorf("Rat.SetString(%q) failed", input) - continue - } - f, exact := r.Float64() - - // 1. Check string -> Rat -> float64 conversions are - // consistent with strconv.ParseFloat. - // Skip this check if the input uses "a/b" rational syntax. - if !strings.Contains(input, "/") { - e, _ := strconv.ParseFloat(input, 64) - - // Careful: negative Rats too small for - // float64 become -0, but Rat obviously cannot - // preserve the sign from SetString("-0"). - switch { - case math.Float64bits(e) == math.Float64bits(f): - // Ok: bitwise equal. - case f == 0 && r.Num().BitLen() == 0: - // Ok: Rat(0) is equivalent to both +/- float64(0). - default: - t.Errorf("strconv.ParseFloat(%q) = %g (%b), want %g (%b); delta = %g", input, e, e, f, f, f-e) - } - } - - if !isFinite(f) { - continue - } - - // 2. Check f is best approximation to r. - if !checkIsBestApprox64(t, f, r) { - // Append context information. - t.Errorf("(input was %q)", input) - } - - // 3. Check f->R->f roundtrip is non-lossy. - checkNonLossyRoundtrip64(t, f) - - // 4. Check exactness using slow algorithm. - if wasExact := new(Rat).SetFloat64(f).Cmp(r) == 0; wasExact != exact { - t.Errorf("Rat.SetString(%q).Float64().exact = %t, want %t", input, exact, wasExact) - } - } -} diff --git a/src/cmd/internal/gc/big/roundingmode_string.go b/src/cmd/internal/gc/big/roundingmode_string.go deleted file mode 100644 index 05024b8065..0000000000 --- a/src/cmd/internal/gc/big/roundingmode_string.go +++ /dev/null @@ -1,16 +0,0 @@ -// generated by stringer -type=RoundingMode; DO NOT EDIT - -package big - -import "fmt" - -const _RoundingMode_name = "ToNearestEvenToNearestAwayToZeroAwayFromZeroToNegativeInfToPositiveInf" - -var _RoundingMode_index = [...]uint8{0, 13, 26, 32, 44, 57, 70} - -func (i RoundingMode) String() string { - if i+1 >= RoundingMode(len(_RoundingMode_index)) { - return fmt.Sprintf("RoundingMode(%d)", i) - } - return _RoundingMode_name[_RoundingMode_index[i]:_RoundingMode_index[i+1]] -} diff --git a/src/cmd/internal/gc/big/vendor.bash b/src/cmd/internal/gc/big/vendor.bash deleted file mode 100755 index 84aa750462..0000000000 --- a/src/cmd/internal/gc/big/vendor.bash +++ /dev/null @@ -1,25 +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. - -# Run this script to obtain an up-to-date vendored version of math/big. - -BIGDIR=../../../../math/big - -# Start from scratch. -rm *.go - -# We don't want any assembly files. -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 -rm arith_decl_pure.go - -# gofmt to clean up after sed -gofmt -w . - -# Test that it works -go test -short diff --git a/src/cmd/internal/gc/builtin.go b/src/cmd/internal/gc/builtin.go deleted file mode 100644 index 6bdf78c8ab..0000000000 --- a/src/cmd/internal/gc/builtin.go +++ /dev/null @@ -1,168 +0,0 @@ -// AUTO-GENERATED by mkbuiltin.go; DO NOT EDIT - -package gc - -const runtimeimport = "" + - "package runtime\n" + - "import runtime \"runtime\"\n" + - "func @\"\".newobject (@\"\".typ·2 *byte) (? *any)\n" + - "func @\"\".panicindex ()\n" + - "func @\"\".panicslice ()\n" + - "func @\"\".panicdivide ()\n" + - "func @\"\".throwreturn ()\n" + - "func @\"\".throwinit ()\n" + - "func @\"\".panicwrap (? string, ? string, ? string)\n" + - "func @\"\".gopanic (? interface {})\n" + - "func @\"\".gorecover (? *int32) (? interface {})\n" + - "func @\"\".printbool (? bool)\n" + - "func @\"\".printfloat (? float64)\n" + - "func @\"\".printint (? int64)\n" + - "func @\"\".printhex (? uint64)\n" + - "func @\"\".printuint (? uint64)\n" + - "func @\"\".printcomplex (? complex128)\n" + - "func @\"\".printstring (? string)\n" + - "func @\"\".printpointer (? any)\n" + - "func @\"\".printiface (? any)\n" + - "func @\"\".printeface (? any)\n" + - "func @\"\".printslice (? any)\n" + - "func @\"\".printnl ()\n" + - "func @\"\".printsp ()\n" + - "func @\"\".printlock ()\n" + - "func @\"\".printunlock ()\n" + - "func @\"\".concatstring2 (? *[32]byte, ? string, ? string) (? string)\n" + - "func @\"\".concatstring3 (? *[32]byte, ? string, ? string, ? string) (? string)\n" + - "func @\"\".concatstring4 (? *[32]byte, ? string, ? string, ? string, ? string) (? string)\n" + - "func @\"\".concatstring5 (? *[32]byte, ? string, ? string, ? string, ? string, ? string) (? string)\n" + - "func @\"\".concatstrings (? *[32]byte, ? []string) (? string)\n" + - "func @\"\".cmpstring (? string, ? string) (? int)\n" + - "func @\"\".eqstring (? string, ? string) (? bool)\n" + - "func @\"\".intstring (? *[4]byte, ? int64) (? string)\n" + - "func @\"\".slicebytetostring (? *[32]byte, ? []byte) (? string)\n" + - "func @\"\".slicebytetostringtmp (? []byte) (? string)\n" + - "func @\"\".slicerunetostring (? *[32]byte, ? []rune) (? string)\n" + - "func @\"\".stringtoslicebyte (? *[32]byte, ? string) (? []byte)\n" + - "func @\"\".stringtoslicebytetmp (? string) (? []byte)\n" + - "func @\"\".stringtoslicerune (? *[32]rune, ? string) (? []rune)\n" + - "func @\"\".stringiter (? string, ? int) (? int)\n" + - "func @\"\".stringiter2 (? string, ? int) (@\"\".retk·1 int, @\"\".retv·2 rune)\n" + - "func @\"\".slicecopy (@\"\".to·2 any, @\"\".fr·3 any, @\"\".wid·4 uintptr) (? int)\n" + - "func @\"\".slicestringcopy (@\"\".to·2 any, @\"\".fr·3 any) (? int)\n" + - "func @\"\".typ2Itab (@\"\".typ·2 *byte, @\"\".typ2·3 *byte, @\"\".cache·4 **byte) (@\"\".ret·1 *byte)\n" + - "func @\"\".convI2E (@\"\".elem·2 any) (@\"\".ret·1 any)\n" + - "func @\"\".convI2I (@\"\".typ·2 *byte, @\"\".elem·3 any) (@\"\".ret·1 any)\n" + - "func @\"\".convT2E (@\"\".typ·2 *byte, @\"\".elem·3 *any, @\"\".buf·4 *any) (@\"\".ret·1 any)\n" + - "func @\"\".convT2I (@\"\".typ·2 *byte, @\"\".typ2·3 *byte, @\"\".cache·4 **byte, @\"\".elem·5 *any, @\"\".buf·6 *any) (@\"\".ret·1 any)\n" + - "func @\"\".assertE2E (@\"\".typ·1 *byte, @\"\".iface·2 any, @\"\".ret·3 *any)\n" + - "func @\"\".assertE2E2 (@\"\".typ·2 *byte, @\"\".iface·3 any, @\"\".ret·4 *any) (? bool)\n" + - "func @\"\".assertE2I (@\"\".typ·1 *byte, @\"\".iface·2 any, @\"\".ret·3 *any)\n" + - "func @\"\".assertE2I2 (@\"\".typ·2 *byte, @\"\".iface·3 any, @\"\".ret·4 *any) (? bool)\n" + - "func @\"\".assertE2T (@\"\".typ·1 *byte, @\"\".iface·2 any, @\"\".ret·3 *any)\n" + - "func @\"\".assertE2T2 (@\"\".typ·2 *byte, @\"\".iface·3 any, @\"\".ret·4 *any) (? bool)\n" + - "func @\"\".assertI2E (@\"\".typ·1 *byte, @\"\".iface·2 any, @\"\".ret·3 *any)\n" + - "func @\"\".assertI2E2 (@\"\".typ·2 *byte, @\"\".iface·3 any, @\"\".ret·4 *any) (? bool)\n" + - "func @\"\".assertI2I (@\"\".typ·1 *byte, @\"\".iface·2 any, @\"\".ret·3 *any)\n" + - "func @\"\".assertI2I2 (@\"\".typ·2 *byte, @\"\".iface·3 any, @\"\".ret·4 *any) (? bool)\n" + - "func @\"\".assertI2T (@\"\".typ·1 *byte, @\"\".iface·2 any, @\"\".ret·3 *any)\n" + - "func @\"\".assertI2T2 (@\"\".typ·2 *byte, @\"\".iface·3 any, @\"\".ret·4 *any) (? bool)\n" + - "func @\"\".panicdottype (@\"\".have·1 *byte, @\"\".want·2 *byte, @\"\".iface·3 *byte)\n" + - "func @\"\".ifaceeq (@\"\".i1·2 any, @\"\".i2·3 any) (@\"\".ret·1 bool)\n" + - "func @\"\".efaceeq (@\"\".i1·2 any, @\"\".i2·3 any) (@\"\".ret·1 bool)\n" + - "func @\"\".ifacethash (@\"\".i1·2 any) (@\"\".ret·1 uint32)\n" + - "func @\"\".efacethash (@\"\".i1·2 any) (@\"\".ret·1 uint32)\n" + - "func @\"\".makemap (@\"\".mapType·2 *byte, @\"\".hint·3 int64, @\"\".mapbuf·4 *any, @\"\".bucketbuf·5 *any) (@\"\".hmap·1 map[any]any)\n" + - "func @\"\".mapaccess1 (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 *any) (@\"\".val·1 *any)\n" + - "func @\"\".mapaccess1_fast32 (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 any) (@\"\".val·1 *any)\n" + - "func @\"\".mapaccess1_fast64 (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 any) (@\"\".val·1 *any)\n" + - "func @\"\".mapaccess1_faststr (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 any) (@\"\".val·1 *any)\n" + - "func @\"\".mapaccess2 (@\"\".mapType·3 *byte, @\"\".hmap·4 map[any]any, @\"\".key·5 *any) (@\"\".val·1 *any, @\"\".pres·2 bool)\n" + - "func @\"\".mapaccess2_fast32 (@\"\".mapType·3 *byte, @\"\".hmap·4 map[any]any, @\"\".key·5 any) (@\"\".val·1 *any, @\"\".pres·2 bool)\n" + - "func @\"\".mapaccess2_fast64 (@\"\".mapType·3 *byte, @\"\".hmap·4 map[any]any, @\"\".key·5 any) (@\"\".val·1 *any, @\"\".pres·2 bool)\n" + - "func @\"\".mapaccess2_faststr (@\"\".mapType·3 *byte, @\"\".hmap·4 map[any]any, @\"\".key·5 any) (@\"\".val·1 *any, @\"\".pres·2 bool)\n" + - "func @\"\".mapassign1 (@\"\".mapType·1 *byte, @\"\".hmap·2 map[any]any, @\"\".key·3 *any, @\"\".val·4 *any)\n" + - "func @\"\".mapiterinit (@\"\".mapType·1 *byte, @\"\".hmap·2 map[any]any, @\"\".hiter·3 *any)\n" + - "func @\"\".mapdelete (@\"\".mapType·1 *byte, @\"\".hmap·2 map[any]any, @\"\".key·3 *any)\n" + - "func @\"\".mapiternext (@\"\".hiter·1 *any)\n" + - "func @\"\".makechan (@\"\".chanType·2 *byte, @\"\".hint·3 int64) (@\"\".hchan·1 chan any)\n" + - "func @\"\".chanrecv1 (@\"\".chanType·1 *byte, @\"\".hchan·2 <-chan any, @\"\".elem·3 *any)\n" + - "func @\"\".chanrecv2 (@\"\".chanType·2 *byte, @\"\".hchan·3 <-chan any, @\"\".elem·4 *any) (? bool)\n" + - "func @\"\".chansend1 (@\"\".chanType·1 *byte, @\"\".hchan·2 chan<- any, @\"\".elem·3 *any)\n" + - "func @\"\".closechan (@\"\".hchan·1 any)\n" + - "var @\"\".writeBarrierEnabled bool\n" + - "func @\"\".writebarrierptr (@\"\".dst·1 *any, @\"\".src·2 any)\n" + - "func @\"\".writebarrierstring (@\"\".dst·1 *any, @\"\".src·2 any)\n" + - "func @\"\".writebarrierslice (@\"\".dst·1 *any, @\"\".src·2 any)\n" + - "func @\"\".writebarrieriface (@\"\".dst·1 *any, @\"\".src·2 any)\n" + - "func @\"\".writebarrierfat01 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat10 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat11 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat001 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat010 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat011 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat100 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat101 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat110 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat111 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat0001 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat0010 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat0011 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat0100 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat0101 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat0110 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat0111 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat1000 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat1001 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat1010 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat1011 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat1100 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat1101 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat1110 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".writebarrierfat1111 (@\"\".dst·1 *any, _ uintptr, @\"\".src·3 any)\n" + - "func @\"\".typedmemmove (@\"\".typ·1 *byte, @\"\".dst·2 *any, @\"\".src·3 *any)\n" + - "func @\"\".typedslicecopy (@\"\".typ·2 *byte, @\"\".dst·3 any, @\"\".src·4 any) (? int)\n" + - "func @\"\".selectnbsend (@\"\".chanType·2 *byte, @\"\".hchan·3 chan<- any, @\"\".elem·4 *any) (? bool)\n" + - "func @\"\".selectnbrecv (@\"\".chanType·2 *byte, @\"\".elem·3 *any, @\"\".hchan·4 <-chan any) (? bool)\n" + - "func @\"\".selectnbrecv2 (@\"\".chanType·2 *byte, @\"\".elem·3 *any, @\"\".received·4 *bool, @\"\".hchan·5 <-chan any) (? bool)\n" + - "func @\"\".newselect (@\"\".sel·1 *byte, @\"\".selsize·2 int64, @\"\".size·3 int32)\n" + - "func @\"\".selectsend (@\"\".sel·2 *byte, @\"\".hchan·3 chan<- any, @\"\".elem·4 *any) (@\"\".selected·1 bool)\n" + - "func @\"\".selectrecv (@\"\".sel·2 *byte, @\"\".hchan·3 <-chan any, @\"\".elem·4 *any) (@\"\".selected·1 bool)\n" + - "func @\"\".selectrecv2 (@\"\".sel·2 *byte, @\"\".hchan·3 <-chan any, @\"\".elem·4 *any, @\"\".received·5 *bool) (@\"\".selected·1 bool)\n" + - "func @\"\".selectdefault (@\"\".sel·2 *byte) (@\"\".selected·1 bool)\n" + - "func @\"\".selectgo (@\"\".sel·1 *byte)\n" + - "func @\"\".block ()\n" + - "func @\"\".makeslice (@\"\".typ·2 *byte, @\"\".nel·3 int64, @\"\".cap·4 int64) (@\"\".ary·1 []any)\n" + - "func @\"\".growslice (@\"\".typ·2 *byte, @\"\".old·3 []any, @\"\".n·4 int) (@\"\".ary·1 []any)\n" + - "func @\"\".memmove (@\"\".to·1 *any, @\"\".frm·2 *any, @\"\".length·3 uintptr)\n" + - "func @\"\".memclr (@\"\".ptr·1 *byte, @\"\".length·2 uintptr)\n" + - "func @\"\".memequal (@\"\".x·2 *any, @\"\".y·3 *any, @\"\".size·4 uintptr) (? bool)\n" + - "func @\"\".memequal8 (@\"\".x·2 *any, @\"\".y·3 *any) (? bool)\n" + - "func @\"\".memequal16 (@\"\".x·2 *any, @\"\".y·3 *any) (? bool)\n" + - "func @\"\".memequal32 (@\"\".x·2 *any, @\"\".y·3 *any) (? bool)\n" + - "func @\"\".memequal64 (@\"\".x·2 *any, @\"\".y·3 *any) (? bool)\n" + - "func @\"\".memequal128 (@\"\".x·2 *any, @\"\".y·3 *any) (? bool)\n" + - "func @\"\".int64div (? int64, ? int64) (? int64)\n" + - "func @\"\".uint64div (? uint64, ? uint64) (? uint64)\n" + - "func @\"\".int64mod (? int64, ? int64) (? int64)\n" + - "func @\"\".uint64mod (? uint64, ? uint64) (? uint64)\n" + - "func @\"\".float64toint64 (? float64) (? int64)\n" + - "func @\"\".float64touint64 (? float64) (? uint64)\n" + - "func @\"\".int64tofloat64 (? int64) (? float64)\n" + - "func @\"\".uint64tofloat64 (? uint64) (? float64)\n" + - "func @\"\".complex128div (@\"\".num·2 complex128, @\"\".den·3 complex128) (@\"\".quo·1 complex128)\n" + - "func @\"\".racefuncenter (? uintptr)\n" + - "func @\"\".racefuncexit ()\n" + - "func @\"\".raceread (? uintptr)\n" + - "func @\"\".racewrite (? uintptr)\n" + - "func @\"\".racereadrange (@\"\".addr·1 uintptr, @\"\".size·2 uintptr)\n" + - "func @\"\".racewriterange (@\"\".addr·1 uintptr, @\"\".size·2 uintptr)\n" + - "\n" + - "$$\n" - -const unsafeimport = "" + - "package unsafe\n" + - "import runtime \"runtime\"\n" + - "type @\"\".Pointer uintptr\n" + - "func @\"\".Offsetof (? any) (? uintptr)\n" + - "func @\"\".Sizeof (? any) (? uintptr)\n" + - "func @\"\".Alignof (? any) (? uintptr)\n" + - "\n" + - "$$\n" diff --git a/src/cmd/internal/gc/builtin/runtime.go b/src/cmd/internal/gc/builtin/runtime.go deleted file mode 100644 index 179a4ddd9a..0000000000 --- a/src/cmd/internal/gc/builtin/runtime.go +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright 2009 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. - -// NOTE: If you change this file you must run "go generate" -// to update builtin.go. This is not done automatically -// to avoid depending on having a working compiler binary. - -// +build ignore - -package PACKAGE - -// emitted by compiler, not referred to by go programs - -func newobject(typ *byte) *any -func panicindex() -func panicslice() -func panicdivide() -func throwreturn() -func throwinit() -func panicwrap(string, string, string) - -func gopanic(interface{}) -func gorecover(*int32) interface{} - -func printbool(bool) -func printfloat(float64) -func printint(int64) -func printhex(uint64) -func printuint(uint64) -func printcomplex(complex128) -func printstring(string) -func printpointer(any) -func printiface(any) -func printeface(any) -func printslice(any) -func printnl() -func printsp() -func printlock() -func printunlock() - -func concatstring2(*[32]byte, string, string) string -func concatstring3(*[32]byte, string, string, string) string -func concatstring4(*[32]byte, string, string, string, string) string -func concatstring5(*[32]byte, string, string, string, string, string) string -func concatstrings(*[32]byte, []string) string - -func cmpstring(string, string) int -func eqstring(string, string) bool -func intstring(*[4]byte, int64) string -func slicebytetostring(*[32]byte, []byte) string -func slicebytetostringtmp([]byte) string -func slicerunetostring(*[32]byte, []rune) string -func stringtoslicebyte(*[32]byte, string) []byte -func stringtoslicebytetmp(string) []byte -func stringtoslicerune(*[32]rune, string) []rune -func stringiter(string, int) int -func stringiter2(string, int) (retk int, retv rune) -func slicecopy(to any, fr any, wid uintptr) int -func slicestringcopy(to any, fr any) int - -// interface conversions -func typ2Itab(typ *byte, typ2 *byte, cache **byte) (ret *byte) -func convI2E(elem any) (ret any) -func convI2I(typ *byte, elem any) (ret any) -func convT2E(typ *byte, elem, buf *any) (ret any) -func convT2I(typ *byte, typ2 *byte, cache **byte, elem, buf *any) (ret any) - -// interface type assertions x.(T) -func assertE2E(typ *byte, iface any, ret *any) -func assertE2E2(typ *byte, iface any, ret *any) bool -func assertE2I(typ *byte, iface any, ret *any) -func assertE2I2(typ *byte, iface any, ret *any) bool -func assertE2T(typ *byte, iface any, ret *any) -func assertE2T2(typ *byte, iface any, ret *any) bool -func assertI2E(typ *byte, iface any, ret *any) -func assertI2E2(typ *byte, iface any, ret *any) bool -func assertI2I(typ *byte, iface any, ret *any) -func assertI2I2(typ *byte, iface any, ret *any) bool -func assertI2T(typ *byte, iface any, ret *any) -func assertI2T2(typ *byte, iface any, ret *any) bool -func panicdottype(have, want, iface *byte) - -func ifaceeq(i1 any, i2 any) (ret bool) -func efaceeq(i1 any, i2 any) (ret bool) -func ifacethash(i1 any) (ret uint32) -func efacethash(i1 any) (ret uint32) - -// *byte is really *runtime.Type -func makemap(mapType *byte, hint int64, mapbuf *any, bucketbuf *any) (hmap map[any]any) -func mapaccess1(mapType *byte, hmap map[any]any, key *any) (val *any) -func mapaccess1_fast32(mapType *byte, hmap map[any]any, key any) (val *any) -func mapaccess1_fast64(mapType *byte, hmap map[any]any, key any) (val *any) -func mapaccess1_faststr(mapType *byte, hmap map[any]any, key any) (val *any) -func mapaccess2(mapType *byte, hmap map[any]any, key *any) (val *any, pres bool) -func mapaccess2_fast32(mapType *byte, hmap map[any]any, key any) (val *any, pres bool) -func mapaccess2_fast64(mapType *byte, hmap map[any]any, key any) (val *any, pres bool) -func mapaccess2_faststr(mapType *byte, hmap map[any]any, key any) (val *any, pres bool) -func mapassign1(mapType *byte, hmap map[any]any, key *any, val *any) -func mapiterinit(mapType *byte, hmap map[any]any, hiter *any) -func mapdelete(mapType *byte, hmap map[any]any, key *any) -func mapiternext(hiter *any) - -// *byte is really *runtime.Type -func makechan(chanType *byte, hint int64) (hchan chan any) -func chanrecv1(chanType *byte, hchan <-chan any, elem *any) -func chanrecv2(chanType *byte, hchan <-chan any, elem *any) bool -func chansend1(chanType *byte, hchan chan<- any, elem *any) -func closechan(hchan any) - -var writeBarrierEnabled bool - -func writebarrierptr(dst *any, src any) -func writebarrierstring(dst *any, src any) -func writebarrierslice(dst *any, src any) -func writebarrieriface(dst *any, src any) - -// The unused *byte argument makes sure that src is 2-pointer-aligned, -// which is the maximum alignment on NaCl amd64p32 -// (and possibly on 32-bit systems if we start 64-bit aligning uint64s). -// The bitmap in the name tells which words being copied are pointers. -func writebarrierfat01(dst *any, _ uintptr, src any) -func writebarrierfat10(dst *any, _ uintptr, src any) -func writebarrierfat11(dst *any, _ uintptr, src any) -func writebarrierfat001(dst *any, _ uintptr, src any) -func writebarrierfat010(dst *any, _ uintptr, src any) -func writebarrierfat011(dst *any, _ uintptr, src any) -func writebarrierfat100(dst *any, _ uintptr, src any) -func writebarrierfat101(dst *any, _ uintptr, src any) -func writebarrierfat110(dst *any, _ uintptr, src any) -func writebarrierfat111(dst *any, _ uintptr, src any) -func writebarrierfat0001(dst *any, _ uintptr, src any) -func writebarrierfat0010(dst *any, _ uintptr, src any) -func writebarrierfat0011(dst *any, _ uintptr, src any) -func writebarrierfat0100(dst *any, _ uintptr, src any) -func writebarrierfat0101(dst *any, _ uintptr, src any) -func writebarrierfat0110(dst *any, _ uintptr, src any) -func writebarrierfat0111(dst *any, _ uintptr, src any) -func writebarrierfat1000(dst *any, _ uintptr, src any) -func writebarrierfat1001(dst *any, _ uintptr, src any) -func writebarrierfat1010(dst *any, _ uintptr, src any) -func writebarrierfat1011(dst *any, _ uintptr, src any) -func writebarrierfat1100(dst *any, _ uintptr, src any) -func writebarrierfat1101(dst *any, _ uintptr, src any) -func writebarrierfat1110(dst *any, _ uintptr, src any) -func writebarrierfat1111(dst *any, _ uintptr, src any) - -// *byte is really *runtime.Type -func typedmemmove(typ *byte, dst *any, src *any) -func typedslicecopy(typ *byte, dst any, src any) int - -func selectnbsend(chanType *byte, hchan chan<- any, elem *any) bool -func selectnbrecv(chanType *byte, elem *any, hchan <-chan any) bool -func selectnbrecv2(chanType *byte, elem *any, received *bool, hchan <-chan any) bool - -func newselect(sel *byte, selsize int64, size int32) -func selectsend(sel *byte, hchan chan<- any, elem *any) (selected bool) -func selectrecv(sel *byte, hchan <-chan any, elem *any) (selected bool) -func selectrecv2(sel *byte, hchan <-chan any, elem *any, received *bool) (selected bool) -func selectdefault(sel *byte) (selected bool) -func selectgo(sel *byte) -func block() - -func makeslice(typ *byte, nel int64, cap int64) (ary []any) -func growslice(typ *byte, old []any, n int) (ary []any) -func memmove(to *any, frm *any, length uintptr) -func memclr(ptr *byte, length uintptr) - -func memequal(x, y *any, size uintptr) bool -func memequal8(x, y *any) bool -func memequal16(x, y *any) bool -func memequal32(x, y *any) bool -func memequal64(x, y *any) bool -func memequal128(x, y *any) bool - -// only used on 32-bit -func int64div(int64, int64) int64 -func uint64div(uint64, uint64) uint64 -func int64mod(int64, int64) int64 -func uint64mod(uint64, uint64) uint64 -func float64toint64(float64) int64 -func float64touint64(float64) uint64 -func int64tofloat64(int64) float64 -func uint64tofloat64(uint64) float64 - -func complex128div(num complex128, den complex128) (quo complex128) - -// race detection -func racefuncenter(uintptr) -func racefuncexit() -func raceread(uintptr) -func racewrite(uintptr) -func racereadrange(addr, size uintptr) -func racewriterange(addr, size uintptr) diff --git a/src/cmd/internal/gc/builtin/unsafe.go b/src/cmd/internal/gc/builtin/unsafe.go deleted file mode 100644 index ce508692eb..0000000000 --- a/src/cmd/internal/gc/builtin/unsafe.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2009 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. - -// NOTE: If you change this file you must run "go generate" -// to update builtin.go. This is not done automatically -// to avoid depending on having a working compiler binary. - -// +build ignore - -package PACKAGE - -type Pointer uintptr // not really; filled in by compiler - -// return types here are ignored; see unsafe.go -func Offsetof(any) uintptr -func Sizeof(any) uintptr -func Alignof(any) uintptr diff --git a/src/cmd/internal/gc/bv.go b/src/cmd/internal/gc/bv.go deleted file mode 100644 index 2b988e6547..0000000000 --- a/src/cmd/internal/gc/bv.go +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright 2013 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 gc - -import "fmt" - -const ( - WORDSIZE = 4 - WORDBITS = 32 - WORDMASK = WORDBITS - 1 - WORDSHIFT = 5 -) - -// A Bvec is a bit vector. -type Bvec struct { - n int32 // number of bits in vector - b []uint32 // words holding bits -} - -func bvsize(n uint32) uint32 { - return ((n + WORDBITS - 1) / WORDBITS) * WORDSIZE -} - -func bvbits(bv Bvec) int32 { - return bv.n -} - -func bvwords(bv Bvec) int32 { - return (bv.n + WORDBITS - 1) / WORDBITS -} - -func bvalloc(n int32) Bvec { - return Bvec{n, make([]uint32, bvsize(uint32(n))/4)} -} - -type bulkBvec struct { - words []uint32 - nbit int32 - nword int32 -} - -func bvbulkalloc(nbit int32, count int32) bulkBvec { - nword := (nbit + WORDBITS - 1) / WORDBITS - return bulkBvec{ - words: make([]uint32, nword*count), - nbit: nbit, - nword: nword, - } -} - -func (b *bulkBvec) next() Bvec { - out := Bvec{b.nbit, b.words[:b.nword]} - b.words = b.words[b.nword:] - return out -} - -/* difference */ -func bvandnot(dst Bvec, src1 Bvec, src2 Bvec) { - for i, x := range src1.b { - dst.b[i] = x &^ src2.b[i] - } -} - -func bvcmp(bv1 Bvec, bv2 Bvec) int { - if bv1.n != bv2.n { - Fatal("bvequal: lengths %d and %d are not equal", bv1.n, bv2.n) - } - for i, x := range bv1.b { - if x != bv2.b[i] { - return 1 - } - } - return 0 -} - -func bvcopy(dst Bvec, src Bvec) { - for i, x := range src.b { - dst.b[i] = x - } -} - -func bvconcat(src1 Bvec, src2 Bvec) Bvec { - dst := bvalloc(src1.n + src2.n) - for i := int32(0); i < src1.n; i++ { - if bvget(src1, i) != 0 { - bvset(dst, i) - } - } - for i := int32(0); i < src2.n; i++ { - if bvget(src2, i) != 0 { - bvset(dst, i+src1.n) - } - } - return dst -} - -func bvget(bv Bvec, i int32) int { - if i < 0 || i >= bv.n { - Fatal("bvget: index %d is out of bounds with length %d\n", i, bv.n) - } - return int((bv.b[i>>WORDSHIFT] >> uint(i&WORDMASK)) & 1) -} - -// bvnext returns the smallest index >= i for which bvget(bv, i) == 1. -// If there is no such index, bvnext returns -1. -func bvnext(bv Bvec, i int32) int { - if i >= bv.n { - return -1 - } - - // Jump i ahead to next word with bits. - if bv.b[i>>WORDSHIFT]>>uint(i&WORDMASK) == 0 { - i &^= WORDMASK - i += WORDBITS - for i < bv.n && bv.b[i>>WORDSHIFT] == 0 { - i += WORDBITS - } - } - - if i >= bv.n { - return -1 - } - - // Find 1 bit. - w := bv.b[i>>WORDSHIFT] >> uint(i&WORDMASK) - - for w&1 == 0 { - w >>= 1 - i++ - } - - return int(i) -} - -func bvisempty(bv Bvec) bool { - for i := int32(0); i < bv.n; i += WORDBITS { - if bv.b[i>>WORDSHIFT] != 0 { - return false - } - } - return true -} - -func bvnot(bv Bvec) { - i := int32(0) - w := int32(0) - for ; i < bv.n; i, w = i+WORDBITS, w+1 { - bv.b[w] = ^bv.b[w] - } -} - -/* union */ -func bvor(dst Bvec, src1 Bvec, src2 Bvec) { - for i, x := range src1.b { - dst.b[i] = x | src2.b[i] - } -} - -/* intersection */ -func bvand(dst Bvec, src1 Bvec, src2 Bvec) { - for i, x := range src1.b { - dst.b[i] = x & src2.b[i] - } -} - -func bvprint(bv Bvec) { - fmt.Printf("#*") - for i := int32(0); i < bv.n; i++ { - fmt.Printf("%d", bvget(bv, i)) - } -} - -func bvreset(bv Bvec, i int32) { - if i < 0 || i >= bv.n { - Fatal("bvreset: index %d is out of bounds with length %d\n", i, bv.n) - } - mask := uint32(^(1 << uint(i%WORDBITS))) - bv.b[i/WORDBITS] &= mask -} - -func bvresetall(bv Bvec) { - for i := range bv.b { - bv.b[i] = 0 - } -} - -func bvset(bv Bvec, i int32) { - if i < 0 || i >= bv.n { - Fatal("bvset: index %d is out of bounds with length %d\n", i, bv.n) - } - mask := uint32(1 << uint(i%WORDBITS)) - bv.b[i/WORDBITS] |= mask -} diff --git a/src/cmd/internal/gc/cgen.go b/src/cmd/internal/gc/cgen.go deleted file mode 100644 index ca58b1c6a3..0000000000 --- a/src/cmd/internal/gc/cgen.go +++ /dev/null @@ -1,3564 +0,0 @@ -// Copyright 2009 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 gc - -import ( - "cmd/internal/obj" - "fmt" -) - -/* - * generate: - * res = n; - * simplifies and calls Thearch.Gmove. - * if wb is true, need to emit write barriers. - */ -func Cgen(n, res *Node) { - cgen_wb(n, res, false) -} - -func cgen_wb(n, res *Node, wb bool) { - if Debug['g'] != 0 { - op := "cgen" - if wb { - op = "cgen_wb" - } - Dump("\n"+op+"-n", n) - Dump(op+"-res", res) - } - - if n == nil || n.Type == nil { - return - } - - if res == nil || res.Type == nil { - Fatal("cgen: res nil") - } - - for n.Op == OCONVNOP { - n = n.Left - } - - switch n.Op { - case OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR: - cgen_slice(n, res, wb) - return - - case OEFACE: - if res.Op != ONAME || !res.Addable || wb { - var n1 Node - Tempname(&n1, n.Type) - Cgen_eface(n, &n1) - cgen_wb(&n1, res, wb) - } else { - Cgen_eface(n, res) - } - return - - case ODOTTYPE: - cgen_dottype(n, res, nil, wb) - return - - case OAPPEND: - cgen_append(n, res) - return - } - - if n.Ullman >= UINF { - if n.Op == OINDREG { - Fatal("cgen: this is going to miscompile") - } - if res.Ullman >= UINF { - var n1 Node - Tempname(&n1, n.Type) - Cgen(n, &n1) - cgen_wb(&n1, res, wb) - return - } - } - - if Isfat(n.Type) { - if n.Type.Width < 0 { - Fatal("forgot to compute width for %v", n.Type) - } - sgen_wb(n, res, n.Type.Width, wb) - return - } - - if !res.Addable { - if n.Ullman > res.Ullman { - if Ctxt.Arch.Regsize == 4 && Is64(n.Type) { - var n1 Node - Tempname(&n1, n.Type) - Cgen(n, &n1) - cgen_wb(&n1, res, wb) - return - } - - var n1 Node - Regalloc(&n1, n.Type, res) - Cgen(n, &n1) - if n1.Ullman > res.Ullman { - Dump("n1", &n1) - Dump("res", res) - Fatal("loop in cgen") - } - - cgen_wb(&n1, res, wb) - Regfree(&n1) - return - } - - var f int - if res.Ullman < UINF { - if Complexop(n, res) { - Complexgen(n, res) - return - } - - f = 1 // gen thru register - switch n.Op { - case OLITERAL: - if Smallintconst(n) { - f = 0 - } - - case OREGISTER: - f = 0 - } - - if !Iscomplex[n.Type.Etype] && Ctxt.Arch.Regsize == 8 && !wb { - a := Thearch.Optoas(OAS, res.Type) - var addr obj.Addr - if Thearch.Sudoaddable(a, res, &addr) { - var p1 *obj.Prog - if f != 0 { - var n2 Node - Regalloc(&n2, res.Type, nil) - Cgen(n, &n2) - p1 = Thearch.Gins(a, &n2, nil) - Regfree(&n2) - } else { - p1 = Thearch.Gins(a, n, nil) - } - p1.To = addr - if Debug['g'] != 0 { - fmt.Printf("%v [ignore previous line]\n", p1) - } - Thearch.Sudoclean() - return - } - } - } - - if Ctxt.Arch.Thechar == '8' { - // no registers to speak of - var n1, n2 Node - Tempname(&n1, n.Type) - Cgen(n, &n1) - Igen(res, &n2, nil) - cgen_wb(&n1, &n2, wb) - Regfree(&n2) - return - } - - var n1 Node - Igen(res, &n1, nil) - cgen_wb(n, &n1, wb) - Regfree(&n1) - return - } - - // update addressability for string, slice - // can't do in walk because n->left->addable - // changes if n->left is an escaping local variable. - switch n.Op { - case OSPTR, OLEN: - if Isslice(n.Left.Type) || Istype(n.Left.Type, TSTRING) { - n.Addable = n.Left.Addable - } - - case OCAP: - if Isslice(n.Left.Type) { - n.Addable = n.Left.Addable - } - - case OITAB: - n.Addable = n.Left.Addable - } - - if wb { - if int(Simtype[res.Type.Etype]) != Tptr { - Fatal("cgen_wb of type %v", res.Type) - } - if n.Ullman >= UINF { - var n1 Node - Tempname(&n1, n.Type) - Cgen(n, &n1) - n = &n1 - } - cgen_wbptr(n, res) - return - } - - // Write barrier now handled. Code below this line can ignore wb. - - if Ctxt.Arch.Thechar == '5' { // TODO(rsc): Maybe more often? - // if both are addressable, move - if n.Addable && res.Addable { - if Is64(n.Type) || Is64(res.Type) || n.Op == OREGISTER || res.Op == OREGISTER || Iscomplex[n.Type.Etype] || Iscomplex[res.Type.Etype] { - Thearch.Gmove(n, res) - } else { - var n1 Node - Regalloc(&n1, n.Type, nil) - Thearch.Gmove(n, &n1) - Cgen(&n1, res) - Regfree(&n1) - } - - return - } - - // if both are not addressable, use a temporary. - if !n.Addable && !res.Addable { - // could use regalloc here sometimes, - // but have to check for ullman >= UINF. - var n1 Node - Tempname(&n1, n.Type) - Cgen(n, &n1) - Cgen(&n1, res) - return - } - - // if result is not addressable directly but n is, - // compute its address and then store via the address. - if !res.Addable { - var n1 Node - Igen(res, &n1, nil) - Cgen(n, &n1) - Regfree(&n1) - return - } - } - - if Complexop(n, res) { - Complexgen(n, res) - return - } - - if (Ctxt.Arch.Thechar == '6' || Ctxt.Arch.Thechar == '8') && n.Addable { - Thearch.Gmove(n, res) - return - } - - if Ctxt.Arch.Thechar == '7' || Ctxt.Arch.Thechar == '9' { - // if both are addressable, move - if n.Addable { - if n.Op == OREGISTER || res.Op == OREGISTER { - Thearch.Gmove(n, res) - } else { - var n1 Node - Regalloc(&n1, n.Type, nil) - Thearch.Gmove(n, &n1) - Cgen(&n1, res) - Regfree(&n1) - } - return - } - } - - // if n is sudoaddable generate addr and move - if Ctxt.Arch.Thechar == '5' && !Is64(n.Type) && !Is64(res.Type) && !Iscomplex[n.Type.Etype] && !Iscomplex[res.Type.Etype] { - a := Thearch.Optoas(OAS, n.Type) - var addr obj.Addr - if Thearch.Sudoaddable(a, n, &addr) { - if res.Op != OREGISTER { - var n2 Node - Regalloc(&n2, res.Type, nil) - p1 := Thearch.Gins(a, nil, &n2) - p1.From = addr - if Debug['g'] != 0 { - fmt.Printf("%v [ignore previous line]\n", p1) - } - Thearch.Gmove(&n2, res) - Regfree(&n2) - } else { - p1 := Thearch.Gins(a, nil, res) - p1.From = addr - if Debug['g'] != 0 { - fmt.Printf("%v [ignore previous line]\n", p1) - } - } - Thearch.Sudoclean() - return - } - } - - nl := n.Left - nr := n.Right - - if nl != nil && nl.Ullman >= UINF { - if nr != nil && nr.Ullman >= UINF { - var n1 Node - Tempname(&n1, nl.Type) - Cgen(nl, &n1) - n2 := *n - n2.Left = &n1 - Cgen(&n2, res) - return - } - } - - // 64-bit ops are hard on 32-bit machine. - if Ctxt.Arch.Regsize == 4 && (Is64(n.Type) || Is64(res.Type) || n.Left != nil && Is64(n.Left.Type)) { - switch n.Op { - // math goes to cgen64. - case OMINUS, - OCOM, - OADD, - OSUB, - OMUL, - OLROT, - OLSH, - ORSH, - OAND, - OOR, - OXOR: - Thearch.Cgen64(n, res) - return - } - } - - if Thearch.Cgen_float != nil && nl != nil && Isfloat[n.Type.Etype] && Isfloat[nl.Type.Etype] { - Thearch.Cgen_float(n, res) - return - } - - if !Iscomplex[n.Type.Etype] && Ctxt.Arch.Regsize == 8 { - a := Thearch.Optoas(OAS, n.Type) - var addr obj.Addr - if Thearch.Sudoaddable(a, n, &addr) { - if res.Op == OREGISTER { - p1 := Thearch.Gins(a, nil, res) - p1.From = addr - } else { - var n2 Node - Regalloc(&n2, n.Type, nil) - p1 := Thearch.Gins(a, nil, &n2) - p1.From = addr - Thearch.Gins(a, &n2, res) - Regfree(&n2) - } - - Thearch.Sudoclean() - return - } - } - - var a int - switch n.Op { - default: - Dump("cgen", n) - Dump("cgen-res", res) - Fatal("cgen: unknown op %v", Nconv(n, obj.FmtShort|obj.FmtSign)) - - case OOROR, OANDAND, - OEQ, ONE, - OLT, OLE, - OGE, OGT, - ONOT: - Bvgen(n, res, true) - return - - case OPLUS: - Cgen(nl, res) - return - - // unary - case OCOM: - a := Thearch.Optoas(OXOR, nl.Type) - - var n1 Node - Regalloc(&n1, nl.Type, nil) - Cgen(nl, &n1) - var n2 Node - Nodconst(&n2, nl.Type, -1) - Thearch.Gins(a, &n2, &n1) - cgen_norm(n, &n1, res) - return - - case OMINUS: - if Isfloat[nl.Type.Etype] { - nr = Nodintconst(-1) - Convlit(&nr, n.Type) - a = Thearch.Optoas(OMUL, nl.Type) - goto sbop - } - - a := Thearch.Optoas(int(n.Op), nl.Type) - // unary - var n1 Node - Regalloc(&n1, nl.Type, res) - - Cgen(nl, &n1) - if Ctxt.Arch.Thechar == '5' { - var n2 Node - Nodconst(&n2, nl.Type, 0) - Thearch.Gins(a, &n2, &n1) - } else if Ctxt.Arch.Thechar == '7' { - Thearch.Gins(a, &n1, &n1) - } else { - Thearch.Gins(a, nil, &n1) - } - cgen_norm(n, &n1, res) - return - - case OSQRT: - var n1 Node - Regalloc(&n1, nl.Type, res) - Cgen(n.Left, &n1) - Thearch.Gins(Thearch.Optoas(OSQRT, nl.Type), &n1, &n1) - Thearch.Gmove(&n1, res) - Regfree(&n1) - return - - case OGETG: - Thearch.Getg(res) - return - - // symmetric binary - case OAND, - OOR, - OXOR, - OADD, - OMUL: - if n.Op == OMUL && Thearch.Cgen_bmul != nil && Thearch.Cgen_bmul(int(n.Op), nl, nr, res) { - break - } - a = Thearch.Optoas(int(n.Op), nl.Type) - goto sbop - - // asymmetric binary - case OSUB: - a = Thearch.Optoas(int(n.Op), nl.Type) - goto abop - - case OHMUL: - Thearch.Cgen_hmul(nl, nr, res) - - case OCONV: - if Eqtype(n.Type, nl.Type) || Noconv(n.Type, nl.Type) { - Cgen(nl, res) - return - } - - if Ctxt.Arch.Thechar == '8' { - var n1 Node - var n2 Node - Tempname(&n2, n.Type) - Mgen(nl, &n1, res) - Thearch.Gmove(&n1, &n2) - Thearch.Gmove(&n2, res) - Mfree(&n1) - break - } - - var n1 Node - var n2 Node - if Ctxt.Arch.Thechar == '5' { - if nl.Addable && !Is64(nl.Type) { - Regalloc(&n1, nl.Type, res) - Thearch.Gmove(nl, &n1) - } else { - if n.Type.Width > int64(Widthptr) || Is64(nl.Type) || Isfloat[nl.Type.Etype] { - Tempname(&n1, nl.Type) - } else { - Regalloc(&n1, nl.Type, res) - } - Cgen(nl, &n1) - } - if n.Type.Width > int64(Widthptr) || Is64(n.Type) || Isfloat[n.Type.Etype] { - Tempname(&n2, n.Type) - } else { - Regalloc(&n2, n.Type, nil) - } - } else { - if n.Type.Width > nl.Type.Width { - // If loading from memory, do conversion during load, - // so as to avoid use of 8-bit register in, say, int(*byteptr). - switch nl.Op { - case ODOT, ODOTPTR, OINDEX, OIND, ONAME: - Igen(nl, &n1, res) - Regalloc(&n2, n.Type, res) - Thearch.Gmove(&n1, &n2) - Thearch.Gmove(&n2, res) - Regfree(&n2) - Regfree(&n1) - return - } - } - Regalloc(&n1, nl.Type, res) - Regalloc(&n2, n.Type, &n1) - Cgen(nl, &n1) - } - - // if we do the conversion n1 -> n2 here - // reusing the register, then gmove won't - // have to allocate its own register. - Thearch.Gmove(&n1, &n2) - Thearch.Gmove(&n2, res) - if n2.Op == OREGISTER { - Regfree(&n2) - } - if n1.Op == OREGISTER { - Regfree(&n1) - } - - case ODOT, - ODOTPTR, - OINDEX, - OIND, - ONAME: // PHEAP or PPARAMREF var - var n1 Node - Igen(n, &n1, res) - - Thearch.Gmove(&n1, res) - Regfree(&n1) - - // interface table is first word of interface value - case OITAB: - var n1 Node - Igen(nl, &n1, res) - - n1.Type = n.Type - Thearch.Gmove(&n1, res) - Regfree(&n1) - - case OSPTR: - // pointer is the first word of string or slice. - if Isconst(nl, CTSTR) { - var n1 Node - Regalloc(&n1, Types[Tptr], res) - p1 := Thearch.Gins(Thearch.Optoas(OAS, n1.Type), nil, &n1) - Datastring(nl.Val.U.(string), &p1.From) - p1.From.Type = obj.TYPE_ADDR - Thearch.Gmove(&n1, res) - Regfree(&n1) - break - } - - var n1 Node - Igen(nl, &n1, res) - n1.Type = n.Type - Thearch.Gmove(&n1, res) - Regfree(&n1) - - case OLEN: - if Istype(nl.Type, TMAP) || Istype(nl.Type, TCHAN) { - // map and chan have len in the first int-sized word. - // a zero pointer means zero length - var n1 Node - Regalloc(&n1, Types[Tptr], res) - - Cgen(nl, &n1) - - var n2 Node - Nodconst(&n2, Types[Tptr], 0) - p1 := Thearch.Ginscmp(OEQ, Types[Tptr], &n1, &n2, 0) - - n2 = n1 - n2.Op = OINDREG - n2.Type = Types[Simtype[TINT]] - Thearch.Gmove(&n2, &n1) - - Patch(p1, Pc) - - Thearch.Gmove(&n1, res) - Regfree(&n1) - break - } - - if Istype(nl.Type, TSTRING) || Isslice(nl.Type) { - // both slice and string have len one pointer into the struct. - // a zero pointer means zero length - var n1 Node - Igen(nl, &n1, res) - - n1.Type = Types[Simtype[TUINT]] - n1.Xoffset += int64(Array_nel) - Thearch.Gmove(&n1, res) - Regfree(&n1) - break - } - - Fatal("cgen: OLEN: unknown type %v", Tconv(nl.Type, obj.FmtLong)) - - case OCAP: - if Istype(nl.Type, TCHAN) { - // chan has cap in the second int-sized word. - // a zero pointer means zero length - var n1 Node - Regalloc(&n1, Types[Tptr], res) - - Cgen(nl, &n1) - - var n2 Node - Nodconst(&n2, Types[Tptr], 0) - p1 := Thearch.Ginscmp(OEQ, Types[Tptr], &n1, &n2, 0) - - n2 = n1 - n2.Op = OINDREG - n2.Xoffset = int64(Widthint) - n2.Type = Types[Simtype[TINT]] - Thearch.Gmove(&n2, &n1) - - Patch(p1, Pc) - - Thearch.Gmove(&n1, res) - Regfree(&n1) - break - } - - if Isslice(nl.Type) { - var n1 Node - Igen(nl, &n1, res) - n1.Type = Types[Simtype[TUINT]] - n1.Xoffset += int64(Array_cap) - Thearch.Gmove(&n1, res) - Regfree(&n1) - break - } - - Fatal("cgen: OCAP: unknown type %v", Tconv(nl.Type, obj.FmtLong)) - - case OADDR: - if n.Bounded { // let race detector avoid nil checks - Disable_checknil++ - } - Agen(nl, res) - if n.Bounded { - Disable_checknil-- - } - - case OCALLMETH: - cgen_callmeth(n, 0) - cgen_callret(n, res) - - case OCALLINTER: - cgen_callinter(n, res, 0) - cgen_callret(n, res) - - case OCALLFUNC: - cgen_call(n, 0) - cgen_callret(n, res) - - case OMOD, ODIV: - if Isfloat[n.Type.Etype] || Thearch.Dodiv == nil { - a = Thearch.Optoas(int(n.Op), nl.Type) - goto abop - } - - if nl.Ullman >= nr.Ullman { - var n1 Node - Regalloc(&n1, nl.Type, res) - Cgen(nl, &n1) - cgen_div(int(n.Op), &n1, nr, res) - Regfree(&n1) - } else { - var n2 Node - if !Smallintconst(nr) { - Regalloc(&n2, nr.Type, res) - Cgen(nr, &n2) - } else { - n2 = *nr - } - - cgen_div(int(n.Op), nl, &n2, res) - if n2.Op != OLITERAL { - Regfree(&n2) - } - } - - case OLSH, ORSH, OLROT: - Thearch.Cgen_shift(int(n.Op), n.Bounded, nl, nr, res) - } - - return - - /* - * put simplest on right - we'll generate into left - * and then adjust it using the computation of right. - * constants and variables have the same ullman - * count, so look for constants specially. - * - * an integer constant we can use as an immediate - * is simpler than a variable - we can use the immediate - * in the adjustment instruction directly - so it goes - * on the right. - * - * other constants, like big integers or floating point - * constants, require a mov into a register, so those - * might as well go on the left, so we can reuse that - * register for the computation. - */ -sbop: // symmetric binary - if nl.Ullman < nr.Ullman || (nl.Ullman == nr.Ullman && (Smallintconst(nl) || (nr.Op == OLITERAL && !Smallintconst(nr)))) { - r := nl - nl = nr - nr = r - } - -abop: // asymmetric binary - var n1 Node - var n2 Node - if Ctxt.Arch.Thechar == '8' { - // no registers, sigh - if Smallintconst(nr) { - var n1 Node - Mgen(nl, &n1, res) - var n2 Node - Regalloc(&n2, nl.Type, &n1) - Thearch.Gmove(&n1, &n2) - Thearch.Gins(a, nr, &n2) - Thearch.Gmove(&n2, res) - Regfree(&n2) - Mfree(&n1) - } else if nl.Ullman >= nr.Ullman { - var nt Node - Tempname(&nt, nl.Type) - Cgen(nl, &nt) - var n2 Node - Mgen(nr, &n2, nil) - var n1 Node - Regalloc(&n1, nl.Type, res) - Thearch.Gmove(&nt, &n1) - Thearch.Gins(a, &n2, &n1) - Thearch.Gmove(&n1, res) - Regfree(&n1) - Mfree(&n2) - } else { - var n2 Node - Regalloc(&n2, nr.Type, res) - Cgen(nr, &n2) - var n1 Node - Regalloc(&n1, nl.Type, nil) - Cgen(nl, &n1) - Thearch.Gins(a, &n2, &n1) - Regfree(&n2) - Thearch.Gmove(&n1, res) - Regfree(&n1) - } - return - } - - if nl.Ullman >= nr.Ullman { - Regalloc(&n1, nl.Type, res) - Cgen(nl, &n1) - - if Smallintconst(nr) && Ctxt.Arch.Thechar != '5' && Ctxt.Arch.Thechar != '7' && Ctxt.Arch.Thechar != '9' { // TODO(rsc): Check opcode for arm - n2 = *nr - } else { - Regalloc(&n2, nr.Type, nil) - Cgen(nr, &n2) - } - } else { - if Smallintconst(nr) && Ctxt.Arch.Thechar != '5' && Ctxt.Arch.Thechar != '7' && Ctxt.Arch.Thechar != '9' { // TODO(rsc): Check opcode for arm - n2 = *nr - } else { - Regalloc(&n2, nr.Type, res) - Cgen(nr, &n2) - } - - Regalloc(&n1, nl.Type, nil) - Cgen(nl, &n1) - } - - Thearch.Gins(a, &n2, &n1) - if n2.Op != OLITERAL { - Regfree(&n2) - } - cgen_norm(n, &n1, res) -} - -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") - } - - var dst, src Node - Igen(res, &dst, nil) - if n.Op == OREGISTER { - src = *n - Regrealloc(&src) - } else { - Cgenr(n, &src, nil) - } - - wbEnabled := syslook("writeBarrierEnabled", 0) - 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) - var adst Node - Agenr(&dst, &adst, &dst) - p := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &adst, nil) - a := &p.To - a.Type = obj.TYPE_MEM - a.Reg = int16(Thearch.REGSP) - a.Offset = 0 - if HasLinkRegister() { - a.Offset += int64(Widthptr) - } - p2 := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &src, nil) - p2.To = p.To - p2.To.Offset += int64(Widthptr) - Regfree(&adst) - if sys_wbptr == nil { - sys_wbptr = writebarrierfn("writebarrierptr", Types[Tptr], Types[Tptr]) - } - Ginscall(sys_wbptr, 0) - Patch(pjmp, Pc) - - Regfree(&dst) - Regfree(&src) -} - -func cgen_wbfat(n, res *Node) { - if Curfn != nil && Curfn.Func.Nowritebarrier { - Yyerror("write barrier prohibited") - } - if Debug_wb > 0 { - Warn("write barrier") - } - needType := true - funcName := "typedmemmove" - var dst, src Node - if n.Ullman >= res.Ullman { - Agenr(n, &src, nil) - Agenr(res, &dst, nil) - } else { - Agenr(res, &dst, nil) - Agenr(n, &src, nil) - } - p := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &dst, nil) - a := &p.To - a.Type = obj.TYPE_MEM - a.Reg = int16(Thearch.REGSP) - a.Offset = 0 - if HasLinkRegister() { - a.Offset += int64(Widthptr) - } - if needType { - a.Offset += int64(Widthptr) - } - p2 := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &src, nil) - p2.To = p.To - p2.To.Offset += int64(Widthptr) - Regfree(&dst) - if needType { - src.Type = Types[Tptr] - Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), typename(n.Type), &src) - p3 := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &src, nil) - p3.To = p2.To - p3.To.Offset -= 2 * int64(Widthptr) - } - Regfree(&src) - Ginscall(writebarrierfn(funcName, Types[Tptr], Types[Tptr]), 0) -} - -// cgen_norm moves n1 to res, truncating to expected type if necessary. -// n1 is a register, and cgen_norm frees it. -func cgen_norm(n, n1, res *Node) { - switch Ctxt.Arch.Thechar { - case '6', '8': - // We use sized math, so the result is already truncated. - default: - switch n.Op { - case OADD, OSUB, OMUL, ODIV, OCOM, OMINUS: - // TODO(rsc): What about left shift? - Thearch.Gins(Thearch.Optoas(OAS, n.Type), n1, n1) - } - } - - Thearch.Gmove(n1, res) - Regfree(n1) -} - -func Mgen(n *Node, n1 *Node, rg *Node) { - n1.Op = OEMPTY - - if n.Addable { - *n1 = *n - if n1.Op == OREGISTER || n1.Op == OINDREG { - reg[n.Reg-int16(Thearch.REGMIN)]++ - } - return - } - - Tempname(n1, n.Type) - Cgen(n, n1) - if n.Type.Width <= int64(Widthptr) || Isfloat[n.Type.Etype] { - n2 := *n1 - Regalloc(n1, n.Type, rg) - Thearch.Gmove(&n2, n1) - } -} - -func Mfree(n *Node) { - if n.Op == OREGISTER { - Regfree(n) - } -} - -/* - * allocate a register (reusing res if possible) and generate - * a = n - * The caller must call Regfree(a). - */ -func Cgenr(n *Node, a *Node, res *Node) { - if Debug['g'] != 0 { - Dump("cgenr-n", n) - } - - if Isfat(n.Type) { - Fatal("cgenr on fat node") - } - - if n.Addable { - Regalloc(a, n.Type, res) - Thearch.Gmove(n, a) - return - } - - switch n.Op { - case ONAME, - ODOT, - ODOTPTR, - OINDEX, - OCALLFUNC, - OCALLMETH, - OCALLINTER: - var n1 Node - Igen(n, &n1, res) - Regalloc(a, Types[Tptr], &n1) - Thearch.Gmove(&n1, a) - Regfree(&n1) - - default: - Regalloc(a, n.Type, res) - Cgen(n, a) - } -} - -/* - * allocate a register (reusing res if possible) and generate - * a = &n - * The caller must call Regfree(a). - * The generated code checks that the result is not nil. - */ -func Agenr(n *Node, a *Node, res *Node) { - if Debug['g'] != 0 { - Dump("\nagenr-n", n) - } - - nl := n.Left - nr := n.Right - - switch n.Op { - case ODOT, ODOTPTR, OCALLFUNC, OCALLMETH, OCALLINTER: - var n1 Node - Igen(n, &n1, res) - Regalloc(a, Types[Tptr], &n1) - Agen(&n1, a) - Regfree(&n1) - - case OIND: - Cgenr(n.Left, a, res) - Cgen_checknil(a) - - case OINDEX: - if Ctxt.Arch.Thechar == '5' { - var p2 *obj.Prog // to be patched to panicindex. - w := uint32(n.Type.Width) - bounded := Debug['B'] != 0 || n.Bounded - var n1 Node - var n3 Node - if nr.Addable { - var tmp Node - if !Isconst(nr, CTINT) { - Tempname(&tmp, Types[TINT32]) - } - if !Isconst(nl, CTSTR) { - Agenr(nl, &n3, res) - } - if !Isconst(nr, CTINT) { - p2 = Thearch.Cgenindex(nr, &tmp, bounded) - Regalloc(&n1, tmp.Type, nil) - Thearch.Gmove(&tmp, &n1) - } - } else if nl.Addable { - if !Isconst(nr, CTINT) { - var tmp Node - Tempname(&tmp, Types[TINT32]) - p2 = Thearch.Cgenindex(nr, &tmp, bounded) - Regalloc(&n1, tmp.Type, nil) - Thearch.Gmove(&tmp, &n1) - } - - if !Isconst(nl, CTSTR) { - Agenr(nl, &n3, res) - } - } else { - var tmp Node - Tempname(&tmp, Types[TINT32]) - p2 = Thearch.Cgenindex(nr, &tmp, bounded) - nr = &tmp - if !Isconst(nl, CTSTR) { - Agenr(nl, &n3, res) - } - Regalloc(&n1, tmp.Type, nil) - Thearch.Gins(Thearch.Optoas(OAS, tmp.Type), &tmp, &n1) - } - - // &a is in &n3 (allocated in res) - // i is in &n1 (if not constant) - // w is width - - // constant index - if Isconst(nr, CTINT) { - if Isconst(nl, CTSTR) { - Fatal("constant string constant index") - } - v := uint64(Mpgetfix(nr.Val.U.(*Mpint))) - var n2 Node - if Isslice(nl.Type) || nl.Type.Etype == TSTRING { - if Debug['B'] == 0 && !n.Bounded { - n1 = n3 - 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) - } - - n1 = n3 - n1.Op = OINDREG - n1.Type = Types[Tptr] - n1.Xoffset = int64(Array_array) - Thearch.Gmove(&n1, &n3) - } - - Nodconst(&n2, Types[Tptr], int64(v*uint64(w))) - Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n3) - *a = n3 - break - } - - var n2 Node - Regalloc(&n2, Types[TINT32], &n1) // i - Thearch.Gmove(&n1, &n2) - Regfree(&n1) - - var n4 Node - if Debug['B'] == 0 && !n.Bounded { - // check bounds - if Isconst(nl, CTSTR) { - Nodconst(&n4, Types[TUINT32], int64(len(nl.Val.U.(string)))) - } else if Isslice(nl.Type) || nl.Type.Etype == TSTRING { - n1 = n3 - n1.Op = OINDREG - n1.Type = Types[Tptr] - n1.Xoffset = int64(Array_nel) - Regalloc(&n4, Types[TUINT32], nil) - Thearch.Gmove(&n1, &n4) - } else { - Nodconst(&n4, Types[TUINT32], nl.Type.Bound) - } - p1 := Thearch.Ginscmp(OLT, Types[TUINT32], &n2, &n4, +1) - if n4.Op == OREGISTER { - Regfree(&n4) - } - if p2 != nil { - Patch(p2, Pc) - } - Ginscall(Panicindex, -1) - Patch(p1, Pc) - } - - if Isconst(nl, CTSTR) { - Regalloc(&n3, Types[Tptr], res) - p1 := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), nil, &n3) - Datastring(nl.Val.U.(string), &p1.From) - p1.From.Type = obj.TYPE_ADDR - } else if Isslice(nl.Type) || nl.Type.Etype == TSTRING { - n1 = n3 - n1.Op = OINDREG - n1.Type = Types[Tptr] - n1.Xoffset = int64(Array_array) - Thearch.Gmove(&n1, &n3) - } - - if w == 0 { - // nothing to do - } else if Thearch.AddIndex != nil && Thearch.AddIndex(&n2, int64(w), &n3) { - // done by back end - } else if w == 1 { - Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n3) - } else { - if w&(w-1) == 0 { - // Power of 2. Use shift. - Thearch.Ginscon(Thearch.Optoas(OLSH, Types[TUINT32]), int64(log2(uint64(w))), &n2) - } else { - // Not a power of 2. Use multiply. - Regalloc(&n4, Types[TUINT32], nil) - Nodconst(&n1, Types[TUINT32], int64(w)) - Thearch.Gmove(&n1, &n4) - Thearch.Gins(Thearch.Optoas(OMUL, Types[TUINT32]), &n4, &n2) - Regfree(&n4) - } - Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n3) - } - *a = n3 - Regfree(&n2) - break - } - if Ctxt.Arch.Thechar == '8' { - var p2 *obj.Prog // to be patched to panicindex. - w := uint32(n.Type.Width) - bounded := Debug['B'] != 0 || n.Bounded - var n3 Node - var tmp Node - var n1 Node - if nr.Addable { - // Generate &nl first, and move nr into register. - if !Isconst(nl, CTSTR) { - Igen(nl, &n3, res) - } - if !Isconst(nr, CTINT) { - p2 = Thearch.Igenindex(nr, &tmp, bounded) - Regalloc(&n1, tmp.Type, nil) - Thearch.Gmove(&tmp, &n1) - } - } else if nl.Addable { - // Generate nr first, and move &nl into register. - if !Isconst(nr, CTINT) { - p2 = Thearch.Igenindex(nr, &tmp, bounded) - Regalloc(&n1, tmp.Type, nil) - Thearch.Gmove(&tmp, &n1) - } - - if !Isconst(nl, CTSTR) { - Igen(nl, &n3, res) - } - } else { - p2 = Thearch.Igenindex(nr, &tmp, bounded) - nr = &tmp - if !Isconst(nl, CTSTR) { - Igen(nl, &n3, res) - } - Regalloc(&n1, tmp.Type, nil) - Thearch.Gins(Thearch.Optoas(OAS, tmp.Type), &tmp, &n1) - } - - // For fixed array we really want the pointer in n3. - var n2 Node - if Isfixedarray(nl.Type) { - Regalloc(&n2, Types[Tptr], &n3) - Agen(&n3, &n2) - Regfree(&n3) - n3 = n2 - } - - // &a[0] is in n3 (allocated in res) - // i is in n1 (if not constant) - // len(a) is in nlen (if needed) - // w is width - - // constant index - if Isconst(nr, CTINT) { - if Isconst(nl, CTSTR) { - Fatal("constant string constant index") // front end should handle - } - v := uint64(Mpgetfix(nr.Val.U.(*Mpint))) - if Isslice(nl.Type) || nl.Type.Etype == TSTRING { - if Debug['B'] == 0 && !n.Bounded { - nlen := n3 - nlen.Type = Types[TUINT32] - nlen.Xoffset += int64(Array_nel) - Nodconst(&n2, Types[TUINT32], int64(v)) - p1 := Thearch.Ginscmp(OGT, Types[TUINT32], &nlen, &n2, +1) - Ginscall(Panicindex, -1) - Patch(p1, Pc) - } - } - - // Load base pointer in n2 = n3. - Regalloc(&n2, Types[Tptr], &n3) - - n3.Type = Types[Tptr] - n3.Xoffset += int64(Array_array) - Thearch.Gmove(&n3, &n2) - Regfree(&n3) - if v*uint64(w) != 0 { - Nodconst(&n1, Types[Tptr], int64(v*uint64(w))) - Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n1, &n2) - } - *a = n2 - break - } - - // i is in register n1, extend to 32 bits. - t := Types[TUINT32] - - if Issigned[n1.Type.Etype] { - t = Types[TINT32] - } - - Regalloc(&n2, t, &n1) // i - Thearch.Gmove(&n1, &n2) - Regfree(&n1) - - if Debug['B'] == 0 && !n.Bounded { - // check bounds - t := Types[TUINT32] - - var nlen Node - if Isconst(nl, CTSTR) { - Nodconst(&nlen, t, int64(len(nl.Val.U.(string)))) - } else if Isslice(nl.Type) || nl.Type.Etype == TSTRING { - nlen = n3 - nlen.Type = t - nlen.Xoffset += int64(Array_nel) - } else { - Nodconst(&nlen, t, nl.Type.Bound) - } - - p1 := Thearch.Ginscmp(OLT, t, &n2, &nlen, +1) - if p2 != nil { - Patch(p2, Pc) - } - Ginscall(Panicindex, -1) - Patch(p1, Pc) - } - - if Isconst(nl, CTSTR) { - Regalloc(&n3, Types[Tptr], res) - p1 := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), nil, &n3) - Datastring(nl.Val.U.(string), &p1.From) - p1.From.Type = obj.TYPE_ADDR - Thearch.Gins(Thearch.Optoas(OADD, n3.Type), &n2, &n3) - goto indexdone1 - } - - // Load base pointer in n3. - Regalloc(&tmp, Types[Tptr], &n3) - - if Isslice(nl.Type) || nl.Type.Etype == TSTRING { - n3.Type = Types[Tptr] - n3.Xoffset += int64(Array_array) - Thearch.Gmove(&n3, &tmp) - } - - Regfree(&n3) - n3 = tmp - - if w == 0 { - // nothing to do - } else if Thearch.AddIndex != nil && Thearch.AddIndex(&n2, int64(w), &n3) { - // done by back end - } else if w == 1 { - Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n3) - } else { - if w&(w-1) == 0 { - // Power of 2. Use shift. - Thearch.Ginscon(Thearch.Optoas(OLSH, Types[TUINT32]), int64(log2(uint64(w))), &n2) - } else { - // Not a power of 2. Use multiply. - Thearch.Ginscon(Thearch.Optoas(OMUL, Types[TUINT32]), int64(w), &n2) - } - Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n3) - } - - indexdone1: - *a = n3 - Regfree(&n2) - break - } - - freelen := 0 - w := uint64(n.Type.Width) - - // Generate the non-addressable child first. - var n3 Node - var nlen Node - var tmp Node - var n1 Node - if nr.Addable { - goto irad - } - if nl.Addable { - Cgenr(nr, &n1, nil) - if !Isconst(nl, CTSTR) { - if Isfixedarray(nl.Type) { - Agenr(nl, &n3, res) - } else { - Igen(nl, &nlen, res) - freelen = 1 - nlen.Type = Types[Tptr] - nlen.Xoffset += int64(Array_array) - Regalloc(&n3, Types[Tptr], res) - Thearch.Gmove(&nlen, &n3) - nlen.Type = Types[Simtype[TUINT]] - nlen.Xoffset += int64(Array_nel) - int64(Array_array) - } - } - - goto index - } - - Tempname(&tmp, nr.Type) - Cgen(nr, &tmp) - nr = &tmp - - irad: - if !Isconst(nl, CTSTR) { - if Isfixedarray(nl.Type) { - Agenr(nl, &n3, res) - } else { - if !nl.Addable { - if res != nil && res.Op == OREGISTER { // give up res, which we don't need yet. - Regfree(res) - } - - // igen will need an addressable node. - var tmp2 Node - Tempname(&tmp2, nl.Type) - Cgen(nl, &tmp2) - nl = &tmp2 - - if res != nil && res.Op == OREGISTER { // reacquire res - Regrealloc(res) - } - } - - Igen(nl, &nlen, res) - freelen = 1 - nlen.Type = Types[Tptr] - nlen.Xoffset += int64(Array_array) - Regalloc(&n3, Types[Tptr], res) - Thearch.Gmove(&nlen, &n3) - nlen.Type = Types[Simtype[TUINT]] - nlen.Xoffset += int64(Array_nel) - int64(Array_array) - } - } - - if !Isconst(nr, CTINT) { - Cgenr(nr, &n1, nil) - } - - goto index - - // &a is in &n3 (allocated in res) - // i is in &n1 (if not constant) - // len(a) is in nlen (if needed) - // w is width - - // constant index - index: - if Isconst(nr, CTINT) { - if Isconst(nl, CTSTR) { - Fatal("constant string constant index") // front end should handle - } - 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) - Ginscall(Panicindex, -1) - Patch(p1, Pc) - } - - Regfree(&nlen) - } - - if v*w != 0 { - Thearch.Ginscon(Thearch.Optoas(OADD, Types[Tptr]), int64(v*w), &n3) - } - *a = n3 - break - } - - // type of the index - t := Types[TUINT64] - - if Issigned[n1.Type.Etype] { - t = Types[TINT64] - } - - var n2 Node - Regalloc(&n2, t, &n1) // i - Thearch.Gmove(&n1, &n2) - Regfree(&n1) - - if Debug['B'] == 0 && !n.Bounded { - // check bounds - t = Types[Simtype[TUINT]] - - if Is64(nr.Type) { - t = Types[TUINT64] - } - if Isconst(nl, CTSTR) { - Nodconst(&nlen, t, int64(len(nl.Val.U.(string)))) - } else if Isslice(nl.Type) || nl.Type.Etype == TSTRING { - // nlen already initialized - } else { - Nodconst(&nlen, t, nl.Type.Bound) - } - - p1 := Thearch.Ginscmp(OLT, t, &n2, &nlen, +1) - Ginscall(Panicindex, -1) - Patch(p1, Pc) - } - - 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.(string), &p1.From) - p1.From.Type = obj.TYPE_ADDR - Thearch.Gins(Thearch.Optoas(OADD, n3.Type), &n2, &n3) - goto indexdone - } - - if w == 0 { - // nothing to do - } else if Thearch.AddIndex != nil && Thearch.AddIndex(&n2, int64(w), &n3) { - // done by back end - } else if w == 1 { - Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n3) - } else { - if w&(w-1) == 0 { - // Power of 2. Use shift. - Thearch.Ginscon(Thearch.Optoas(OLSH, t), int64(log2(w)), &n2) - } else { - // Not a power of 2. Use multiply. - Thearch.Ginscon(Thearch.Optoas(OMUL, t), int64(w), &n2) - } - Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n3) - } - - indexdone: - *a = n3 - Regfree(&n2) - if freelen != 0 { - Regfree(&nlen) - } - - default: - Regalloc(a, Types[Tptr], res) - Agen(n, a) - } -} - -// log2 returns the logarithm base 2 of n. n must be a power of 2. -func log2(n uint64) int { - x := 0 - for n>>uint(x) != 1 { - x++ - } - return x -} - -/* - * generate: - * res = &n; - * The generated code checks that the result is not nil. - */ -func Agen(n *Node, res *Node) { - if Debug['g'] != 0 { - Dump("\nagen-res", res) - Dump("agen-r", n) - } - - if n == nil || n.Type == nil { - return - } - - for n.Op == OCONVNOP { - n = n.Left - } - - if Isconst(n, CTNIL) && n.Type.Width > int64(Widthptr) { - // Use of a nil interface or nil slice. - // Create a temporary we can take the address of and read. - // The generated code is just going to panic, so it need not - // be terribly efficient. See issue 3670. - var n1 Node - Tempname(&n1, n.Type) - - Gvardef(&n1) - Thearch.Clearfat(&n1) - var n2 Node - Regalloc(&n2, Types[Tptr], res) - var n3 Node - n3.Op = OADDR - n3.Left = &n1 - Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &n3, &n2) - Thearch.Gmove(&n2, res) - Regfree(&n2) - return - } - - if n.Op == OINDREG && n.Xoffset == 0 { - // Generate MOVW R0, R1 instead of MOVW $0(R0), R1. - // This allows better move propagation in the back ends - // (and maybe it helps the processor). - n1 := *n - n1.Op = OREGISTER - n1.Type = res.Type - Thearch.Gmove(&n1, res) - return - } - - if n.Addable { - if n.Op == OREGISTER { - Fatal("agen OREGISTER") - } - var n1 Node - n1.Op = OADDR - n1.Left = n - var n2 Node - Regalloc(&n2, Types[Tptr], res) - Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &n1, &n2) - Thearch.Gmove(&n2, res) - Regfree(&n2) - return - } - - nl := n.Left - - switch n.Op { - default: - Fatal("agen: unknown op %v", Nconv(n, obj.FmtShort|obj.FmtSign)) - - case OCALLMETH: - cgen_callmeth(n, 0) - cgen_aret(n, res) - - case OCALLINTER: - cgen_callinter(n, res, 0) - cgen_aret(n, res) - - case OCALLFUNC: - cgen_call(n, 0) - cgen_aret(n, res) - - case OEFACE, ODOTTYPE, OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR: - var n1 Node - Tempname(&n1, n.Type) - Cgen(n, &n1) - Agen(&n1, res) - - case OINDEX: - var n1 Node - Agenr(n, &n1, res) - Thearch.Gmove(&n1, res) - Regfree(&n1) - - case ONAME: - // should only get here with names in this func. - if n.Funcdepth > 0 && n.Funcdepth != Funcdepth { - Dump("bad agen", n) - Fatal("agen: bad ONAME funcdepth %d != %d", n.Funcdepth, Funcdepth) - } - - // should only get here for heap vars or paramref - if n.Class&PHEAP == 0 && n.Class != PPARAMREF { - Dump("bad agen", n) - Fatal("agen: bad ONAME class %#x", n.Class) - } - - Cgen(n.Name.Heapaddr, res) - if n.Xoffset != 0 { - addOffset(res, n.Xoffset) - } - - case OIND: - Cgen(nl, res) - Cgen_checknil(res) - - case ODOT: - Agen(nl, res) - if n.Xoffset != 0 { - addOffset(res, n.Xoffset) - } - - case ODOTPTR: - Cgen(nl, res) - Cgen_checknil(res) - if n.Xoffset != 0 { - addOffset(res, n.Xoffset) - } - } -} - -func addOffset(res *Node, offset int64) { - if Ctxt.Arch.Thechar == '6' || Ctxt.Arch.Thechar == '8' { - Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), Nodintconst(offset), res) - return - } - - var n1, n2 Node - Regalloc(&n1, Types[Tptr], nil) - Thearch.Gmove(res, &n1) - Regalloc(&n2, Types[Tptr], nil) - Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), Nodintconst(offset), &n2) - Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n1) - Thearch.Gmove(&n1, res) - Regfree(&n1) - Regfree(&n2) -} - -// Igen computes the address &n, stores it in a register r, -// and rewrites a to refer to *r. The chosen r may be the -// stack pointer, it may be borrowed from res, or it may -// be a newly allocated register. The caller must call Regfree(a) -// to free r when the address is no longer needed. -// The generated code ensures that &n is not nil. -func Igen(n *Node, a *Node, res *Node) { - if Debug['g'] != 0 { - Dump("\nigen-n", n) - } - - switch n.Op { - case ONAME: - if (n.Class&PHEAP != 0) || n.Class == PPARAMREF { - break - } - *a = *n - return - - case OINDREG: - // Increase the refcount of the register so that igen's caller - // has to call Regfree. - if n.Reg != int16(Thearch.REGSP) { - reg[n.Reg-int16(Thearch.REGMIN)]++ - } - *a = *n - return - - case ODOT: - Igen(n.Left, a, res) - a.Xoffset += n.Xoffset - a.Type = n.Type - Fixlargeoffset(a) - return - - case ODOTPTR: - Cgenr(n.Left, a, res) - Cgen_checknil(a) - a.Op = OINDREG - a.Xoffset += n.Xoffset - a.Type = n.Type - Fixlargeoffset(a) - return - - case OCALLFUNC, OCALLMETH, OCALLINTER: - switch n.Op { - case OCALLFUNC: - cgen_call(n, 0) - - case OCALLMETH: - cgen_callmeth(n, 0) - - case OCALLINTER: - cgen_callinter(n, nil, 0) - } - - var flist Iter - fp := Structfirst(&flist, Getoutarg(n.Left.Type)) - *a = Node{} - a.Op = OINDREG - a.Reg = int16(Thearch.REGSP) - a.Addable = true - a.Xoffset = fp.Width - if HasLinkRegister() { - a.Xoffset += int64(Ctxt.Arch.Ptrsize) - } - a.Type = n.Type - return - - // Index of fixed-size array by constant can - // put the offset in the addressing. - // Could do the same for slice except that we need - // to use the real index for the bounds checking. - case OINDEX: - if Isfixedarray(n.Left.Type) || (Isptr[n.Left.Type.Etype] && Isfixedarray(n.Left.Left.Type)) { - if Isconst(n.Right, CTINT) { - // Compute &a. - if !Isptr[n.Left.Type.Etype] { - Igen(n.Left, a, res) - } else { - var n1 Node - Igen(n.Left, &n1, res) - Cgen_checknil(&n1) - Regalloc(a, Types[Tptr], res) - Thearch.Gmove(&n1, a) - Regfree(&n1) - a.Op = OINDREG - } - - // Compute &a[i] as &a + i*width. - a.Type = n.Type - - a.Xoffset += Mpgetfix(n.Right.Val.U.(*Mpint)) * n.Type.Width - Fixlargeoffset(a) - return - } - } - } - - Agenr(n, a, res) - a.Op = OINDREG - a.Type = n.Type -} - -// Bgen generates code for branches: -// -// if n == wantTrue { -// goto to -// } -func Bgen(n *Node, wantTrue bool, likely int, to *obj.Prog) { - bgenx(n, nil, wantTrue, likely, to) -} - -// Bvgen generates code for calculating boolean values: -// res = n == wantTrue -func Bvgen(n, res *Node, wantTrue bool) { - if Thearch.Ginsboolval == nil { - // Direct value generation not implemented for this architecture. - // Implement using jumps. - bvgenjump(n, res, wantTrue, true) - return - } - bgenx(n, res, wantTrue, 0, nil) -} - -// bvgenjump implements boolean value generation using jumps: -// if n == wantTrue { -// res = 1 -// } else { -// res = 0 -// } -// geninit controls whether n's Ninit is generated. -func bvgenjump(n, res *Node, wantTrue, geninit bool) { - init := n.Ninit - if !geninit { - n.Ninit = nil - } - p1 := Gbranch(obj.AJMP, nil, 0) - p2 := Pc - Thearch.Gmove(Nodbool(true), res) - p3 := Gbranch(obj.AJMP, nil, 0) - Patch(p1, Pc) - Bgen(n, wantTrue, 0, p2) - Thearch.Gmove(Nodbool(false), res) - Patch(p3, Pc) - n.Ninit = init -} - -// bgenx is the backend for Bgen and Bvgen. -// If res is nil, it generates a branch. -// Otherwise, it generates a boolean value. -func bgenx(n, res *Node, wantTrue bool, likely int, to *obj.Prog) { - if Debug['g'] != 0 { - fmt.Printf("\nbgenx wantTrue=%t likely=%d to=%v\n", wantTrue, likely, to) - Dump("n", n) - Dump("res", res) - } - - genval := res != nil - - if n == nil { - n = Nodbool(true) - } - - Genlist(n.Ninit) - - if n.Type == nil { - Convlit(&n, Types[TBOOL]) - if n.Type == nil { - return - } - } - - if n.Type.Etype != TBOOL { - Fatal("bgen: bad type %v for %v", n.Type, Oconv(int(n.Op), 0)) - } - - for n.Op == OCONVNOP { - n = n.Left - Genlist(n.Ninit) - } - - if Thearch.Bgen_float != nil && n.Left != nil && Isfloat[n.Left.Type.Etype] { - if genval { - bvgenjump(n, res, wantTrue, false) - return - } - Thearch.Bgen_float(n, wantTrue, likely, to) - return - } - - switch n.Op { - default: - if genval { - Cgen(n, res) - if !wantTrue { - Thearch.Gins(Thearch.Optoas(OXOR, Types[TUINT8]), Nodintconst(1), res) - } - return - } - - var tmp Node - Regalloc(&tmp, n.Type, nil) - Cgen(n, &tmp) - bgenNonZero(&tmp, nil, wantTrue, likely, to) - Regfree(&tmp) - return - - case ONAME: - if genval { - // 5g, 7g, and 9g might need a temporary or other help here, - // but they don't support direct generation of a bool value yet. - // We can fix that as we go. - switch Ctxt.Arch.Thechar { - case '5', '7', '9': - Fatal("genval 5g, 7g, 9g ONAMES not fully implemented") - } - Cgen(n, res) - if !wantTrue { - Thearch.Gins(Thearch.Optoas(OXOR, Types[TUINT8]), Nodintconst(1), res) - } - return - } - - if n.Addable && Ctxt.Arch.Thechar != '5' && Ctxt.Arch.Thechar != '7' && Ctxt.Arch.Thechar != '9' { - // no need for a temporary - bgenNonZero(n, nil, wantTrue, likely, to) - return - } - var tmp Node - Regalloc(&tmp, n.Type, nil) - Cgen(n, &tmp) - bgenNonZero(&tmp, nil, wantTrue, likely, to) - Regfree(&tmp) - return - - case OLITERAL: - // n is a constant. - if !Isconst(n, CTBOOL) { - Fatal("bgen: non-bool const %v\n", Nconv(n, obj.FmtLong)) - } - if genval { - Cgen(Nodbool(wantTrue == n.Val.U.(bool)), res) - return - } - // If n == wantTrue, jump; otherwise do nothing. - if wantTrue == n.Val.U.(bool) { - Patch(Gbranch(obj.AJMP, nil, likely), to) - } - return - - case OANDAND, OOROR: - and := (n.Op == OANDAND) == wantTrue - if genval { - p1 := Gbranch(obj.AJMP, nil, 0) - p2 := Gbranch(obj.AJMP, nil, 0) - Patch(p2, Pc) - Cgen(Nodbool(!and), res) - p3 := Gbranch(obj.AJMP, nil, 0) - Patch(p1, Pc) - Bgen(n.Left, wantTrue != and, 0, p2) - Bvgen(n.Right, res, wantTrue) - Patch(p3, Pc) - return - } - - if and { - p1 := Gbranch(obj.AJMP, nil, 0) - p2 := Gbranch(obj.AJMP, nil, 0) - Patch(p1, Pc) - Bgen(n.Left, !wantTrue, -likely, p2) - Bgen(n.Right, !wantTrue, -likely, p2) - p1 = Gbranch(obj.AJMP, nil, 0) - Patch(p1, to) - Patch(p2, Pc) - } else { - Bgen(n.Left, wantTrue, likely, to) - Bgen(n.Right, wantTrue, likely, to) - } - return - - case ONOT: // unary - if n.Left == nil || n.Left.Type == nil { - return - } - bgenx(n.Left, res, !wantTrue, likely, to) - return - - case OEQ, ONE, OLT, OGT, OLE, OGE: - if n.Left == nil || n.Left.Type == nil || n.Right == nil || n.Right.Type == nil { - return - } - } - - // n.Op is one of OEQ, ONE, OLT, OGT, OLE, OGE - nl := n.Left - nr := n.Right - a := int(n.Op) - - if !wantTrue { - if Isfloat[nr.Type.Etype] { - // Brcom is not valid on floats when NaN is involved. - ll := n.Ninit // avoid re-genning Ninit - n.Ninit = nil - if genval { - bgenx(n, res, true, likely, to) - Thearch.Gins(Thearch.Optoas(OXOR, Types[TUINT8]), Nodintconst(1), res) // res = !res - n.Ninit = ll - return - } - p1 := Gbranch(obj.AJMP, nil, 0) - p2 := Gbranch(obj.AJMP, nil, 0) - Patch(p1, Pc) - bgenx(n, res, true, -likely, p2) - Patch(Gbranch(obj.AJMP, nil, 0), to) - Patch(p2, Pc) - n.Ninit = ll - return - } - - a = Brcom(a) - } - wantTrue = true - - // make simplest on right - if nl.Op == OLITERAL || (nl.Ullman < nr.Ullman && nl.Ullman < UINF) { - a = Brrev(a) - nl, nr = nr, nl - } - - if Isslice(nl.Type) || Isinter(nl.Type) { - // front end should only leave cmp to literal nil - if (a != OEQ && a != ONE) || nr.Op != OLITERAL { - if Isslice(nl.Type) { - Yyerror("illegal slice comparison") - } else { - Yyerror("illegal interface comparison") - } - return - } - - var ptr Node - Igen(nl, &ptr, nil) - if Isslice(nl.Type) { - ptr.Xoffset += int64(Array_array) - } - ptr.Type = Types[Tptr] - var tmp Node - Regalloc(&tmp, ptr.Type, &ptr) - Cgen(&ptr, &tmp) - Regfree(&ptr) - bgenNonZero(&tmp, res, a == OEQ != wantTrue, likely, to) - Regfree(&tmp) - return - } - - if Iscomplex[nl.Type.Etype] { - complexbool(a, nl, nr, res, wantTrue, likely, to) - return - } - - if Ctxt.Arch.Regsize == 4 && Is64(nr.Type) { - if genval { - // TODO: Teach Cmp64 to generate boolean values and remove this. - bvgenjump(n, res, wantTrue, false) - return - } - if !nl.Addable || Isconst(nl, CTINT) { - nl = CgenTemp(nl) - } - if !nr.Addable { - nr = CgenTemp(nr) - } - Thearch.Cmp64(nl, nr, a, likely, to) - return - } - - if nr.Ullman >= UINF { - var n1 Node - Regalloc(&n1, nl.Type, nil) - Cgen(nl, &n1) - - var tmp Node - Tempname(&tmp, nl.Type) - Thearch.Gmove(&n1, &tmp) - Regfree(&n1) - - var n2 Node - Regalloc(&n2, nr.Type, nil) - Cgen(nr, &n2) - Regfree(&n2) - - Regalloc(&n1, nl.Type, nil) - Cgen(&tmp, &n1) - Regfree(&n1) - } else { - var n1 Node - if !nl.Addable && Ctxt.Arch.Thechar == '8' { - Tempname(&n1, nl.Type) - } else { - Regalloc(&n1, nl.Type, nil) - defer Regfree(&n1) - } - Cgen(nl, &n1) - nl = &n1 - - if Smallintconst(nr) && Ctxt.Arch.Thechar != '9' { - Thearch.Gins(Thearch.Optoas(OCMP, nr.Type), nl, nr) - bins(nr.Type, res, a, likely, to) - return - } - - if !nr.Addable && Ctxt.Arch.Thechar == '8' { - nr = CgenTemp(nr) - } - - var n2 Node - Regalloc(&n2, nr.Type, nil) - Cgen(nr, &n2) - nr = &n2 - Regfree(&n2) - } - - l, r := nl, nr - - // On x86, only < and <= work right with NaN; reverse if needed - if Ctxt.Arch.Thechar == '6' && Isfloat[nl.Type.Etype] && (a == OGT || a == OGE) { - l, r = r, l - a = Brrev(a) - } - - // Do the comparison. - Thearch.Gins(Thearch.Optoas(OCMP, nr.Type), l, r) - - // Handle floating point special cases. - // Note that 8g has Bgen_float and is handled above. - if Isfloat[nl.Type.Etype] { - switch Ctxt.Arch.Thechar { - case '5': - if genval { - Fatal("genval 5g Isfloat special cases not implemented") - } - switch n.Op { - case ONE: - Patch(Gbranch(Thearch.Optoas(OPS, nr.Type), nr.Type, likely), to) - Patch(Gbranch(Thearch.Optoas(a, nr.Type), nr.Type, likely), to) - default: - p := Gbranch(Thearch.Optoas(OPS, nr.Type), nr.Type, -likely) - Patch(Gbranch(Thearch.Optoas(a, nr.Type), nr.Type, likely), to) - Patch(p, Pc) - } - return - case '6': - switch n.Op { - case OEQ: - // neither NE nor P - if genval { - var reg Node - Regalloc(®, Types[TBOOL], nil) - Thearch.Ginsboolval(Thearch.Optoas(OEQ, nr.Type), ®) - Thearch.Ginsboolval(Thearch.Optoas(OPC, nr.Type), res) - Thearch.Gins(Thearch.Optoas(OAND, Types[TBOOL]), ®, res) - Regfree(®) - } else { - p1 := Gbranch(Thearch.Optoas(ONE, nr.Type), nil, -likely) - p2 := Gbranch(Thearch.Optoas(OPS, nr.Type), nil, -likely) - Patch(Gbranch(obj.AJMP, nil, 0), to) - Patch(p1, Pc) - Patch(p2, Pc) - } - return - case ONE: - // either NE or P - if genval { - var reg Node - Regalloc(®, Types[TBOOL], nil) - Thearch.Ginsboolval(Thearch.Optoas(ONE, nr.Type), ®) - Thearch.Ginsboolval(Thearch.Optoas(OPS, nr.Type), res) - Thearch.Gins(Thearch.Optoas(OOR, Types[TBOOL]), ®, res) - Regfree(®) - } else { - Patch(Gbranch(Thearch.Optoas(ONE, nr.Type), nil, likely), to) - Patch(Gbranch(Thearch.Optoas(OPS, nr.Type), nil, likely), to) - } - return - } - case '7', '9': - if genval { - Fatal("genval 7g, 9g Isfloat special cases not implemented") - } - switch n.Op { - // On arm64 and ppc64, <= and >= mishandle NaN. Must decompose into < or > and =. - // TODO(josh): Convert a <= b to b > a instead? - case OLE, OGE: - if a == OLE { - a = OLT - } else { - a = OGT - } - Patch(Gbranch(Thearch.Optoas(a, nr.Type), nr.Type, likely), to) - Patch(Gbranch(Thearch.Optoas(OEQ, nr.Type), nr.Type, likely), to) - return - } - } - } - - // Not a special case. Insert the conditional jump or value gen. - bins(nr.Type, res, a, likely, to) -} - -func bgenNonZero(n, res *Node, wantTrue bool, likely int, to *obj.Prog) { - // TODO: Optimize on systems that can compare to zero easily. - a := ONE - if !wantTrue { - a = OEQ - } - var zero Node - Nodconst(&zero, n.Type, 0) - Thearch.Gins(Thearch.Optoas(OCMP, n.Type), n, &zero) - bins(n.Type, res, a, likely, to) -} - -// bins inserts an instruction to handle the result of a compare. -// If res is non-nil, it inserts appropriate value generation instructions. -// If res is nil, it inserts a branch to to. -func bins(typ *Type, res *Node, a, likely int, to *obj.Prog) { - a = Thearch.Optoas(a, typ) - if res != nil { - // value gen - Thearch.Ginsboolval(a, res) - } else { - // jump - Patch(Gbranch(a, typ, likely), to) - } -} - -// 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: - t := n.Left.Type - if Isptr[t.Etype] { - break - } - off := stkof(n.Left) - if off == -1000 || off == +1000 { - return off - } - return off + n.Xoffset - - case OINDEX: - t := n.Left.Type - if !Isfixedarray(t) { - break - } - off := stkof(n.Left) - 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 // on stack but not sure exactly where - - case OCALLMETH, OCALLINTER, OCALLFUNC: - t := n.Left.Type - if Isptr[t.Etype] { - t = t.Type - } - - var flist Iter - t = Structfirst(&flist, Getoutarg(t)) - if t != nil { - w := t.Width - if HasLinkRegister() { - w += int64(Ctxt.Arch.Ptrsize) - } - return w - } - } - - // botch - probably failing to recognize address - // arithmetic on the above. eg INDEX and DOT - return -1000 // not on stack -} - -/* - * block copy: - * memmove(&ns, &n, w); - * if wb is true, needs write barrier. - */ -func sgen_wb(n *Node, ns *Node, w int64, wb bool) { - if Debug['g'] != 0 { - op := "sgen" - if wb { - op = "sgen-wb" - } - fmt.Printf("\n%s w=%d\n", op, w) - Dump("r", n) - Dump("res", ns) - } - - if n.Ullman >= UINF && ns.Ullman >= UINF { - Fatal("sgen UINF") - } - - if w < 0 { - Fatal("sgen copy %d", w) - } - - // If copying .args, that's all the results, so record definition sites - // for them for the liveness analysis. - if ns.Op == ONAME && ns.Sym.Name == ".args" { - for l := Curfn.Func.Dcl; l != nil; l = l.Next { - if l.N.Class == PPARAMOUT { - Gvardef(l.N) - } - } - } - - // Avoid taking the address for simple enough types. - if componentgen_wb(n, ns, wb) { - return - } - - if w == 0 { - // evaluate side effects only - var nodr Node - Regalloc(&nodr, Types[Tptr], nil) - Agen(ns, &nodr) - Agen(n, &nodr) - Regfree(&nodr) - return - } - - // offset on the stack - osrc := stkof(n) - odst := stkof(ns) - - if odst != -1000 { - // on stack, write barrier not needed after all - wb = false - } - - if osrc != -1000 && odst != -1000 && (osrc == 1000 || odst == 1000) || wb && osrc != -1000 { - // osrc and odst both on stack, and at least one is in - // an unknown position. Could generate code to test - // for forward/backward copy, but instead just copy - // to a temporary location first. - // - // OR: write barrier needed and source is on stack. - // Invoking the write barrier will use the stack to prepare its call. - // Copy to temporary. - var tmp Node - Tempname(&tmp, n.Type) - sgen_wb(n, &tmp, w, false) - sgen_wb(&tmp, ns, w, wb) - return - } - - if wb { - cgen_wbfat(n, ns) - return - } - - Thearch.Blockcopy(n, ns, osrc, odst, w) -} - -/* - * generate: - * call f - * proc=-1 normal call but no return - * proc=0 normal call - * proc=1 goroutine run in new proc - * proc=2 defer call save away stack - * proc=3 normal call to C pointer (not Go func value) -*/ -func Ginscall(f *Node, proc int) { - if f.Type != nil { - extra := int32(0) - if proc == 1 || proc == 2 { - extra = 2 * int32(Widthptr) - } - Setmaxarg(f.Type, extra) - } - - switch proc { - default: - Fatal("Ginscall: bad proc %d", proc) - - case 0, // normal call - -1: // normal call but no return - if f.Op == ONAME && f.Class == PFUNC { - if f == Deferreturn { - // Deferred calls will appear to be returning to - // the CALL deferreturn(SB) that we are about to emit. - // However, the stack trace code will show the line - // of the instruction byte before the return PC. - // To avoid that being an unrelated instruction, - // insert an actual hardware NOP that will have the right line number. - // This is different from obj.ANOP, which is a virtual no-op - // that doesn't make it into the instruction stream. - Thearch.Ginsnop() - } - - p := Thearch.Gins(obj.ACALL, nil, f) - Afunclit(&p.To, f) - if proc == -1 || Noreturn(p) { - Thearch.Gins(obj.AUNDEF, nil, nil) - } - break - } - - var reg Node - Nodreg(®, Types[Tptr], Thearch.REGCTXT) - var r1 Node - Nodreg(&r1, Types[Tptr], Thearch.REGCALLX) - Thearch.Gmove(f, ®) - reg.Op = OINDREG - Thearch.Gmove(®, &r1) - reg.Op = OREGISTER - Thearch.Gins(obj.ACALL, ®, &r1) - - case 3: // normal call of c function pointer - Thearch.Gins(obj.ACALL, nil, f) - - case 1, // call in new proc (go) - 2: // deferred call (defer) - var stk Node - - // size of arguments at 0(SP) - stk.Op = OINDREG - stk.Reg = int16(Thearch.REGSP) - stk.Xoffset = 0 - if HasLinkRegister() { - stk.Xoffset += int64(Ctxt.Arch.Ptrsize) - } - Thearch.Ginscon(Thearch.Optoas(OAS, Types[TINT32]), int64(Argsize(f.Type)), &stk) - - // FuncVal* at 8(SP) - stk.Xoffset = int64(Widthptr) - if HasLinkRegister() { - stk.Xoffset += int64(Ctxt.Arch.Ptrsize) - } - - var reg Node - Nodreg(®, Types[Tptr], Thearch.REGCALLX2) - Thearch.Gmove(f, ®) - Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), ®, &stk) - - if proc == 1 { - Ginscall(Newproc, 0) - } else { - if Hasdefer == 0 { - Fatal("hasdefer=0 but has defer") - } - Ginscall(Deferproc, 0) - } - - if proc == 2 { - Nodreg(®, Types[TINT32], Thearch.REGRETURN) - p := Thearch.Ginscmp(OEQ, Types[TINT32], ®, Nodintconst(0), +1) - cgen_ret(nil) - Patch(p, Pc) - } - } -} - -/* - * n is call to interface method. - * generate res = n. - */ -func cgen_callinter(n *Node, res *Node, proc int) { - i := n.Left - if i.Op != ODOTINTER { - Fatal("cgen_callinter: not ODOTINTER %v", Oconv(int(i.Op), 0)) - } - - f := i.Right // field - if f.Op != ONAME { - Fatal("cgen_callinter: not ONAME %v", Oconv(int(f.Op), 0)) - } - - i = i.Left // interface - - if !i.Addable { - var tmpi Node - Tempname(&tmpi, i.Type) - Cgen(i, &tmpi) - i = &tmpi - } - - Genlist(n.List) // assign the args - - // i is now addable, prepare an indirected - // register to hold its address. - var nodi Node - Igen(i, &nodi, res) // REG = &inter - - var nodsp Node - Nodindreg(&nodsp, Types[Tptr], Thearch.REGSP) - nodsp.Xoffset = 0 - if HasLinkRegister() { - nodsp.Xoffset += int64(Ctxt.Arch.Ptrsize) - } - if proc != 0 { - nodsp.Xoffset += 2 * int64(Widthptr) // leave room for size & fn - } - nodi.Type = Types[Tptr] - nodi.Xoffset += int64(Widthptr) - Cgen(&nodi, &nodsp) // {0, 8(nacl), or 16}(SP) = 8(REG) -- i.data - - var nodo Node - Regalloc(&nodo, Types[Tptr], res) - - nodi.Type = Types[Tptr] - nodi.Xoffset -= int64(Widthptr) - Cgen(&nodi, &nodo) // REG = 0(REG) -- i.tab - Regfree(&nodi) - - var nodr Node - Regalloc(&nodr, Types[Tptr], &nodo) - if n.Left.Xoffset == BADWIDTH { - Fatal("cgen_callinter: badwidth") - } - Cgen_checknil(&nodo) // in case offset is huge - nodo.Op = OINDREG - nodo.Xoffset = n.Left.Xoffset + 3*int64(Widthptr) + 8 - if proc == 0 { - // plain call: use direct c function pointer - more efficient - Cgen(&nodo, &nodr) // REG = 32+offset(REG) -- i.tab->fun[f] - proc = 3 - } else { - // go/defer. generate go func value. - Agen(&nodo, &nodr) // REG = &(32+offset(REG)) -- i.tab->fun[f] - } - - nodr.Type = n.Left.Type - Ginscall(&nodr, proc) - - Regfree(&nodr) - Regfree(&nodo) -} - -/* - * generate function call; - * proc=0 normal call - * proc=1 goroutine run in new proc - * proc=2 defer call save away stack - */ -func cgen_call(n *Node, proc int) { - if n == nil { - return - } - - var afun Node - if n.Left.Ullman >= UINF { - // if name involves a fn call - // precompute the address of the fn - Tempname(&afun, Types[Tptr]) - - Cgen(n.Left, &afun) - } - - Genlist(n.List) // assign the args - t := n.Left.Type - - // call tempname pointer - if n.Left.Ullman >= UINF { - var nod Node - Regalloc(&nod, Types[Tptr], nil) - Cgen_as(&nod, &afun) - nod.Type = t - Ginscall(&nod, proc) - Regfree(&nod) - return - } - - // call pointer - if n.Left.Op != ONAME || n.Left.Class != PFUNC { - var nod Node - Regalloc(&nod, Types[Tptr], nil) - Cgen_as(&nod, n.Left) - nod.Type = t - Ginscall(&nod, proc) - Regfree(&nod) - return - } - - // call direct - n.Left.Name.Method = true - - Ginscall(n.Left, proc) -} - -func HasLinkRegister() bool { - c := Ctxt.Arch.Thechar - return c != '6' && c != '8' -} - -/* - * call to n has already been generated. - * generate: - * res = return value from call. - */ -func cgen_callret(n *Node, res *Node) { - t := n.Left.Type - if t.Etype == TPTR32 || t.Etype == TPTR64 { - t = t.Type - } - - var flist Iter - fp := Structfirst(&flist, Getoutarg(t)) - if fp == nil { - Fatal("cgen_callret: nil") - } - - var nod Node - nod.Op = OINDREG - nod.Reg = int16(Thearch.REGSP) - nod.Addable = true - - nod.Xoffset = fp.Width - if HasLinkRegister() { - nod.Xoffset += int64(Ctxt.Arch.Ptrsize) - } - nod.Type = fp.Type - Cgen_as(res, &nod) -} - -/* - * call to n has already been generated. - * generate: - * res = &return value from call. - */ -func cgen_aret(n *Node, res *Node) { - t := n.Left.Type - if Isptr[t.Etype] { - t = t.Type - } - - var flist Iter - fp := Structfirst(&flist, Getoutarg(t)) - if fp == nil { - Fatal("cgen_aret: nil") - } - - var nod1 Node - nod1.Op = OINDREG - nod1.Reg = int16(Thearch.REGSP) - nod1.Addable = true - nod1.Xoffset = fp.Width - if HasLinkRegister() { - nod1.Xoffset += int64(Ctxt.Arch.Ptrsize) - } - nod1.Type = fp.Type - - if res.Op != OREGISTER { - var nod2 Node - Regalloc(&nod2, Types[Tptr], res) - Agen(&nod1, &nod2) - Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &nod2, res) - Regfree(&nod2) - } else { - Agen(&nod1, res) - } -} - -/* - * generate return. - * n->left is assignments to return values. - */ -func cgen_ret(n *Node) { - if n != nil { - Genlist(n.List) // copy out args - } - if Hasdefer != 0 { - Ginscall(Deferreturn, 0) - } - Genlist(Curfn.Func.Exit) - p := Thearch.Gins(obj.ARET, nil, nil) - if n != nil && n.Op == ORETJMP { - p.To.Type = obj.TYPE_MEM - p.To.Name = obj.NAME_EXTERN - p.To.Sym = Linksym(n.Left.Sym) - } -} - -/* - * generate division according to op, one of: - * res = nl / nr - * res = nl % nr - */ -func cgen_div(op int, nl *Node, nr *Node, res *Node) { - var w int - - // TODO(rsc): arm64 needs to support the relevant instructions - // in peep and optoas in order to enable this. - // TODO(rsc): ppc64 needs to support the relevant instructions - // in peep and optoas in order to enable this. - if nr.Op != OLITERAL || Ctxt.Arch.Thechar == '7' || Ctxt.Arch.Thechar == '9' { - goto longdiv - } - w = int(nl.Type.Width * 8) - - // Front end handled 32-bit division. We only need to handle 64-bit. - // try to do division by multiply by (2^w)/d - // see hacker's delight chapter 10 - switch Simtype[nl.Type.Etype] { - default: - goto longdiv - - case TUINT64: - var m Magic - m.W = w - m.Ud = uint64(Mpgetfix(nr.Val.U.(*Mpint))) - Umagic(&m) - if m.Bad != 0 { - break - } - if op == OMOD { - goto longmod - } - - var n1 Node - Cgenr(nl, &n1, nil) - var n2 Node - Nodconst(&n2, nl.Type, int64(m.Um)) - var n3 Node - Regalloc(&n3, nl.Type, res) - Thearch.Cgen_hmul(&n1, &n2, &n3) - - if m.Ua != 0 { - // need to add numerator accounting for overflow - Thearch.Gins(Thearch.Optoas(OADD, nl.Type), &n1, &n3) - - Nodconst(&n2, nl.Type, 1) - Thearch.Gins(Thearch.Optoas(ORROTC, nl.Type), &n2, &n3) - Nodconst(&n2, nl.Type, int64(m.S)-1) - Thearch.Gins(Thearch.Optoas(ORSH, nl.Type), &n2, &n3) - } else { - Nodconst(&n2, nl.Type, int64(m.S)) - Thearch.Gins(Thearch.Optoas(ORSH, nl.Type), &n2, &n3) // shift dx - } - - Thearch.Gmove(&n3, res) - Regfree(&n1) - Regfree(&n3) - return - - case TINT64: - var m Magic - m.W = w - m.Sd = Mpgetfix(nr.Val.U.(*Mpint)) - Smagic(&m) - if m.Bad != 0 { - break - } - if op == OMOD { - goto longmod - } - - var n1 Node - Cgenr(nl, &n1, res) - var n2 Node - Nodconst(&n2, nl.Type, m.Sm) - var n3 Node - Regalloc(&n3, nl.Type, nil) - Thearch.Cgen_hmul(&n1, &n2, &n3) - - if m.Sm < 0 { - // need to add numerator - Thearch.Gins(Thearch.Optoas(OADD, nl.Type), &n1, &n3) - } - - Nodconst(&n2, nl.Type, int64(m.S)) - Thearch.Gins(Thearch.Optoas(ORSH, nl.Type), &n2, &n3) // shift n3 - - Nodconst(&n2, nl.Type, int64(w)-1) - - Thearch.Gins(Thearch.Optoas(ORSH, nl.Type), &n2, &n1) // -1 iff num is neg - Thearch.Gins(Thearch.Optoas(OSUB, nl.Type), &n1, &n3) // added - - if m.Sd < 0 { - // this could probably be removed - // by factoring it into the multiplier - Thearch.Gins(Thearch.Optoas(OMINUS, nl.Type), nil, &n3) - } - - Thearch.Gmove(&n3, res) - Regfree(&n1) - Regfree(&n3) - return - } - - goto longdiv - - // division and mod using (slow) hardware instruction -longdiv: - Thearch.Dodiv(op, nl, nr, res) - - return - - // mod using formula A%B = A-(A/B*B) but - // we know that there is a fast algorithm for A/B -longmod: - var n1 Node - Regalloc(&n1, nl.Type, res) - - Cgen(nl, &n1) - var n2 Node - Regalloc(&n2, nl.Type, nil) - cgen_div(ODIV, &n1, nr, &n2) - a := Thearch.Optoas(OMUL, nl.Type) - if w == 8 { - // use 2-operand 16-bit multiply - // because there is no 2-operand 8-bit multiply - a = Thearch.Optoas(OMUL, Types[TINT16]) // XXX was IMULW - } - - if !Smallintconst(nr) { - var n3 Node - Regalloc(&n3, nl.Type, nil) - Cgen(nr, &n3) - Thearch.Gins(a, &n3, &n2) - Regfree(&n3) - } else { - Thearch.Gins(a, nr, &n2) - } - Thearch.Gins(Thearch.Optoas(OSUB, nl.Type), &n2, &n1) - Thearch.Gmove(&n1, res) - Regfree(&n1) - Regfree(&n2) -} - -func Fixlargeoffset(n *Node) { - if n == nil { - return - } - if n.Op != OINDREG { - return - } - if n.Reg == int16(Thearch.REGSP) { // stack offset cannot be large - return - } - if n.Xoffset != int64(int32(n.Xoffset)) { - // offset too large, add to register instead. - a := *n - - a.Op = OREGISTER - a.Type = Types[Tptr] - a.Xoffset = 0 - Cgen_checknil(&a) - Thearch.Ginscon(Thearch.Optoas(OADD, Types[Tptr]), n.Xoffset, &a) - 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++ - } -} - -// 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) { - 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) - // 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.(string)))) - 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.(string))) - } - if Isconst(&i, CTINT) { - 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.(*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.(*Mpint), 0) < 0 || bound >= 0 && mpcmpfixc(k.Val.U.(*Mpint), 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.(*Mpint), n2.Val.U.(*Mpint)) == 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.(*Mpint), n2.Val.U.(*Mpint)) <= 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) { - // 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) - } - - 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.(*Mpint))-Mpgetfix(i.Val.U.(*Mpint))) - if Debug_slice > 0 { - Warn("slice: result len == %d", Mpgetfix(j.Val.U.(*Mpint))) - } - 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.(*Mpint)) - 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.(*Mpint))-Mpgetfix(i.Val.U.(*Mpint))) - if Debug_slice > 0 { - Warn("slice: result cap == %d", Mpgetfix(k.Val.U.(*Mpint))) - } - 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.(*Mpint)) - 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.(*Mpint))*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/closure.go b/src/cmd/internal/gc/closure.go deleted file mode 100644 index b51e74b77d..0000000000 --- a/src/cmd/internal/gc/closure.go +++ /dev/null @@ -1,689 +0,0 @@ -// Copyright 2009 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 gc - -import ( - "cmd/internal/obj" - "fmt" -) - -/* - * function literals aka closures - */ -func closurehdr(ntype *Node) { - var name *Node - var a *Node - - n := Nod(OCLOSURE, nil, nil) - n.Ntype = ntype - n.Funcdepth = Funcdepth - n.Func.Outerfunc = Curfn - - funchdr(n) - - // steal ntype's argument names and - // leave a fresh copy in their place. - // references to these variables need to - // refer to the variables in the external - // function declared below; see walkclosure. - n.List = ntype.List - - n.Rlist = ntype.Rlist - ntype.List = nil - ntype.Rlist = nil - for l := n.List; l != nil; l = l.Next { - name = l.N.Left - if name != nil { - name = newname(name.Sym) - } - a = Nod(ODCLFIELD, name, l.N.Right) - a.Isddd = l.N.Isddd - if name != nil { - name.Isddd = a.Isddd - } - ntype.List = list(ntype.List, a) - } - - for l := n.Rlist; l != nil; l = l.Next { - name = l.N.Left - if name != nil { - name = newname(name.Sym) - } - ntype.Rlist = list(ntype.Rlist, Nod(ODCLFIELD, name, l.N.Right)) - } -} - -func closurebody(body *NodeList) *Node { - if body == nil { - body = list1(Nod(OEMPTY, nil, nil)) - } - - func_ := Curfn - func_.Nbody = body - func_.Func.Endlineno = lineno - funcbody(func_) - - // closure-specific variables are hanging off the - // ordinary ones in the symbol table; see oldname. - // unhook them. - // make the list of pointers for the closure call. - 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) - } - - return func_ -} - -func typecheckclosure(func_ *Node, top int) { - var n *Node - - for l := func_.Func.Cvars; l != nil; l = l.Next { - n = l.N.Closure - 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.Name.Decldepth == decldepth { - n.Assigned = false - } - } - } - - 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.Name.Decldepth = 1 - } - } - - oldfn := Curfn - typecheck(&func_.Ntype, Etype) - func_.Type = func_.Ntype.Type - func_.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 - // ready to type check code yet; we'll check it later, because the - // underlying closure function we create is added to xtop. - if Curfn != nil && func_.Type != nil { - Curfn = func_ - olddd := decldepth - decldepth = 1 - typechecklist(func_.Nbody, Etop) - decldepth = olddd - Curfn = oldfn - } - - // Create top-level function - xtop = list(xtop, makeclosure(func_)) -} - -// closurename returns name for OCLOSURE n. -// It is not as simple as it ought to be, because we typecheck nested closures -// starting from the innermost one. So when we check the inner closure, -// we don't yet have name for the outer closure. This function uses recursion -// to generate names all the way up if necessary. - -var closurename_closgen int - -func closurename(n *Node) *Sym { - if n.Sym != nil { - return n.Sym - } - gen := 0 - outer := "" - prefix := "" - if n.Func.Outerfunc == nil { - // Global closure. - outer = "glob" - - prefix = "func" - closurename_closgen++ - gen = closurename_closgen - } else if n.Func.Outerfunc.Op == ODCLFUNC { - // The outermost closure inside of a named function. - outer = n.Func.Outerfunc.Nname.Sym.Name - - prefix = "func" - - // Yes, functions can be named _. - // Can't use function closgen in such case, - // because it would lead to name clashes. - if !isblank(n.Func.Outerfunc.Nname) { - n.Func.Outerfunc.Func.Closgen++ - gen = n.Func.Outerfunc.Func.Closgen - } else { - closurename_closgen++ - gen = closurename_closgen - } - } else if n.Func.Outerfunc.Op == OCLOSURE { - // Nested closure, recurse. - outer = closurename(n.Func.Outerfunc).Name - - prefix = "" - n.Func.Outerfunc.Func.Closgen++ - gen = n.Func.Outerfunc.Func.Closgen - } else { - Fatal("closurename called for %v", Nconv(n, obj.FmtShort)) - } - n.Sym = Lookupf("%s.%s%d", outer, prefix, gen) - return n.Sym -} - -func makeclosure(func_ *Node) *Node { - /* - * wrap body in external function - * that begins by reading closure parameters. - */ - xtype := Nod(OTFUNC, nil, nil) - - xtype.List = func_.List - xtype.Rlist = func_.Rlist - - // create the function - xfunc := Nod(ODCLFUNC, nil, nil) - - xfunc.Nname = newfuncname(closurename(func_)) - xfunc.Nname.Sym.Flags |= SymExported // disable export - xfunc.Nname.Ntype = xtype - xfunc.Nname.Defn = xfunc - declare(xfunc.Nname, PFUNC) - xfunc.Nname.Funcdepth = func_.Funcdepth - xfunc.Funcdepth = func_.Funcdepth - xfunc.Func.Endlineno = func_.Func.Endlineno - - xfunc.Nbody = func_.Nbody - xfunc.Func.Dcl = concat(func_.Func.Dcl, xfunc.Func.Dcl) - if xfunc.Nbody == nil { - Fatal("empty body - won't generate any code") - } - typecheck(&xfunc, Etop) - - xfunc.Closure = func_ - func_.Closure = xfunc - - func_.Nbody = nil - func_.List = nil - func_.Rlist = nil - - return xfunc -} - -// capturevars is called in a separate phase after all typechecking is done. -// It decides whether each variable captured by a closure should be captured -// by value or by reference. -// We use value capturing for values <= 128 bytes that are never reassigned -// after capturing (effectively constant). -func capturevars(xfunc *Node) { - var v *Node - var outer *Node - - lno := int(lineno) - lineno = xfunc.Lineno - - func_ := xfunc.Closure - func_.Func.Enter = nil - for l := func_.Func.Cvars; l != nil; l = l.Next { - v = l.N - if v.Type == nil { - // if v->type is nil, it means v looked like it was - // going to be used in the closure but wasn't. - // this happens because when parsing a, b, c := f() - // the a, b, c gets parsed as references to older - // a, b, c before the parser figures out this is a - // declaration. - v.Op = OXXX - - continue - } - - // type check the & of closed variables outside the closure, - // so that the outer frame also grabs them and knows they escape. - dowidth(v.Type) - - outer = v.Outerexpr - v.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 { - v.Name.Byval = true - } else { - v.Closure.Addrtaken = true - outer = Nod(OADDR, outer, nil) - } - - if Debug['m'] > 1 { - var name *Sym - if v.Curfn != nil && v.Curfn.Nname != nil { - name = v.Curfn.Nname.Sym - } - how := "ref" - 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)) - } - - typecheck(&outer, Erv) - func_.Func.Enter = list(func_.Func.Enter, outer) - } - - lineno = int32(lno) -} - -// transformclosure is called in a separate phase after escape analysis. -// It transform closure bodies to properly reference captured variables. -func transformclosure(xfunc *Node) { - lno := int(lineno) - lineno = xfunc.Lineno - func_ := xfunc.Closure - - if func_.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) - // will complete the transformation later. - // For illustration, the following closure: - // func(a int) { - // println(byval) - // byref++ - // }(42) - // becomes: - // func(a int, byval int, &byref *int) { - // println(byval) - // (*&byref)++ - // }(42, byval, &byref) - - // f is ONAME of the actual function. - f := xfunc.Nname - - // Get pointer to input arguments and rewind to the end. - // We are going to append captured variables to input args. - param := &getinargx(f.Type).Type - - for ; *param != nil; param = &(*param).Down { - } - var v *Node - var addr *Node - var fld *Type - for l := func_.Func.Cvars; l != nil; l = l.Next { - v = l.N - if v.Op == OXXX { - continue - } - fld = typ(TFIELD) - fld.Funarg = 1 - if v.Name.Byval { - // If v is captured by value, we merely downgrade it to PPARAM. - v.Class = PPARAM - - v.Ullman = 1 - fld.Nname = v - } else { - // If v of type T is captured by reference, - // we introduce function param &v *T - // and v remains PPARAMREF with &v heapaddr - // (accesses will implicitly deref &v). - addr = newname(Lookupf("&%s", v.Sym.Name)) - addr.Type = Ptrto(v.Type) - addr.Class = PPARAM - v.Name.Heapaddr = addr - fld.Nname = addr - } - - fld.Type = fld.Nname.Type - fld.Sym = fld.Nname.Sym - - // Declare the new param and append it to input arguments. - xfunc.Func.Dcl = list(xfunc.Func.Dcl, fld.Nname) - - *param = fld - param = &fld.Down - } - - // Recalculate param offsets. - if f.Type.Width > 0 { - Fatal("transformclosure: width is already calculated") - } - dowidth(f.Type) - xfunc.Type = f.Type // update type of ODCLFUNC - } else { - // The closure is not called, so it is going to stay as closure. - nvar := 0 - - var body *NodeList - offset := int64(Widthptr) - var addr *Node - var v *Node - var cv *Node - for l := func_.Func.Cvars; l != nil; l = l.Next { - v = l.N - if v.Op == OXXX { - continue - } - nvar++ - - // cv refers to the field inside of closure OSTRUCTLIT. - cv = Nod(OCLOSUREVAR, nil, nil) - - cv.Type = v.Type - 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.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 - v.Class = PAUTO - - v.Ullman = 1 - xfunc.Func.Dcl = list(xfunc.Func.Dcl, v) - body = list(body, Nod(OAS, v, cv)) - } else { - // 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.Class = PAUTO - addr.Used = true - addr.Curfn = xfunc - xfunc.Func.Dcl = list(xfunc.Func.Dcl, addr) - v.Name.Heapaddr = addr - if v.Name.Byval { - cv = Nod(OADDR, cv, nil) - } - body = list(body, Nod(OAS, addr, cv)) - } - } - - typechecklist(body, Etop) - walkstmtlist(body) - xfunc.Func.Enter = body - xfunc.Func.Needctxt = nvar > 0 - } - - lineno = int32(lno) -} - -func walkclosure(func_ *Node, init **NodeList) *Node { - // If no closure vars, don't bother wrapping. - if func_.Func.Cvars == nil { - return func_.Closure.Nname - } - - // Create closure in the form of a composite literal. - // supposing the closure captures an int i and a string s - // and has one float64 argument and no results, - // the generated code looks like: - // - // clos = &struct{.F uintptr; i *int; s *string}{func.1, &i, &s} - // - // The use of the struct provides type information to the garbage - // collector so that it can walk the closure. We could use (in this case) - // [3]unsafe.Pointer instead, but that would leave the gc in the dark. - // The information appears in the binary in the form of type descriptors; - // the struct is unnamed so that closures in multiple packages with the - // same struct type can share the descriptor. - - typ := Nod(OTSTRUCT, nil, nil) - - typ.List = list1(Nod(ODCLFIELD, newname(Lookup(".F")), typenod(Types[TUINTPTR]))) - var typ1 *Node - var v *Node - for l := func_.Func.Cvars; l != nil; l = l.Next { - v = l.N - if v.Op == OXXX { - continue - } - typ1 = typenod(v.Type) - if !v.Name.Byval { - typ1 = Nod(OIND, typ1, nil) - } - typ.List = list(typ.List, Nod(ODCLFIELD, newname(v.Sym), typ1)) - } - - 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) - - // Force type conversion from *struct to the func type. - clos = Nod(OCONVNOP, clos, nil) - - clos.Type = func_.Type - - typecheck(&clos, Erv) - - // typecheck will insert a PTRLIT node under CONVNOP, - // tag it with escape analysis result. - clos.Left.Esc = func_.Esc - - // non-escaping temp to use, if any. - // orderexpr did not compute the type; fill it in now. - if func_.Alloc != nil { - func_.Alloc.Type = clos.Left.Left.Type - func_.Alloc.Orig.Type = func_.Alloc.Type - clos.Left.Right = func_.Alloc - func_.Alloc = nil - } - - walkexpr(&clos, init) - - return clos -} - -func typecheckpartialcall(fn *Node, sym *Node) { - switch fn.Op { - case ODOTINTER, ODOTMETH: - break - - default: - Fatal("invalid typecheckpartialcall") - } - - // Create top-level function. - fn.Nname = makepartialcall(fn, fn.Type, sym) - - fn.Right = sym - fn.Op = OCALLPART - fn.Type = fn.Nname.Type -} - -var makepartialcall_gopkg *Pkg - -func makepartialcall(fn *Node, t0 *Type, meth *Node) *Node { - var p string - - rcvrtype := fn.Left.Type - if exportname(meth.Sym.Name) { - p = fmt.Sprintf("(%v).%s-fm", Tconv(rcvrtype, obj.FmtLeft|obj.FmtShort), meth.Sym.Name) - } else { - p = fmt.Sprintf("(%v).(%v)-fm", Tconv(rcvrtype, obj.FmtLeft|obj.FmtShort), Sconv(meth.Sym, obj.FmtLeft)) - } - basetype := rcvrtype - if Isptr[rcvrtype.Etype] { - basetype = basetype.Type - } - if basetype.Etype != TINTER && basetype.Sym == nil { - Fatal("missing base type for %v", rcvrtype) - } - - var spkg *Pkg - if basetype.Sym != nil { - spkg = basetype.Sym.Pkg - } - if spkg == nil { - if makepartialcall_gopkg == nil { - makepartialcall_gopkg = mkpkg("go") - } - spkg = makepartialcall_gopkg - } - - sym := Pkglookup(p, spkg) - - if sym.Flags&SymUniq != 0 { - return sym.Def - } - sym.Flags |= SymUniq - - savecurfn := Curfn - Curfn = nil - - xtype := Nod(OTFUNC, nil, nil) - i := 0 - var l *NodeList - var callargs *NodeList - ddd := false - xfunc := Nod(ODCLFUNC, nil, nil) - Curfn = xfunc - var fld *Node - var n *Node - for t := getinargx(t0).Type; t != nil; t = t.Down { - n = newname(Lookupf("a%d", i)) - i++ - n.Class = PPARAM - xfunc.Func.Dcl = list(xfunc.Func.Dcl, n) - callargs = list(callargs, n) - fld = Nod(ODCLFIELD, n, typenod(t.Type)) - if t.Isddd { - fld.Isddd = true - ddd = true - } - - l = list(l, fld) - } - - xtype.List = l - i = 0 - l = nil - var retargs *NodeList - for t := getoutargx(t0).Type; t != nil; t = t.Down { - n = newname(Lookupf("r%d", i)) - i++ - n.Class = PPARAMOUT - xfunc.Func.Dcl = list(xfunc.Func.Dcl, n) - retargs = list(retargs, n) - l = list(l, Nod(ODCLFIELD, n, typenod(t.Type))) - } - - xtype.Rlist = l - - xfunc.Func.Dupok = true - xfunc.Nname = newfuncname(sym) - xfunc.Nname.Sym.Flags |= SymExported // disable export - xfunc.Nname.Ntype = xtype - xfunc.Nname.Defn = xfunc - declare(xfunc.Nname, PFUNC) - - // Declare and initialize variable holding receiver. - - xfunc.Func.Needctxt = true - cv := Nod(OCLOSUREVAR, nil, nil) - cv.Xoffset = int64(Widthptr) - cv.Type = rcvrtype - if int(cv.Type.Align) > Widthptr { - cv.Xoffset = int64(cv.Type.Align) - } - ptr := Nod(ONAME, nil, nil) - ptr.Sym = Lookup("rcvr") - ptr.Class = PAUTO - ptr.Addable = true - ptr.Ullman = 1 - ptr.Used = true - ptr.Curfn = xfunc - xfunc.Func.Dcl = list(xfunc.Func.Dcl, ptr) - var body *NodeList - if Isptr[rcvrtype.Etype] || Isinter(rcvrtype) { - ptr.Ntype = typenod(rcvrtype) - body = list(body, Nod(OAS, ptr, cv)) - } else { - ptr.Ntype = typenod(Ptrto(rcvrtype)) - body = list(body, Nod(OAS, ptr, Nod(OADDR, cv, nil))) - } - - call := Nod(OCALL, Nod(OXDOT, ptr, meth), nil) - call.List = callargs - call.Isddd = ddd - if t0.Outtuple == 0 { - body = list(body, call) - } else { - n := Nod(OAS2, nil, nil) - n.List = retargs - n.Rlist = list1(call) - body = list(body, n) - n = Nod(ORETURN, nil, nil) - body = list(body, n) - } - - xfunc.Nbody = body - - typecheck(&xfunc, Etop) - sym.Def = xfunc - xtop = list(xtop, xfunc) - Curfn = savecurfn - - return xfunc -} - -func walkpartialcall(n *Node, init **NodeList) *Node { - // Create closure in the form of a composite literal. - // For x.M with receiver (x) type T, the generated code looks like: - // - // clos = &struct{F uintptr; R T}{M.T·f, x} - // - // Like walkclosure above. - - if Isinter(n.Left.Type) { - // Trigger panic for method on nil interface now. - // Otherwise it happens in the wrapper and is confusing. - n.Left = cheapexpr(n.Left, init) - - checknil(n.Left, init) - } - - typ := Nod(OTSTRUCT, nil, nil) - typ.List = list1(Nod(ODCLFIELD, newname(Lookup("F")), typenod(Types[TUINTPTR]))) - typ.List = list(typ.List, Nod(ODCLFIELD, newname(Lookup("R")), typenod(n.Left.Type))) - - clos := Nod(OCOMPLIT, nil, Nod(OIND, typ, nil)) - clos.Esc = n.Esc - clos.Right.Implicit = true - clos.List = list1(Nod(OCFUNC, n.Nname.Nname, nil)) - clos.List = list(clos.List, n.Left) - - // Force type conversion from *struct to the func type. - clos = Nod(OCONVNOP, clos, nil) - - clos.Type = n.Type - - typecheck(&clos, Erv) - - // typecheck will insert a PTRLIT node under CONVNOP, - // tag it with escape analysis result. - clos.Left.Esc = n.Esc - - // non-escaping temp to use, if any. - // orderexpr did not compute the type; fill it in now. - if n.Alloc != nil { - n.Alloc.Type = clos.Left.Left.Type - n.Alloc.Orig.Type = n.Alloc.Type - clos.Left.Right = n.Alloc - n.Alloc = nil - } - - walkexpr(&clos, init) - - return clos -} diff --git a/src/cmd/internal/gc/const.go b/src/cmd/internal/gc/const.go deleted file mode 100644 index 986e2c3337..0000000000 --- a/src/cmd/internal/gc/const.go +++ /dev/null @@ -1,1717 +0,0 @@ -// Copyright 2009 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 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.(*Mpint)) -} - -// 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.(*Mpint), 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.(*Mpint).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.(bool) -} - -/* - * truncate float literal fv to 32-bit or 64-bit precision - * according to type; return truncated value. - */ -func truncfltlit(oldv *Mpflt, t *Type) *Mpflt { - if t == nil { - return oldv - } - - var v Val - v.Ctype = CTFLT - v.U = oldv - overflow(v, t) - - fv := newMpflt() - mpmovefltflt(fv, oldv) - - // convert large precision literal floating - // into limited precision (float64 or float32) - switch t.Etype { - case TFLOAT64: - d := mpgetflt(fv) - Mpmovecflt(fv, d) - - case TFLOAT32: - d := mpgetflt32(fv) - Mpmovecflt(fv, d) - } - - return fv -} - -/* - * convert n, if literal, to type t. - * implicit conversion. - */ -func Convlit(np **Node, t *Type) { - convlit1(np, t, false) -} - -/* - * convert n, if literal, to type t. - * return a new node if necessary - * (if n is a named constant, can't edit n->type directly). - */ -func convlit1(np **Node, t *Type, explicit bool) { - n := *np - if n == nil || t == nil || n.Type == nil || isideal(t) || n.Type == t { - return - } - if !explicit && !isideal(n.Type) { - return - } - - if n.Op == OLITERAL { - nn := Nod(OXXX, nil, nil) - *nn = *n - n = nn - *np = n - } - - switch n.Op { - default: - if n.Type == idealbool { - if t.Etype == TBOOL { - n.Type = t - } else { - n.Type = Types[TBOOL] - } - } - - if n.Type.Etype == TIDEAL { - Convlit(&n.Left, t) - Convlit(&n.Right, t) - n.Type = t - } - - return - - // target is invalid type for a constant? leave alone. - case OLITERAL: - if !okforconst[t.Etype] && n.Type.Etype != TNIL { - defaultlit(&n, nil) - *np = n - return - } - - case OLSH, ORSH: - convlit1(&n.Left, t, explicit && isideal(n.Left.Type)) - t = n.Left.Type - if t != nil && t.Etype == TIDEAL && n.Val.Ctype != CTINT { - n.Val = toint(n.Val) - } - if t != nil && !Isint[t.Etype] { - Yyerror("invalid operation: %v (shift of type %v)", n, t) - t = nil - } - - n.Type = t - return - - case OCOMPLEX: - if n.Type.Etype == TIDEAL { - switch t.Etype { - // If trying to convert to non-complex type, - // leave as complex128 and let typechecker complain. - default: - t = Types[TCOMPLEX128] - fallthrough - - //fallthrough - case TCOMPLEX128: - n.Type = t - - Convlit(&n.Left, Types[TFLOAT64]) - Convlit(&n.Right, Types[TFLOAT64]) - - case TCOMPLEX64: - n.Type = t - Convlit(&n.Left, Types[TFLOAT32]) - Convlit(&n.Right, Types[TFLOAT32]) - } - } - - return - } - - // avoided repeated calculations, errors - if Eqtype(n.Type, t) { - return - } - - ct := consttype(n) - var et int - if ct < 0 { - goto bad - } - - et = int(t.Etype) - if et == TINTER { - if ct == CTNIL && n.Type == Types[TNIL] { - n.Type = t - return - } - - defaultlit(np, nil) - return - } - - switch ct { - default: - goto bad - - case CTNIL: - switch et { - default: - n.Type = nil - goto bad - - // let normal conversion code handle it - case TSTRING: - return - - case TARRAY: - if !Isslice(t) { - goto bad - } - - case TPTR32, - TPTR64, - TINTER, - TMAP, - TCHAN, - TFUNC, - TUNSAFEPTR: - break - - // A nil literal may be converted to uintptr - // if it is an unsafe.Pointer - case TUINTPTR: - if n.Type.Etype == TUNSAFEPTR { - n.Val.U = new(Mpint) - Mpmovecfix(n.Val.U.(*Mpint), 0) - n.Val.Ctype = CTINT - } else { - goto bad - } - } - - case CTSTR, CTBOOL: - if et != int(n.Type.Etype) { - goto bad - } - - 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 { - default: - goto bad - - case CTCPLX, CTFLT, CTRUNE: - n.Val = toint(n.Val) - fallthrough - - // flowthrough - case CTINT: - overflow(n.Val, t) - } - } else if Isfloat[et] { - switch ct { - default: - goto bad - - case CTCPLX, CTINT, CTRUNE: - n.Val = toflt(n.Val) - fallthrough - - // flowthrough - case CTFLT: - n.Val.U = truncfltlit(n.Val.U.(*Mpflt), t) - } - } else if Iscomplex[et] { - switch ct { - default: - goto bad - - case CTFLT, CTINT, CTRUNE: - n.Val = tocplx(n.Val) - - case CTCPLX: - overflow(n.Val, t) - } - } else if et == TSTRING && (ct == CTINT || ct == CTRUNE) && explicit { - n.Val = tostr(n.Val) - } else { - goto bad - } - } - - n.Type = t - return - -bad: - if n.Diag == 0 { - if t.Broke == 0 { - Yyerror("cannot convert %v to type %v", n, t) - } - n.Diag = 1 - } - - if isideal(n.Type) { - defaultlit(&n, nil) - *np = n - } -} - -func copyval(v Val) Val { - switch v.Ctype { - case CTINT, CTRUNE: - i := new(Mpint) - mpmovefixfix(i, v.U.(*Mpint)) - v.U = i - - case CTFLT: - f := newMpflt() - mpmovefltflt(f, v.U.(*Mpflt)) - v.U = f - - case CTCPLX: - c := new(Mpcplx) - mpmovefltflt(&c.Real, &v.U.(*Mpcplx).Real) - mpmovefltflt(&c.Imag, &v.U.(*Mpcplx).Imag) - v.U = c - } - - return v -} - -func tocplx(v Val) Val { - switch v.Ctype { - case CTINT, CTRUNE: - c := new(Mpcplx) - Mpmovefixflt(&c.Real, v.U.(*Mpint)) - Mpmovecflt(&c.Imag, 0.0) - v.Ctype = CTCPLX - v.U = c - - case CTFLT: - c := new(Mpcplx) - mpmovefltflt(&c.Real, v.U.(*Mpflt)) - Mpmovecflt(&c.Imag, 0.0) - v.Ctype = CTCPLX - v.U = c - } - - return v -} - -func toflt(v Val) Val { - switch v.Ctype { - case CTINT, CTRUNE: - f := newMpflt() - Mpmovefixflt(f, v.U.(*Mpint)) - v.Ctype = CTFLT - v.U = f - - case CTCPLX: - f := newMpflt() - 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 = f - } - - return v -} - -func toint(v Val) Val { - switch v.Ctype { - case CTRUNE: - v.Ctype = CTINT - - case CTFLT: - i := new(Mpint) - if mpmovefltfix(i, v.U.(*Mpflt)) < 0 { - Yyerror("constant %v truncated to integer", Fconv(v.U.(*Mpflt), obj.FmtSharp)) - } - v.Ctype = CTINT - v.U = i - - case CTCPLX: - i := new(Mpint) - 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.(*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 = i - } - - return v -} - -func doesoverflow(v Val, t *Type) bool { - switch v.Ctype { - case CTINT, CTRUNE: - if !Isint[t.Etype] { - Fatal("overflow: %v integer constant", t) - } - if Mpcmpfixfix(v.U.(*Mpint), Minintval[t.Etype]) < 0 || Mpcmpfixfix(v.U.(*Mpint), Maxintval[t.Etype]) > 0 { - return true - } - - case CTFLT: - if !Isfloat[t.Etype] { - Fatal("overflow: %v floating-point constant", t) - } - if mpcmpfltflt(v.U.(*Mpflt), minfltval[t.Etype]) <= 0 || mpcmpfltflt(v.U.(*Mpflt), maxfltval[t.Etype]) >= 0 { - return true - } - - case CTCPLX: - if !Iscomplex[t.Etype] { - Fatal("overflow: %v complex constant", t) - } - 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 - } - } - - return false -} - -func overflow(v Val, t *Type) { - // v has already been converted - // to appropriate form for t. - if t == nil || t.Etype == TIDEAL { - return - } - - // Only uintptrs may be converted to unsafe.Pointer, which cannot overflow. - if t.Etype == TUNSAFEPTR { - return - } - - if !doesoverflow(v, t) { - return - } - - switch v.Ctype { - case CTINT, CTRUNE: - Yyerror("constant %v overflows %v", v.U.(*Mpint), t) - - case CTFLT: - Yyerror("constant %v overflows %v", Fconv(v.U.(*Mpflt), obj.FmtSharp), t) - - case CTCPLX: - 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.(*Mpint), Minintval[TINT]) < 0 || Mpcmpfixfix(v.U.(*Mpint), Maxintval[TINT]) > 0 { - Yyerror("overflow in int -> string") - } - r := uint(Mpgetfix(v.U.(*Mpint))) - v = Val{} - v.Ctype = CTSTR - v.U = string(r) - - case CTFLT: - Yyerror("no float -> string") - fallthrough - - case CTNIL: - v = Val{} - v.Ctype = CTSTR - v.U = "" - } - - return v -} - -func consttype(n *Node) int { - if n == nil || n.Op != OLITERAL { - return -1 - } - return int(n.Val.Ctype) -} - -func Isconst(n *Node, ct int) bool { - t := consttype(n) - - // If the caller is asking for CTINT, allow CTRUNE too. - // Makes life easier for back ends. - return t == ct || (ct == CTINT && t == CTRUNE) -} - -func saveorig(n *Node) *Node { - if n == n.Orig { - // duplicate node for n->orig. - n1 := Nod(OLITERAL, nil, nil) - - n.Orig = n1 - *n1 = *n - } - - return n.Orig -} - -/* - * if n is constant, rewrite as OLITERAL node. - */ -func evconst(n *Node) { - // pick off just the opcodes that can be - // constant evaluated. - switch n.Op { - default: - return - - case OADD, - OAND, - OANDAND, - OANDNOT, - OARRAYBYTESTR, - OCOM, - ODIV, - OEQ, - OGE, - OGT, - OLE, - OLSH, - OLT, - OMINUS, - OMOD, - OMUL, - ONE, - ONOT, - OOR, - OOROR, - OPLUS, - ORSH, - OSUB, - OXOR: - break - - case OCONV: - if n.Type == nil { - return - } - if !okforconst[n.Type.Etype] && n.Type.Etype != TNIL { - return - } - - // merge adjacent constants in the argument list. - case OADDSTR: - var nr *Node - var nl *Node - var l2 *NodeList - for l1 := n.List; l1 != nil; l1 = l1.Next { - if Isconst(l1.N, CTSTR) && l1.Next != nil && Isconst(l1.Next.N, CTSTR) { - // merge from l1 up to but not including l2 - var strs []string - l2 = l1 - for l2 != nil && Isconst(l2.N, CTSTR) { - nr = l2.N - strs = append(strs, nr.Val.U.(string)) - l2 = l2.Next - } - - nl = Nod(OXXX, nil, nil) - *nl = *l1.N - nl.Orig = nl - nl.Val.Ctype = CTSTR - nl.Val.U = strings.Join(strs, "") - l1.N = nl - l1.Next = l2 - } - } - - // fix list end pointer. - for l2 := n.List; l2 != nil; l2 = l2.Next { - n.List.End = l2 - } - - // collapse single-constant list to single constant. - if count(n.List) == 1 && Isconst(n.List.N, CTSTR) { - n.Op = OLITERAL - n.Val = n.List.N.Val - } - - return - } - - nl := n.Left - if nl == nil || nl.Type == nil { - return - } - if consttype(nl) < 0 { - return - } - wl := int(nl.Type.Etype) - if Isint[wl] || Isfloat[wl] || Iscomplex[wl] { - wl = TIDEAL - } - - nr := n.Right - var rv Val - var lno int - var wr int - var v Val - var norig *Node - if nr == nil { - // copy numeric value to avoid modifying - // nl, in case someone still refers to it (e.g. iota). - v = nl.Val - - if wl == TIDEAL { - v = copyval(v) - } - - switch uint32(n.Op)<<16 | uint32(v.Ctype) { - default: - if n.Diag == 0 { - Yyerror("illegal constant expression %v %v", Oconv(int(n.Op), 0), nl.Type) - n.Diag = 1 - } - - return - - case OCONV<<16 | CTNIL, - OARRAYBYTESTR<<16 | CTNIL: - if n.Type.Etype == TSTRING { - v = tostr(v) - nl.Type = n.Type - break - } - fallthrough - - // fall through - case OCONV<<16 | CTINT, - OCONV<<16 | CTRUNE, - OCONV<<16 | CTFLT, - OCONV<<16 | CTSTR: - convlit1(&nl, n.Type, true) - - v = nl.Val - - case OPLUS<<16 | CTINT, - OPLUS<<16 | CTRUNE: - break - - case OMINUS<<16 | CTINT, - OMINUS<<16 | CTRUNE: - mpnegfix(v.U.(*Mpint)) - - case OCOM<<16 | CTINT, - OCOM<<16 | CTRUNE: - et := Txxx - if nl.Type != nil { - et = int(nl.Type.Etype) - } - - // calculate the mask in b - // result will be (a ^ mask) - var b Mpint - switch et { - // signed guys change sign - default: - Mpmovecfix(&b, -1) - - // unsigned guys invert their bits - case TUINT8, - TUINT16, - TUINT32, - TUINT64, - TUINT, - TUINTPTR: - mpmovefixfix(&b, Maxintval[et]) - } - - mpxorfixfix(v.U.(*Mpint), &b) - - case OPLUS<<16 | CTFLT: - break - - case OMINUS<<16 | CTFLT: - mpnegflt(v.U.(*Mpflt)) - - case OPLUS<<16 | CTCPLX: - break - - case OMINUS<<16 | CTCPLX: - mpnegflt(&v.U.(*Mpcplx).Real) - mpnegflt(&v.U.(*Mpcplx).Imag) - - case ONOT<<16 | CTBOOL: - if !v.U.(bool) { - goto settrue - } - goto setfalse - } - goto ret - } - if nr.Type == nil { - return - } - if consttype(nr) < 0 { - return - } - wr = int(nr.Type.Etype) - if Isint[wr] || Isfloat[wr] || Iscomplex[wr] { - wr = TIDEAL - } - - // check for compatible general types (numeric, string, etc) - if wl != wr { - goto illegal - } - - // check for compatible types. - switch n.Op { - // ideal const mixes with anything but otherwise must match. - default: - if nl.Type.Etype != TIDEAL { - defaultlit(&nr, nl.Type) - n.Right = nr - } - - if nr.Type.Etype != TIDEAL { - defaultlit(&nl, nr.Type) - n.Left = nl - } - - if nl.Type.Etype != nr.Type.Etype { - goto illegal - } - - // right must be unsigned. - // left can be ideal. - case OLSH, ORSH: - defaultlit(&nr, Types[TUINT]) - - n.Right = nr - if nr.Type != nil && (Issigned[nr.Type.Etype] || !Isint[nr.Type.Etype]) { - goto illegal - } - if nl.Val.Ctype != CTRUNE { - nl.Val = toint(nl.Val) - } - nr.Val = toint(nr.Val) - } - - // copy numeric value to avoid modifying - // n->left, in case someone still refers to it (e.g. iota). - v = nl.Val - - if wl == TIDEAL { - v = copyval(v) - } - - rv = nr.Val - - // convert to common ideal - if v.Ctype == CTCPLX || rv.Ctype == CTCPLX { - v = tocplx(v) - rv = tocplx(rv) - } - - if v.Ctype == CTFLT || rv.Ctype == CTFLT { - v = toflt(v) - rv = toflt(rv) - } - - // Rune and int turns into rune. - if v.Ctype == CTRUNE && rv.Ctype == CTINT { - rv.Ctype = CTRUNE - } - if v.Ctype == CTINT && rv.Ctype == CTRUNE { - if n.Op == OLSH || n.Op == ORSH { - rv.Ctype = CTINT - } else { - v.Ctype = CTRUNE - } - } - - if v.Ctype != rv.Ctype { - // Use of undefined name as constant? - if (v.Ctype == 0 || rv.Ctype == 0) && nerrors > 0 { - return - } - Fatal("constant type mismatch %v(%d) %v(%d)", nl.Type, v.Ctype, nr.Type, rv.Ctype) - } - - // run op - switch uint32(n.Op)<<16 | uint32(v.Ctype) { - default: - goto illegal - - case OADD<<16 | CTINT, - OADD<<16 | CTRUNE: - mpaddfixfix(v.U.(*Mpint), rv.U.(*Mpint), 0) - - case OSUB<<16 | CTINT, - OSUB<<16 | CTRUNE: - mpsubfixfix(v.U.(*Mpint), rv.U.(*Mpint)) - - case OMUL<<16 | CTINT, - OMUL<<16 | CTRUNE: - mpmulfixfix(v.U.(*Mpint), rv.U.(*Mpint)) - - case ODIV<<16 | CTINT, - ODIV<<16 | CTRUNE: - if mpcmpfixc(rv.U.(*Mpint), 0) == 0 { - Yyerror("division by zero") - mpsetovf(v.U.(*Mpint)) - break - } - - mpdivfixfix(v.U.(*Mpint), rv.U.(*Mpint)) - - case OMOD<<16 | CTINT, - OMOD<<16 | CTRUNE: - if mpcmpfixc(rv.U.(*Mpint), 0) == 0 { - Yyerror("division by zero") - mpsetovf(v.U.(*Mpint)) - break - } - - mpmodfixfix(v.U.(*Mpint), rv.U.(*Mpint)) - - case OLSH<<16 | CTINT, - OLSH<<16 | CTRUNE: - mplshfixfix(v.U.(*Mpint), rv.U.(*Mpint)) - - case ORSH<<16 | CTINT, - ORSH<<16 | CTRUNE: - mprshfixfix(v.U.(*Mpint), rv.U.(*Mpint)) - - case OOR<<16 | CTINT, - OOR<<16 | CTRUNE: - mporfixfix(v.U.(*Mpint), rv.U.(*Mpint)) - - case OAND<<16 | CTINT, - OAND<<16 | CTRUNE: - mpandfixfix(v.U.(*Mpint), rv.U.(*Mpint)) - - case OANDNOT<<16 | CTINT, - OANDNOT<<16 | CTRUNE: - mpandnotfixfix(v.U.(*Mpint), rv.U.(*Mpint)) - - case OXOR<<16 | CTINT, - OXOR<<16 | CTRUNE: - mpxorfixfix(v.U.(*Mpint), rv.U.(*Mpint)) - - case OADD<<16 | CTFLT: - mpaddfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) - - case OSUB<<16 | CTFLT: - mpsubfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) - - case OMUL<<16 | CTFLT: - mpmulfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) - - case ODIV<<16 | CTFLT: - if mpcmpfltc(rv.U.(*Mpflt), 0) == 0 { - Yyerror("division by zero") - Mpmovecflt(v.U.(*Mpflt), 1.0) - break - } - - mpdivfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) - - // The default case above would print 'ideal % ideal', - // which is not quite an ideal error. - case OMOD<<16 | CTFLT: - if n.Diag == 0 { - Yyerror("illegal constant expression: floating-point %% operation") - n.Diag = 1 - } - - return - - case OADD<<16 | CTCPLX: - 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.(*Mpcplx).Real, &rv.U.(*Mpcplx).Real) - mpsubfltflt(&v.U.(*Mpcplx).Imag, &rv.U.(*Mpcplx).Imag) - - case OMUL<<16 | CTCPLX: - cmplxmpy(v.U.(*Mpcplx), rv.U.(*Mpcplx)) - - case ODIV<<16 | CTCPLX: - if mpcmpfltc(&rv.U.(*Mpcplx).Real, 0) == 0 && mpcmpfltc(&rv.U.(*Mpcplx).Imag, 0) == 0 { - Yyerror("complex division by zero") - Mpmovecflt(&rv.U.(*Mpcplx).Real, 1.0) - Mpmovecflt(&rv.U.(*Mpcplx).Imag, 0.0) - break - } - - cmplxdiv(v.U.(*Mpcplx), rv.U.(*Mpcplx)) - - case OEQ<<16 | CTNIL: - goto settrue - - case ONE<<16 | CTNIL: - goto setfalse - - case OEQ<<16 | CTINT, - OEQ<<16 | CTRUNE: - if Mpcmpfixfix(v.U.(*Mpint), rv.U.(*Mpint)) == 0 { - goto settrue - } - goto setfalse - - case ONE<<16 | CTINT, - ONE<<16 | CTRUNE: - if Mpcmpfixfix(v.U.(*Mpint), rv.U.(*Mpint)) != 0 { - goto settrue - } - goto setfalse - - case OLT<<16 | CTINT, - OLT<<16 | CTRUNE: - if Mpcmpfixfix(v.U.(*Mpint), rv.U.(*Mpint)) < 0 { - goto settrue - } - goto setfalse - - case OLE<<16 | CTINT, - OLE<<16 | CTRUNE: - if Mpcmpfixfix(v.U.(*Mpint), rv.U.(*Mpint)) <= 0 { - goto settrue - } - goto setfalse - - case OGE<<16 | CTINT, - OGE<<16 | CTRUNE: - if Mpcmpfixfix(v.U.(*Mpint), rv.U.(*Mpint)) >= 0 { - goto settrue - } - goto setfalse - - case OGT<<16 | CTINT, - OGT<<16 | CTRUNE: - if Mpcmpfixfix(v.U.(*Mpint), rv.U.(*Mpint)) > 0 { - goto settrue - } - goto setfalse - - case OEQ<<16 | CTFLT: - if mpcmpfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) == 0 { - goto settrue - } - goto setfalse - - case ONE<<16 | CTFLT: - if mpcmpfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) != 0 { - goto settrue - } - goto setfalse - - case OLT<<16 | CTFLT: - if mpcmpfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) < 0 { - goto settrue - } - goto setfalse - - case OLE<<16 | CTFLT: - if mpcmpfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) <= 0 { - goto settrue - } - goto setfalse - - case OGE<<16 | CTFLT: - if mpcmpfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) >= 0 { - goto settrue - } - goto setfalse - - case OGT<<16 | CTFLT: - if mpcmpfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) > 0 { - goto settrue - } - goto setfalse - - case OEQ<<16 | CTCPLX: - 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.(*Mpcplx).Real, &rv.U.(*Mpcplx).Real) != 0 || mpcmpfltflt(&v.U.(*Mpcplx).Imag, &rv.U.(*Mpcplx).Imag) != 0 { - goto settrue - } - goto setfalse - - case OEQ<<16 | CTSTR: - if cmpslit(nl, nr) == 0 { - goto settrue - } - goto setfalse - - case ONE<<16 | CTSTR: - if cmpslit(nl, nr) != 0 { - goto settrue - } - goto setfalse - - case OLT<<16 | CTSTR: - if cmpslit(nl, nr) < 0 { - goto settrue - } - goto setfalse - - case OLE<<16 | CTSTR: - if cmpslit(nl, nr) <= 0 { - goto settrue - } - goto setfalse - - case OGE<<16 | CTSTR: - if cmpslit(nl, nr) >= 0 { - goto settrue - } - goto setfalse - - case OGT<<16 | CTSTR: - if cmpslit(nl, nr) > 0 { - goto settrue - } - goto setfalse - - case OOROR<<16 | CTBOOL: - if v.U.(bool) || rv.U.(bool) { - goto settrue - } - goto setfalse - - case OANDAND<<16 | CTBOOL: - if v.U.(bool) && rv.U.(bool) { - goto settrue - } - goto setfalse - - case OEQ<<16 | CTBOOL: - if v.U.(bool) == rv.U.(bool) { - goto settrue - } - goto setfalse - - case ONE<<16 | CTBOOL: - if v.U.(bool) != rv.U.(bool) { - goto settrue - } - goto setfalse - } - - goto ret - -ret: - norig = saveorig(n) - *n = *nl - - // restore value of n->orig. - n.Orig = norig - - n.Val = v - - // check range. - lno = int(setlineno(n)) - - overflow(v, n.Type) - lineno = int32(lno) - - // truncate precision for non-ideal float. - if v.Ctype == CTFLT && n.Type.Etype != TIDEAL { - n.Val.U = truncfltlit(v.U.(*Mpflt), n.Type) - } - return - -settrue: - norig = saveorig(n) - *n = *Nodbool(true) - n.Orig = norig - return - -setfalse: - norig = saveorig(n) - *n = *Nodbool(false) - n.Orig = norig - return - -illegal: - if n.Diag == 0 { - Yyerror("illegal constant expression: %v %v %v", nl.Type, Oconv(int(n.Op), 0), nr.Type) - n.Diag = 1 - } - - return -} - -func nodlit(v Val) *Node { - n := Nod(OLITERAL, nil, nil) - n.Val = v - switch v.Ctype { - default: - Fatal("nodlit ctype %d", v.Ctype) - - case CTSTR: - n.Type = idealstring - - case CTBOOL: - n.Type = idealbool - - case CTINT, CTRUNE, CTFLT, CTCPLX: - n.Type = Types[TIDEAL] - - case CTNIL: - n.Type = Types[TNIL] - } - - return n -} - -func nodcplxlit(r Val, i Val) *Node { - r = toflt(r) - i = toflt(i) - - c := new(Mpcplx) - n := Nod(OLITERAL, nil, nil) - n.Type = Types[TIDEAL] - 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.(*Mpflt)) - mpmovefltflt(&c.Imag, i.U.(*Mpflt)) - return n -} - -// idealkind returns a constant kind like consttype -// but for an arbitrary "ideal" (untyped constant) expression. -func idealkind(n *Node) int { - if n == nil || !isideal(n.Type) { - return CTxxx - } - - switch n.Op { - default: - return CTxxx - - case OLITERAL: - return int(n.Val.Ctype) - - // numeric kinds. - case OADD, - OAND, - OANDNOT, - OCOM, - ODIV, - OMINUS, - OMOD, - OMUL, - OSUB, - OXOR, - OOR, - OPLUS: - k1 := idealkind(n.Left) - - k2 := idealkind(n.Right) - if k1 > k2 { - return k1 - } else { - return k2 - } - - case OREAL, OIMAG: - return CTFLT - - case OCOMPLEX: - return CTCPLX - - case OADDSTR: - return CTSTR - - case OANDAND, - OEQ, - OGE, - OGT, - OLE, - OLT, - ONE, - ONOT, - OOROR, - OCMPSTR, - OCMPIFACE: - return CTBOOL - - // shifts (beware!). - case OLSH, ORSH: - return idealkind(n.Left) - } -} - -func defaultlit(np **Node, t *Type) { - n := *np - if n == nil || !isideal(n.Type) { - return - } - - if n.Op == OLITERAL { - nn := Nod(OXXX, nil, nil) - *nn = *n - n = nn - *np = n - } - - lno := int(setlineno(n)) - ctype := idealkind(n) - var t1 *Type - switch ctype { - default: - if t != nil { - Convlit(np, t) - return - } - - if n.Val.Ctype == CTNIL { - lineno = int32(lno) - if n.Diag == 0 { - Yyerror("use of untyped nil") - n.Diag = 1 - } - - n.Type = nil - break - } - - if n.Val.Ctype == CTSTR { - t1 := Types[TSTRING] - Convlit(np, t1) - break - } - - Yyerror("defaultlit: unknown literal: %v", n) - - case CTxxx: - Fatal("defaultlit: idealkind is CTxxx: %v", Nconv(n, obj.FmtSign)) - - case CTBOOL: - t1 := Types[TBOOL] - if t != nil && t.Etype == TBOOL { - t1 = t - } - Convlit(np, t1) - - case CTINT: - t1 = Types[TINT] - goto num - - case CTRUNE: - t1 = runetype - goto num - - case CTFLT: - t1 = Types[TFLOAT64] - goto num - - case CTCPLX: - t1 = Types[TCOMPLEX128] - goto num - } - - lineno = int32(lno) - return - -num: - if t != nil { - if Isint[t.Etype] { - t1 = t - n.Val = toint(n.Val) - } else if Isfloat[t.Etype] { - t1 = t - n.Val = toflt(n.Val) - } else if Iscomplex[t.Etype] { - t1 = t - n.Val = tocplx(n.Val) - } - } - - overflow(n.Val, t1) - Convlit(np, t1) - lineno = int32(lno) - return -} - -/* - * defaultlit on both nodes simultaneously; - * if they're both ideal going in they better - * get the same type going out. - * force means must assign concrete (non-ideal) type. - */ -func defaultlit2(lp **Node, rp **Node, force int) { - l := *lp - r := *rp - if l.Type == nil || r.Type == nil { - return - } - if !isideal(l.Type) { - Convlit(rp, l.Type) - return - } - - if !isideal(r.Type) { - Convlit(lp, r.Type) - return - } - - if force == 0 { - return - } - if l.Type.Etype == TBOOL { - Convlit(lp, Types[TBOOL]) - Convlit(rp, Types[TBOOL]) - } - - lkind := idealkind(l) - rkind := idealkind(r) - if lkind == CTCPLX || rkind == CTCPLX { - Convlit(lp, Types[TCOMPLEX128]) - Convlit(rp, Types[TCOMPLEX128]) - return - } - - if lkind == CTFLT || rkind == CTFLT { - Convlit(lp, Types[TFLOAT64]) - Convlit(rp, Types[TFLOAT64]) - return - } - - if lkind == CTRUNE || rkind == CTRUNE { - Convlit(lp, runetype) - Convlit(rp, runetype) - return - } - - Convlit(lp, Types[TINT]) - Convlit(rp, Types[TINT]) -} - -func cmpslit(l, r *Node) int { - return stringsCompare(l.Val.U.(string), r.Val.U.(string)) -} - -func Smallintconst(n *Node) bool { - if n.Op == OLITERAL && Isconst(n, CTINT) && n.Type != nil { - switch Simtype[n.Type.Etype] { - case TINT8, - TUINT8, - TINT16, - TUINT16, - TINT32, - TUINT32, - TBOOL, - TPTR32: - return true - - case TIDEAL, TINT64, TUINT64, TPTR64: - if Mpcmpfixfix(n.Val.U.(*Mpint), Minintval[TINT32]) < 0 || Mpcmpfixfix(n.Val.U.(*Mpint), Maxintval[TINT32]) > 0 { - break - } - return true - } - } - - return false -} - -func nonnegconst(n *Node) int { - if n.Op == OLITERAL && n.Type != nil { - switch Simtype[n.Type.Etype] { - // check negative and 2^31 - case TINT8, - TUINT8, - TINT16, - TUINT16, - TINT32, - TUINT32, - TINT64, - TUINT64, - TIDEAL: - if Mpcmpfixfix(n.Val.U.(*Mpint), Minintval[TUINT32]) < 0 || Mpcmpfixfix(n.Val.U.(*Mpint), Maxintval[TINT32]) > 0 { - break - } - return int(Mpgetfix(n.Val.U.(*Mpint))) - } - } - - return -1 -} - -/* - * convert x to type et and back to int64 - * for sign extension and truncation. - */ -func iconv(x int64, et int) int64 { - switch et { - case TINT8: - x = int64(int8(x)) - - case TUINT8: - x = int64(uint8(x)) - - case TINT16: - x = int64(int16(x)) - - case TUINT16: - x = int64(uint64(x)) - - case TINT32: - x = int64(int32(x)) - - case TUINT32: - x = int64(uint32(x)) - - case TINT64, TUINT64: - break - } - - return x -} - -// 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 = n.Val - - if Isint[tt] { - con.Val.Ctype = CTINT - 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.(*Mpint)) - - case CTBOOL: - i = int64(obj.Bool2int(n.Val.U.(bool))) - - case CTNIL: - i = 0 - } - - i = iconv(i, tt) - Mpmovecfix(con.Val.U.(*Mpint), i) - return - } - - if Isfloat[tt] { - con.Val = toflt(con.Val) - if con.Val.Ctype != CTFLT { - Fatal("convconst ctype=%d %v", con.Val.Ctype, t) - } - if tt == TFLOAT32 { - con.Val.U = truncfltlit(con.Val.U.(*Mpflt), t) - } - return - } - - if Iscomplex[tt] { - con.Val = tocplx(con.Val) - if tt == TCOMPLEX64 { - 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 - } - - Fatal("convconst %v constant", Tconv(t, obj.FmtLong)) -} - -// complex multiply v *= rv -// (a, b) * (c, d) = (a*c - b*d, b*c + a*d) -func cmplxmpy(v *Mpcplx, rv *Mpcplx) { - var ac Mpflt - var bd Mpflt - var bc Mpflt - var ad Mpflt - - mpmovefltflt(&ac, &v.Real) - mpmulfltflt(&ac, &rv.Real) // ac - - mpmovefltflt(&bd, &v.Imag) - - mpmulfltflt(&bd, &rv.Imag) // bd - - mpmovefltflt(&bc, &v.Imag) - - mpmulfltflt(&bc, &rv.Real) // bc - - mpmovefltflt(&ad, &v.Real) - - mpmulfltflt(&ad, &rv.Imag) // ad - - mpmovefltflt(&v.Real, &ac) - - mpsubfltflt(&v.Real, &bd) // ac-bd - - mpmovefltflt(&v.Imag, &bc) - - mpaddfltflt(&v.Imag, &ad) // bc+ad -} - -// complex divide v /= rv -// (a, b) / (c, d) = ((a*c + b*d), (b*c - a*d))/(c*c + d*d) -func cmplxdiv(v *Mpcplx, rv *Mpcplx) { - var ac Mpflt - var bd Mpflt - var bc Mpflt - var ad Mpflt - var cc_plus_dd Mpflt - - mpmovefltflt(&cc_plus_dd, &rv.Real) - mpmulfltflt(&cc_plus_dd, &rv.Real) // cc - - mpmovefltflt(&ac, &rv.Imag) - - mpmulfltflt(&ac, &rv.Imag) // dd - - mpaddfltflt(&cc_plus_dd, &ac) // cc+dd - - mpmovefltflt(&ac, &v.Real) - - mpmulfltflt(&ac, &rv.Real) // ac - - mpmovefltflt(&bd, &v.Imag) - - mpmulfltflt(&bd, &rv.Imag) // bd - - mpmovefltflt(&bc, &v.Imag) - - mpmulfltflt(&bc, &rv.Real) // bc - - mpmovefltflt(&ad, &v.Real) - - mpmulfltflt(&ad, &rv.Imag) // ad - - mpmovefltflt(&v.Real, &ac) - - mpaddfltflt(&v.Real, &bd) // ac+bd - mpdivfltflt(&v.Real, &cc_plus_dd) // (ac+bd)/(cc+dd) - - mpmovefltflt(&v.Imag, &bc) - - mpsubfltflt(&v.Imag, &ad) // bc-ad - mpdivfltflt(&v.Imag, &cc_plus_dd) // (bc+ad)/(cc+dd) -} - -// Is n a Go language constant (as opposed to a compile-time constant)? -// Expressions derived from nil, like string([]byte(nil)), while they -// may be known at compile time, are not Go language constants. -// Only called for expressions known to evaluated to compile-time -// constants. -func isgoconst(n *Node) bool { - if n.Orig != nil { - n = n.Orig - } - - switch n.Op { - case OADD, - OADDSTR, - OAND, - OANDAND, - OANDNOT, - OCOM, - ODIV, - OEQ, - OGE, - OGT, - OLE, - OLSH, - OLT, - OMINUS, - OMOD, - OMUL, - ONE, - ONOT, - OOR, - OOROR, - OPLUS, - ORSH, - OSUB, - OXOR, - OIOTA, - OCOMPLEX, - OREAL, - OIMAG: - if isgoconst(n.Left) && (n.Right == nil || isgoconst(n.Right)) { - return true - } - - case OCONV: - if okforconst[n.Type.Etype] && isgoconst(n.Left) { - return true - } - - case OLEN, OCAP: - l := n.Left - if isgoconst(l) { - return true - } - - // Special case: len/cap is constant when applied to array or - // pointer to array when the expression does not contain - // function calls or channel receive operations. - t := l.Type - - if t != nil && Isptr[t.Etype] { - t = t.Type - } - if Isfixedarray(t) && !hascallchan(l) { - return true - } - - case OLITERAL: - if n.Val.Ctype != CTNIL { - return true - } - - case ONAME: - l := n.Sym.Def - if l != nil && l.Op == OLITERAL && n.Val.Ctype != CTNIL { - return true - } - - case ONONAME: - if n.Sym.Def != nil && n.Sym.Def.Op == OIOTA { - return true - } - - // Only constant calls are unsafe.Alignof, Offsetof, and Sizeof. - case OCALL: - l := n.Left - - for l.Op == OPAREN { - l = l.Left - } - if l.Op != ONAME || l.Sym.Pkg != unsafepkg { - break - } - if l.Sym.Name == "Alignof" || l.Sym.Name == "Offsetof" || l.Sym.Name == "Sizeof" { - return true - } - } - - //dump("nonconst", n); - return false -} - -func hascallchan(n *Node) bool { - if n == nil { - return false - } - switch n.Op { - case OAPPEND, - OCALL, - OCALLFUNC, - OCALLINTER, - OCALLMETH, - OCAP, - OCLOSE, - OCOMPLEX, - OCOPY, - ODELETE, - OIMAG, - OLEN, - OMAKE, - ONEW, - OPANIC, - OPRINT, - OPRINTN, - OREAL, - ORECOVER, - ORECV: - return true - } - - if hascallchan(n.Left) || hascallchan(n.Right) { - return true - } - - for l := n.List; l != nil; l = l.Next { - if hascallchan(l.N) { - return true - } - } - for l := n.Rlist; l != nil; l = l.Next { - if hascallchan(l.N) { - return true - } - } - - return false -} diff --git a/src/cmd/internal/gc/cplx.go b/src/cmd/internal/gc/cplx.go deleted file mode 100644 index 56a4892636..0000000000 --- a/src/cmd/internal/gc/cplx.go +++ /dev/null @@ -1,480 +0,0 @@ -// Copyright 2009 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 gc - -import "cmd/internal/obj" - -func overlap_cplx(f *Node, t *Node) bool { - // check whether f and t could be overlapping stack references. - // not exact, because it's hard to check for the stack register - // in portable code. close enough: worst case we will allocate - // an extra temporary and the registerizer will clean it up. - return f.Op == OINDREG && t.Op == OINDREG && f.Xoffset+f.Type.Width >= t.Xoffset && t.Xoffset+t.Type.Width >= f.Xoffset -} - -func complexbool(op int, nl, nr, res *Node, wantTrue bool, likely int, to *obj.Prog) { - // make both sides addable in ullman order - if nr != nil { - if nl.Ullman > nr.Ullman && !nl.Addable { - nl = CgenTemp(nl) - } - - if !nr.Addable { - nr = CgenTemp(nr) - } - } - if !nl.Addable { - nl = CgenTemp(nl) - } - - // Break nl and nr into real and imaginary components. - var lreal, limag, rreal, rimag Node - subnode(&lreal, &limag, nl) - subnode(&rreal, &rimag, nr) - - // build tree - // if branching: - // real(l) == real(r) && imag(l) == imag(r) - // if generating a value, use a branch-free version: - // real(l) == real(r) & imag(l) == imag(r) - realeq := Node{ - Op: OEQ, - Left: &lreal, - Right: &rreal, - Type: Types[TBOOL], - } - imageq := Node{ - Op: OEQ, - Left: &limag, - Right: &rimag, - Type: Types[TBOOL], - } - and := Node{ - Op: OANDAND, - Left: &realeq, - Right: &imageq, - Type: Types[TBOOL], - } - - if res != nil { - // generating a value - and.Op = OAND - if op == ONE { - and.Op = OOR - realeq.Op = ONE - imageq.Op = ONE - } - Bvgen(&and, res, true) - return - } - - // generating a branch - if op == ONE { - wantTrue = !wantTrue - } - - Bgen(&and, wantTrue, likely, to) -} - -// break addable nc-complex into nr-real and ni-imaginary -func subnode(nr *Node, ni *Node, nc *Node) { - if !nc.Addable { - Fatal("subnode not addable") - } - - tc := Simsimtype(nc.Type) - tc = cplxsubtype(tc) - t := Types[tc] - - if nc.Op == OLITERAL { - nodfconst(nr, t, &nc.Val.U.(*Mpcplx).Real) - nodfconst(ni, t, &nc.Val.U.(*Mpcplx).Imag) - return - } - - *nr = *nc - nr.Type = t - - *ni = *nc - ni.Type = t - ni.Xoffset += t.Width -} - -// generate code res = -nl -func minus(nl *Node, res *Node) { - var ra Node - ra.Op = OMINUS - ra.Left = nl - ra.Type = nl.Type - Cgen(&ra, res) -} - -// build and execute tree -// real(res) = -real(nl) -// imag(res) = -imag(nl) -func complexminus(nl *Node, res *Node) { - var n1 Node - var n2 Node - var n5 Node - var n6 Node - - subnode(&n1, &n2, nl) - subnode(&n5, &n6, res) - - minus(&n1, &n5) - minus(&n2, &n6) -} - -// build and execute tree -// real(res) = real(nl) op real(nr) -// imag(res) = imag(nl) op imag(nr) -func complexadd(op int, nl *Node, nr *Node, res *Node) { - var n1 Node - var n2 Node - var n3 Node - var n4 Node - var n5 Node - var n6 Node - - subnode(&n1, &n2, nl) - subnode(&n3, &n4, nr) - subnode(&n5, &n6, res) - - var ra Node - ra.Op = uint8(op) - ra.Left = &n1 - ra.Right = &n3 - ra.Type = n1.Type - Cgen(&ra, &n5) - - ra = Node{} - ra.Op = uint8(op) - ra.Left = &n2 - ra.Right = &n4 - ra.Type = n2.Type - Cgen(&ra, &n6) -} - -// build and execute tree -// tmp = real(nl)*real(nr) - imag(nl)*imag(nr) -// imag(res) = real(nl)*imag(nr) + imag(nl)*real(nr) -// real(res) = tmp -func complexmul(nl *Node, nr *Node, res *Node) { - var n1 Node - var n2 Node - var n3 Node - var n4 Node - var n5 Node - var n6 Node - var tmp Node - - subnode(&n1, &n2, nl) - subnode(&n3, &n4, nr) - subnode(&n5, &n6, res) - Tempname(&tmp, n5.Type) - - // real part -> tmp - var rm1 Node - - rm1.Op = OMUL - rm1.Left = &n1 - rm1.Right = &n3 - rm1.Type = n1.Type - - var rm2 Node - rm2.Op = OMUL - rm2.Left = &n2 - rm2.Right = &n4 - rm2.Type = n2.Type - - var ra Node - ra.Op = OSUB - ra.Left = &rm1 - ra.Right = &rm2 - ra.Type = rm1.Type - Cgen(&ra, &tmp) - - // imag part - rm1 = Node{} - - rm1.Op = OMUL - rm1.Left = &n1 - rm1.Right = &n4 - rm1.Type = n1.Type - - rm2 = Node{} - rm2.Op = OMUL - rm2.Left = &n2 - rm2.Right = &n3 - rm2.Type = n2.Type - - ra = Node{} - ra.Op = OADD - ra.Left = &rm1 - ra.Right = &rm2 - ra.Type = rm1.Type - Cgen(&ra, &n6) - - // tmp ->real part - Cgen(&tmp, &n5) -} - -func nodfconst(n *Node, t *Type, fval *Mpflt) { - *n = Node{} - n.Op = OLITERAL - n.Addable = true - ullmancalc(n) - n.Val.U = fval - n.Val.Ctype = CTFLT - n.Type = t - - if !Isfloat[t.Etype] { - Fatal("nodfconst: bad type %v", t) - } -} - -func Complexop(n *Node, res *Node) bool { - if n != nil && n.Type != nil { - if Iscomplex[n.Type.Etype] { - goto maybe - } - } - - if res != nil && res.Type != nil { - if Iscomplex[res.Type.Etype] { - goto maybe - } - } - - if n.Op == OREAL || n.Op == OIMAG { - //dump("\ncomplex-yes", n); - return true - } - - //dump("\ncomplex-no", n); - return false - -maybe: - switch n.Op { - case OCONV, // implemented ops - OADD, - OSUB, - OMUL, - OMINUS, - OCOMPLEX, - OREAL, - OIMAG: - //dump("\ncomplex-yes", n); - return true - - case ODOT, - ODOTPTR, - OINDEX, - OIND, - ONAME: - //dump("\ncomplex-yes", n); - return true - } - - //dump("\ncomplex-no", n); - return false -} - -func Complexmove(f *Node, t *Node) { - if Debug['g'] != 0 { - Dump("\ncomplexmove-f", f) - Dump("complexmove-t", t) - } - - if !t.Addable { - Fatal("complexmove: to not addable") - } - - ft := Simsimtype(f.Type) - tt := Simsimtype(t.Type) - switch uint32(ft)<<16 | uint32(tt) { - default: - Fatal("complexmove: unknown conversion: %v -> %v\n", f.Type, t.Type) - - // complex to complex move/convert. - // make f addable. - // also use temporary if possible stack overlap. - case TCOMPLEX64<<16 | TCOMPLEX64, - TCOMPLEX64<<16 | TCOMPLEX128, - TCOMPLEX128<<16 | TCOMPLEX64, - TCOMPLEX128<<16 | TCOMPLEX128: - if !f.Addable || overlap_cplx(f, t) { - var tmp Node - Tempname(&tmp, f.Type) - Complexmove(f, &tmp) - f = &tmp - } - - var n1 Node - var n2 Node - subnode(&n1, &n2, f) - var n4 Node - var n3 Node - subnode(&n3, &n4, t) - - Cgen(&n1, &n3) - Cgen(&n2, &n4) - } -} - -func Complexgen(n *Node, res *Node) { - if Debug['g'] != 0 { - Dump("\ncomplexgen-n", n) - Dump("complexgen-res", res) - } - - for n.Op == OCONVNOP { - n = n.Left - } - - // pick off float/complex opcodes - switch n.Op { - case OCOMPLEX: - if res.Addable { - var n1 Node - var n2 Node - subnode(&n1, &n2, res) - var tmp Node - Tempname(&tmp, n1.Type) - Cgen(n.Left, &tmp) - Cgen(n.Right, &n2) - Cgen(&tmp, &n1) - return - } - - case OREAL, OIMAG: - nl := n.Left - if !nl.Addable { - var tmp Node - Tempname(&tmp, nl.Type) - Complexgen(nl, &tmp) - nl = &tmp - } - - var n1 Node - var n2 Node - subnode(&n1, &n2, nl) - if n.Op == OREAL { - Cgen(&n1, res) - return - } - - Cgen(&n2, res) - return - } - - // perform conversion from n to res - tl := Simsimtype(res.Type) - - tl = cplxsubtype(tl) - tr := Simsimtype(n.Type) - tr = cplxsubtype(tr) - if tl != tr { - if !n.Addable { - var n1 Node - Tempname(&n1, n.Type) - Complexmove(n, &n1) - n = &n1 - } - - Complexmove(n, res) - return - } - - if !res.Addable { - var n1 Node - Igen(res, &n1, nil) - Cgen(n, &n1) - Regfree(&n1) - return - } - - if n.Addable { - Complexmove(n, res) - return - } - - switch n.Op { - default: - Dump("complexgen: unknown op", n) - Fatal("complexgen: unknown op %v", Oconv(int(n.Op), 0)) - - case ODOT, - ODOTPTR, - OINDEX, - OIND, - ONAME, // PHEAP or PPARAMREF var - OCALLFUNC, - OCALLMETH, - OCALLINTER: - var n1 Node - Igen(n, &n1, res) - - Complexmove(&n1, res) - Regfree(&n1) - return - - case OCONV, - OADD, - OSUB, - OMUL, - OMINUS, - OCOMPLEX, - OREAL, - OIMAG: - break - } - - nl := n.Left - if nl == nil { - return - } - nr := n.Right - - // make both sides addable in ullman order - var tnl Node - if nr != nil { - if nl.Ullman > nr.Ullman && !nl.Addable { - Tempname(&tnl, nl.Type) - Cgen(nl, &tnl) - nl = &tnl - } - - if !nr.Addable { - var tnr Node - Tempname(&tnr, nr.Type) - Cgen(nr, &tnr) - nr = &tnr - } - } - - if !nl.Addable { - Tempname(&tnl, nl.Type) - Cgen(nl, &tnl) - nl = &tnl - } - - switch n.Op { - default: - Fatal("complexgen: unknown op %v", Oconv(int(n.Op), 0)) - - case OCONV: - Complexmove(nl, res) - - case OMINUS: - complexminus(nl, res) - - case OADD, OSUB: - complexadd(int(n.Op), nl, nr, res) - - case OMUL: - complexmul(nl, nr, res) - } -} diff --git a/src/cmd/internal/gc/dcl.go b/src/cmd/internal/gc/dcl.go deleted file mode 100644 index 85a33bec3f..0000000000 --- a/src/cmd/internal/gc/dcl.go +++ /dev/null @@ -1,1494 +0,0 @@ -// Copyright 2009 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 gc - -import ( - "cmd/internal/obj" - "fmt" - "strings" -) - -func dflag() bool { - if Debug['d'] == 0 { - return false - } - if Debug['y'] != 0 { - return true - } - if incannedimport != 0 { - return false - } - return true -} - -/* - * declaration stack & operations - */ -func dcopy(a *Sym, b *Sym) { - a.Pkg = b.Pkg - a.Name = b.Name - a.Def = b.Def - a.Block = b.Block - a.Lastlineno = b.Lastlineno -} - -func push() *Sym { - d := new(Sym) - d.Lastlineno = lineno - d.Link = dclstack - dclstack = d - return d -} - -func pushdcl(s *Sym) *Sym { - d := push() - dcopy(d, s) - if dflag() { - fmt.Printf("\t%v push %v %p\n", Ctxt.Line(int(lineno)), s, s.Def) - } - return d -} - -func popdcl() { - var d *Sym - var s *Sym - var lno int - - // if(dflag()) - // print("revert\n"); - - for d = dclstack; d != nil; d = d.Link { - if d.Name == "" { - break - } - s = Pkglookup(d.Name, d.Pkg) - lno = int(s.Lastlineno) - dcopy(s, d) - d.Lastlineno = int32(lno) - if dflag() { - fmt.Printf("\t%v pop %v %p\n", Ctxt.Line(int(lineno)), s, s.Def) - } - } - - if d == nil { - Fatal("popdcl: no mark") - } - dclstack = d.Link - block = d.Block -} - -func poptodcl() { - // pop the old marker and push a new one - // (cannot reuse the existing one) - // because we use the markers to identify blocks - // for the goto restriction checks. - popdcl() - - markdcl() -} - -func markdcl() { - d := push() - d.Name = "" // used as a mark in fifo - d.Block = block - - blockgen++ - block = blockgen -} - -// if(dflag()) -// print("markdcl\n"); -func dumpdcl(st string) { - var s *Sym - - i := 0 - for d := dclstack; d != nil; d = d.Link { - i++ - fmt.Printf(" %.2d %p", i, d) - if d.Name == "" { - fmt.Printf("\n") - continue - } - - fmt.Printf(" '%s'", d.Name) - s = Pkglookup(d.Name, d.Pkg) - fmt.Printf(" %v\n", s) - } -} - -func testdclstack() { - for d := dclstack; d != nil; d = d.Link { - if d.Name == "" { - if nerrors != 0 { - errorexit() - } - Yyerror("mark left on the stack") - continue - } - } -} - -func redeclare(s *Sym, where string) { - if s.Lastlineno == 0 { - var tmp string - if s.Origpkg != nil { - tmp = s.Origpkg.Path - } else { - tmp = s.Pkg.Path - } - pkgstr := tmp - Yyerror("%v redeclared %s\n"+"\tprevious declaration during import %q", s, where, pkgstr) - } else { - line1 := parserline() - line2 := int(s.Lastlineno) - - // When an import and a declaration collide in separate files, - // present the import as the "redeclared", because the declaration - // is visible where the import is, but not vice versa. - // See issue 4510. - if s.Def == nil { - line2 = line1 - line1 = int(s.Lastlineno) - } - - yyerrorl(int(line1), "%v redeclared %s\n"+"\tprevious declaration at %v", s, where, Ctxt.Line(line2)) - } -} - -var vargen int - -/* - * declare individual names - var, typ, const - */ - -var declare_typegen int - -func declare(n *Node, ctxt uint8) { - if ctxt == PDISCARD { - return - } - - if isblank(n) { - return - } - - n.Lineno = int32(parserline()) - s := n.Sym - - // kludgy: typecheckok means we're past parsing. Eg genwrapper may declare out of package names later. - if importpkg == nil && typecheckok == 0 && s.Pkg != localpkg { - Yyerror("cannot declare name %v", s) - } - - if ctxt == PEXTERN && s.Name == "init" { - Yyerror("cannot declare init - must be func") - } - - gen := 0 - if ctxt == PEXTERN { - externdcl = list(externdcl, n) - if dflag() { - fmt.Printf("\t%v global decl %v %p\n", Ctxt.Line(int(lineno)), s, n) - } - } else { - if Curfn == nil && ctxt == PAUTO { - Fatal("automatic outside function") - } - if Curfn != nil { - Curfn.Func.Dcl = list(Curfn.Func.Dcl, n) - } - if n.Op == OTYPE { - declare_typegen++ - gen = declare_typegen - } else if n.Op == ONAME && ctxt == PAUTO && !strings.Contains(s.Name, "·") { - vargen++ - gen = vargen - } - pushdcl(s) - n.Curfn = Curfn - } - - if ctxt == PAUTO { - n.Xoffset = 0 - } - - if s.Block == block { - // functype will print errors about duplicate function arguments. - // Don't repeat the error here. - if ctxt != PPARAM && ctxt != PPARAMOUT { - redeclare(s, "in this block") - } - } - - s.Block = block - s.Lastlineno = int32(parserline()) - s.Def = n - n.Vargen = int32(gen) - n.Funcdepth = Funcdepth - n.Class = uint8(ctxt) - - autoexport(n, ctxt) -} - -func addvar(n *Node, t *Type, ctxt uint8) { - if n == nil || n.Sym == nil || (n.Op != ONAME && n.Op != ONONAME) || t == nil { - Fatal("addvar: n=%v t=%v nil", n, t) - } - - n.Op = ONAME - declare(n, ctxt) - n.Type = t -} - -/* - * declare variables from grammar - * new_name_list (type | [type] = expr_list) - */ -func variter(vl *NodeList, t *Node, el *NodeList) *NodeList { - var init *NodeList - doexpr := el != nil - - if count(el) == 1 && count(vl) > 1 { - e := el.N - as2 := Nod(OAS2, nil, nil) - as2.List = vl - as2.Rlist = list1(e) - var v *Node - for ; vl != nil; vl = vl.Next { - v = vl.N - v.Op = ONAME - declare(v, dclcontext) - v.Ntype = t - v.Defn = as2 - if Funcdepth > 0 { - init = list(init, Nod(ODCL, v, nil)) - } - } - - return list(init, as2) - } - - var v *Node - var e *Node - for ; vl != nil; vl = vl.Next { - if doexpr { - if el == nil { - Yyerror("missing expression in var declaration") - break - } - - e = el.N - el = el.Next - } else { - e = nil - } - - v = vl.N - v.Op = ONAME - declare(v, dclcontext) - v.Ntype = t - - if e != nil || Funcdepth > 0 || isblank(v) { - if Funcdepth > 0 { - init = list(init, Nod(ODCL, v, nil)) - } - e = Nod(OAS, v, e) - init = list(init, e) - if e.Right != nil { - v.Defn = e - } - } - } - - if el != nil { - Yyerror("extra expression in var declaration") - } - return init -} - -/* - * declare constants from grammar - * new_name_list [[type] = expr_list] - */ -func constiter(vl *NodeList, t *Node, cl *NodeList) *NodeList { - if cl == nil { - if t != nil { - Yyerror("const declaration cannot have type without expression") - } - cl = lastconst - t = lasttype - } else { - lastconst = cl - lasttype = t - } - - cl = listtreecopy(cl) - - var v *Node - var c *Node - var vv *NodeList - for ; vl != nil; vl = vl.Next { - if cl == nil { - Yyerror("missing value in const declaration") - break - } - - c = cl.N - cl = cl.Next - - v = vl.N - v.Op = OLITERAL - declare(v, dclcontext) - - v.Ntype = t - v.Defn = c - - vv = list(vv, Nod(ODCLCONST, v, nil)) - } - - if cl != nil { - Yyerror("extra expression in const declaration") - } - iota_ += 1 - return vv -} - -/* - * this generates a new name node, - * typically for labels or other one-off names. - */ -func newname(s *Sym) *Node { - if s == nil { - Fatal("newname nil") - } - - n := Nod(ONAME, nil, nil) - n.Sym = s - n.Type = nil - n.Addable = true - n.Ullman = 1 - n.Xoffset = 0 - return n -} - -// newfuncname generates a new name node for a function or method. -// TODO(rsc): Use an ODCLFUNC node instead. See comment in CL 7360. -func newfuncname(s *Sym) *Node { - n := newname(s) - n.Func = new(Func) - return n -} - -/* - * this generates a new name node for a name - * being declared. - */ -func dclname(s *Sym) *Node { - n := newname(s) - n.Op = ONONAME // caller will correct it - return n -} - -func typenod(t *Type) *Node { - // if we copied another type with *t = *u - // then t->nod might be out of date, so - // check t->nod->type too - if t.Nod == nil || t.Nod.Type != t { - t.Nod = Nod(OTYPE, nil, nil) - t.Nod.Type = t - t.Nod.Sym = t.Sym - } - - return t.Nod -} - -/* - * this will return an old name - * that has already been pushed on the - * declaration list. a diagnostic is - * generated if no name has been defined. - */ -func oldname(s *Sym) *Node { - n := s.Def - if n == nil { - // maybe a top-level name will come along - // to give this a definition later. - // walkdef will check s->def again once - // all the input source has been processed. - n = newname(s) - - n.Op = ONONAME - n.Iota = iota_ // save current iota value in const declarations - } - - if Curfn != nil && n.Funcdepth > 0 && n.Funcdepth != Funcdepth && n.Op == ONAME { - // inner func is referring to var in outer func. - // - // TODO(rsc): If there is an outer variable x and we - // 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 { - // create new closure var. - c := Nod(ONAME, nil, nil) - - c.Sym = s - c.Class = PPARAMREF - c.Isddd = n.Isddd - c.Defn = n - c.Addable = false - c.Ullman = 2 - c.Funcdepth = Funcdepth - c.Outer = n.Closure - n.Closure = c - c.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 -} - -/* - * := declarations - */ -func colasname(n *Node) bool { - switch n.Op { - case ONAME, - ONONAME, - OPACK, - OTYPE, - OLITERAL: - return n.Sym != nil - } - - return false -} - -func colasdefn(left *NodeList, defn *Node) { - for l := left; l != nil; l = l.Next { - if l.N.Sym != nil { - l.N.Sym.Flags |= SymUniq - } - } - - nnew := 0 - nerr := 0 - var n *Node - for l := left; l != nil; l = l.Next { - n = l.N - if isblank(n) { - continue - } - if !colasname(n) { - yyerrorl(int(defn.Lineno), "non-name %v on left side of :=", n) - nerr++ - continue - } - - if n.Sym.Flags&SymUniq == 0 { - yyerrorl(int(defn.Lineno), "%v repeated on left side of :=", n.Sym) - n.Diag++ - nerr++ - continue - } - - n.Sym.Flags &^= SymUniq - if n.Sym.Block == block { - continue - } - - nnew++ - n = newname(n.Sym) - declare(n, dclcontext) - n.Defn = defn - defn.Ninit = list(defn.Ninit, Nod(ODCL, n, nil)) - l.N = n - } - - if nnew == 0 && nerr == 0 { - yyerrorl(int(defn.Lineno), "no new variables on left side of :=") - } -} - -func colas(left *NodeList, right *NodeList, lno int32) *Node { - as := Nod(OAS2, nil, nil) - as.List = left - as.Rlist = right - as.Colas = true - as.Lineno = lno - colasdefn(left, as) - - // make the tree prettier; not necessary - if count(left) == 1 && count(right) == 1 { - as.Left = as.List.N - as.Right = as.Rlist.N - as.List = nil - as.Rlist = nil - as.Op = OAS - } - - return as -} - -/* - * declare the arguments in an - * interface field declaration. - */ -func ifacedcl(n *Node) { - if n.Op != ODCLFIELD || n.Right == nil { - Fatal("ifacedcl") - } - - if isblank(n.Left) { - Yyerror("methods must have a unique non-blank name") - } - - n.Func = new(Func) - dclcontext = PPARAM - markdcl() - Funcdepth++ - n.Outer = Curfn - Curfn = n - funcargs(n.Right) - - // funcbody is normally called after the parser has - // seen the body of a function but since an interface - // field declaration does not have a body, we must - // call it now to pop the current declaration context. - dclcontext = PAUTO - - funcbody(n) -} - -/* - * declare the function proper - * and declare the arguments. - * called in extern-declaration context - * returns in auto-declaration context. - */ -func funchdr(n *Node) { - // change the declaration context from extern to auto - if Funcdepth == 0 && dclcontext != PEXTERN { - Fatal("funchdr: dclcontext") - } - - dclcontext = PAUTO - markdcl() - Funcdepth++ - - n.Outer = Curfn - Curfn = n - - if n.Nname != nil { - funcargs(n.Nname.Ntype) - } else if n.Ntype != nil { - funcargs(n.Ntype) - } else { - funcargs2(n.Type) - } -} - -func funcargs(nt *Node) { - if nt.Op != OTFUNC { - Fatal("funcargs %v", Oconv(int(nt.Op), 0)) - } - - // re-start the variable generation number - // we want to use small numbers for the return variables, - // so let them have the chunk starting at 1. - vargen = count(nt.Rlist) - - // declare the receiver and in arguments. - // no n->defn because type checking of func header - // will not fill in the types until later - if nt.Left != nil { - n := nt.Left - if n.Op != ODCLFIELD { - Fatal("funcargs receiver %v", Oconv(int(n.Op), 0)) - } - if n.Left != nil { - n.Left.Op = ONAME - n.Left.Ntype = n.Right - declare(n.Left, PPARAM) - if dclcontext == PAUTO { - vargen++ - n.Left.Vargen = int32(vargen) - } - } - } - - var n *Node - for l := nt.List; l != nil; l = l.Next { - n = l.N - if n.Op != ODCLFIELD { - Fatal("funcargs in %v", Oconv(int(n.Op), 0)) - } - if n.Left != nil { - n.Left.Op = ONAME - n.Left.Ntype = n.Right - declare(n.Left, PPARAM) - if dclcontext == PAUTO { - vargen++ - n.Left.Vargen = int32(vargen) - } - } - } - - // declare the out arguments. - gen := count(nt.List) - var i int = 0 - var nn *Node - for l := nt.Rlist; l != nil; l = l.Next { - n = l.N - - if n.Op != ODCLFIELD { - Fatal("funcargs out %v", Oconv(int(n.Op), 0)) - } - - if n.Left == nil { - // Name so that escape analysis can track it. ~r stands for 'result'. - n.Left = newname(Lookupf("~r%d", gen)) - gen++ - } - - // TODO: n->left->missing = 1; - n.Left.Op = ONAME - - if isblank(n.Left) { - // Give it a name so we can assign to it during return. ~b stands for 'blank'. - // The name must be different from ~r above because if you have - // func f() (_ int) - // func g() int - // f is allowed to use a plain 'return' with no arguments, while g is not. - // So the two cases must be distinguished. - // We do not record a pointer to the original node (n->orig). - // Having multiple names causes too much confusion in later passes. - nn = Nod(OXXX, nil, nil) - - *nn = *n.Left - nn.Orig = nn - nn.Sym = Lookupf("~b%d", gen) - gen++ - n.Left = nn - } - - n.Left.Ntype = n.Right - declare(n.Left, PPARAMOUT) - if dclcontext == PAUTO { - i++ - n.Left.Vargen = int32(i) - } - } -} - -/* - * Same as funcargs, except run over an already constructed TFUNC. - * This happens during import, where the hidden_fndcl rule has - * used functype directly to parse the function's type. - */ -func funcargs2(t *Type) { - if t.Etype != TFUNC { - Fatal("funcargs2 %v", t) - } - - if t.Thistuple != 0 { - var n *Node - for ft := getthisx(t).Type; ft != nil; ft = ft.Down { - if ft.Nname == nil || ft.Nname.Sym == nil { - continue - } - n = ft.Nname // no need for newname(ft->nname->sym) - n.Type = ft.Type - declare(n, PPARAM) - } - } - - if t.Intuple != 0 { - var n *Node - for ft := getinargx(t).Type; ft != nil; ft = ft.Down { - if ft.Nname == nil || ft.Nname.Sym == nil { - continue - } - n = ft.Nname - n.Type = ft.Type - declare(n, PPARAM) - } - } - - if t.Outtuple != 0 { - var n *Node - for ft := getoutargx(t).Type; ft != nil; ft = ft.Down { - if ft.Nname == nil || ft.Nname.Sym == nil { - continue - } - n = ft.Nname - n.Type = ft.Type - declare(n, PPARAMOUT) - } - } -} - -/* - * finish the body. - * called in auto-declaration context. - * returns in extern-declaration context. - */ -func funcbody(n *Node) { - // change the declaration context from auto to extern - if dclcontext != PAUTO { - Fatal("funcbody: dclcontext") - } - popdcl() - Funcdepth-- - Curfn = n.Outer - n.Outer = nil - if Funcdepth == 0 { - dclcontext = PEXTERN - } -} - -/* - * new type being defined with name s. - */ -func typedcl0(s *Sym) *Node { - n := newname(s) - n.Op = OTYPE - declare(n, dclcontext) - return n -} - -/* - * node n, which was returned by typedcl0 - * is being declared to have uncompiled type t. - * return the ODCLTYPE node to use. - */ -func typedcl1(n *Node, t *Node, local bool) *Node { - n.Ntype = t - n.Local = local - return Nod(ODCLTYPE, n, nil) -} - -/* - * structs, functions, and methods. - * they don't belong here, but where do they belong? - */ -func checkembeddedtype(t *Type) { - if t == nil { - return - } - - if t.Sym == nil && Isptr[t.Etype] { - t = t.Type - if t.Etype == TINTER { - Yyerror("embedded type cannot be a pointer to interface") - } - } - - if Isptr[t.Etype] { - Yyerror("embedded type cannot be a pointer") - } else if t.Etype == TFORW && t.Embedlineno == 0 { - t.Embedlineno = lineno - } -} - -func structfield(n *Node) *Type { - lno := int(lineno) - lineno = n.Lineno - - if n.Op != ODCLFIELD { - Fatal("structfield: oops %v\n", n) - } - - f := typ(TFIELD) - f.Isddd = n.Isddd - - if n.Right != nil { - typecheck(&n.Right, Etype) - n.Type = n.Right.Type - if n.Left != nil { - n.Left.Type = n.Type - } - if n.Embedded != 0 { - checkembeddedtype(n.Type) - } - } - - n.Right = nil - - f.Type = n.Type - if f.Type == nil { - f.Broke = 1 - } - - switch n.Val.Ctype { - case CTSTR: - f.Note = new(string) - *f.Note = n.Val.U.(string) - - default: - Yyerror("field annotation must be string") - fallthrough - - case CTxxx: - f.Note = nil - } - - if n.Left != nil && n.Left.Op == ONAME { - f.Nname = n.Left - f.Embedded = n.Embedded - f.Sym = f.Nname.Sym - } - - lineno = int32(lno) - return f -} - -var uniqgen uint32 - -func checkdupfields(t *Type, what string) { - lno := int(lineno) - - for ; t != nil; t = t.Down { - if t.Sym != nil && t.Nname != nil && !isblank(t.Nname) { - if t.Sym.Uniqgen == uniqgen { - lineno = t.Nname.Lineno - Yyerror("duplicate %s %s", what, t.Sym.Name) - } else { - t.Sym.Uniqgen = uniqgen - } - } - } - - lineno = int32(lno) -} - -/* - * convert a parsed id/type list into - * a type for struct/interface/arglist - */ -func tostruct(l *NodeList) *Type { - var f *Type - t := typ(TSTRUCT) - - for tp := &t.Type; l != nil; l = l.Next { - f = structfield(l.N) - - *tp = f - tp = &f.Down - } - - for f := t.Type; f != nil && t.Broke == 0; f = f.Down { - if f.Broke != 0 { - t.Broke = 1 - } - } - - uniqgen++ - checkdupfields(t.Type, "field") - - if t.Broke == 0 { - checkwidth(t) - } - - return t -} - -func tofunargs(l *NodeList) *Type { - var f *Type - - t := typ(TSTRUCT) - t.Funarg = 1 - - for tp := &t.Type; l != nil; l = l.Next { - f = structfield(l.N) - f.Funarg = 1 - - // esc.c needs to find f given a PPARAM to add the tag. - if l.N.Left != nil && l.N.Left.Class == PPARAM { - l.N.Left.Paramfld = f - } - - *tp = f - tp = &f.Down - } - - for f := t.Type; f != nil && t.Broke == 0; f = f.Down { - if f.Broke != 0 { - t.Broke = 1 - } - } - - return t -} - -func interfacefield(n *Node) *Type { - lno := int(lineno) - lineno = n.Lineno - - if n.Op != ODCLFIELD { - Fatal("interfacefield: oops %v\n", n) - } - - if n.Val.Ctype != CTxxx { - Yyerror("interface method cannot have annotation") - } - - f := typ(TFIELD) - f.Isddd = n.Isddd - - if n.Right != nil { - if n.Left != nil { - // queue resolution of method type for later. - // right now all we need is the name list. - // avoids cycles for recursive interface types. - n.Type = typ(TINTERMETH) - - n.Type.Nname = n.Right - n.Left.Type = n.Type - queuemethod(n) - - if n.Left.Op == ONAME { - f.Nname = n.Left - f.Embedded = n.Embedded - f.Sym = f.Nname.Sym - } - } else { - typecheck(&n.Right, Etype) - n.Type = n.Right.Type - - if n.Embedded != 0 { - checkembeddedtype(n.Type) - } - - if n.Type != nil { - switch n.Type.Etype { - case TINTER: - break - - case TFORW: - Yyerror("interface type loop involving %v", n.Type) - f.Broke = 1 - - default: - Yyerror("interface contains embedded non-interface %v", n.Type) - f.Broke = 1 - } - } - } - } - - n.Right = nil - - f.Type = n.Type - if f.Type == nil { - f.Broke = 1 - } - - lineno = int32(lno) - return f -} - -func tointerface(l *NodeList) *Type { - var f *Type - var t1 *Type - - t := typ(TINTER) - - tp := &t.Type - for ; l != nil; l = l.Next { - f = interfacefield(l.N) - - if l.N.Left == nil && f.Type.Etype == TINTER { - // embedded interface, inline methods - for t1 = f.Type.Type; t1 != nil; t1 = t1.Down { - f = typ(TFIELD) - f.Type = t1.Type - f.Broke = t1.Broke - f.Sym = t1.Sym - if f.Sym != nil { - f.Nname = newname(f.Sym) - } - *tp = f - tp = &f.Down - } - } else { - *tp = f - tp = &f.Down - } - } - - for f := t.Type; f != nil && t.Broke == 0; f = f.Down { - if f.Broke != 0 { - t.Broke = 1 - } - } - - uniqgen++ - checkdupfields(t.Type, "method") - t = sortinter(t) - checkwidth(t) - - return t -} - -func embedded(s *Sym, pkg *Pkg) *Node { - const ( - CenterDot = 0xB7 - ) - // Names sometimes have disambiguation junk - // appended after a center dot. Discard it when - // making the name for the embedded struct field. - name := s.Name - - if i := strings.Index(s.Name, string(CenterDot)); i >= 0 { - name = s.Name[:i] - } - - var n *Node - if exportname(name) { - n = newname(Lookup(name)) - } else if s.Pkg == builtinpkg { - // The name of embedded builtins belongs to pkg. - n = newname(Pkglookup(name, pkg)) - } else { - n = newname(Pkglookup(name, s.Pkg)) - } - n = Nod(ODCLFIELD, n, oldname(s)) - n.Embedded = 1 - return n -} - -/* - * check that the list of declarations is either all anonymous or all named - */ -func findtype(l *NodeList) *Node { - for ; l != nil; l = l.Next { - if l.N.Op == OKEY { - return l.N.Right - } - } - return nil -} - -func checkarglist(all *NodeList, input int) *NodeList { - named := 0 - for l := all; l != nil; l = l.Next { - if l.N.Op == OKEY { - named = 1 - break - } - } - - if named != 0 { - var n *Node - var l *NodeList - for l = all; l != nil; l = l.Next { - n = l.N - if n.Op != OKEY && n.Sym == nil { - Yyerror("mixed named and unnamed function parameters") - break - } - } - - if l == nil && n != nil && n.Op != OKEY { - Yyerror("final function parameter must have type") - } - } - - var nextt *Node - var t *Node - var n *Node - for l := all; l != nil; l = l.Next { - // can cache result from findtype to avoid - // quadratic behavior here, but unlikely to matter. - n = l.N - - if named != 0 { - if n.Op == OKEY { - t = n.Right - n = n.Left - nextt = nil - } else { - if nextt == nil { - nextt = findtype(l) - } - t = nextt - } - } else { - t = n - n = nil - } - - // during import l->n->op is OKEY, but l->n->left->sym == S - // means it was a '?', not that it was - // a lone type This doesn't matter for the exported - // declarations, which are parsed by rules that don't - // use checkargs, but can happen for func literals in - // the inline bodies. - // TODO(rsc) this can go when typefmt case TFIELD in exportmode fmt.c prints _ instead of ? - if importpkg != nil && n.Sym == nil { - n = nil - } - - if n != nil && n.Sym == nil { - t = n - n = nil - } - - if n != nil { - n = newname(n.Sym) - } - n = Nod(ODCLFIELD, n, t) - if n.Right != nil && n.Right.Op == ODDD { - if input == 0 { - Yyerror("cannot use ... in output argument list") - } else if l.Next != nil { - Yyerror("can only use ... as final argument in list") - } - n.Right.Op = OTARRAY - n.Right.Right = n.Right.Left - n.Right.Left = nil - n.Isddd = true - if n.Left != nil { - n.Left.Isddd = true - } - } - - l.N = n - } - - return all -} - -func fakethis() *Node { - n := Nod(ODCLFIELD, nil, typenod(Ptrto(typ(TSTRUCT)))) - return n -} - -/* - * Is this field a method on an interface? - * Those methods have an anonymous - * *struct{} as the receiver. - * (See fakethis above.) - */ -func isifacemethod(f *Type) bool { - rcvr := getthisx(f).Type - if rcvr.Sym != nil { - return false - } - t := rcvr.Type - if !Isptr[t.Etype] { - return false - } - t = t.Type - if t.Sym != nil || t.Etype != TSTRUCT || t.Type != nil { - return false - } - return true -} - -/* - * turn a parsed function declaration - * into a type - */ -func functype(this *Node, in *NodeList, out *NodeList) *Type { - t := typ(TFUNC) - - var rcvr *NodeList - if this != nil { - rcvr = list1(this) - } - t.Type = tofunargs(rcvr) - t.Type.Down = tofunargs(out) - t.Type.Down.Down = tofunargs(in) - - uniqgen++ - checkdupfields(t.Type.Type, "argument") - checkdupfields(t.Type.Down.Type, "argument") - checkdupfields(t.Type.Down.Down.Type, "argument") - - if t.Type.Broke != 0 || t.Type.Down.Broke != 0 || t.Type.Down.Down.Broke != 0 { - t.Broke = 1 - } - - if this != nil { - t.Thistuple = 1 - } - t.Outtuple = count(out) - t.Intuple = count(in) - t.Outnamed = 0 - if t.Outtuple > 0 && out.N.Left != nil && out.N.Left.Orig != nil { - s := out.N.Left.Orig.Sym - if s != nil && (s.Name[0] != '~' || s.Name[1] != 'r') { // ~r%d is the name invented for an unnamed result - t.Outnamed = 1 - } - } - - return t -} - -var methodsym_toppkg *Pkg - -func methodsym(nsym *Sym, t0 *Type, iface int) *Sym { - var s *Sym - var p string - var suffix string - var spkg *Pkg - - t := t0 - if t == nil { - goto bad - } - s = t.Sym - if s == nil && Isptr[t.Etype] { - t = t.Type - if t == nil { - goto bad - } - s = t.Sym - } - - spkg = nil - if s != nil { - spkg = s.Pkg - } - - // if t0 == *t and t0 has a sym, - // we want to see *t, not t0, in the method name. - if t != t0 && t0.Sym != nil { - t0 = Ptrto(t) - } - - suffix = "" - if iface != 0 { - dowidth(t0) - if t0.Width < Types[Tptr].Width { - suffix = "·i" - } - } - - if (spkg == nil || nsym.Pkg != spkg) && !exportname(nsym.Name) { - if t0.Sym == nil && Isptr[t0.Etype] { - p = fmt.Sprintf("(%v).%s.%s%s", Tconv(t0, obj.FmtLeft|obj.FmtShort), nsym.Pkg.Prefix, nsym.Name, suffix) - } else { - p = fmt.Sprintf("%v.%s.%s%s", Tconv(t0, obj.FmtLeft|obj.FmtShort), nsym.Pkg.Prefix, nsym.Name, suffix) - } - } else { - if t0.Sym == nil && Isptr[t0.Etype] { - p = fmt.Sprintf("(%v).%s%s", Tconv(t0, obj.FmtLeft|obj.FmtShort), nsym.Name, suffix) - } else { - p = fmt.Sprintf("%v.%s%s", Tconv(t0, obj.FmtLeft|obj.FmtShort), nsym.Name, suffix) - } - } - - if spkg == nil { - if methodsym_toppkg == nil { - methodsym_toppkg = mkpkg("go") - } - spkg = methodsym_toppkg - } - - s = Pkglookup(p, spkg) - - return s - -bad: - Yyerror("illegal receiver type: %v", t0) - return nil -} - -func methodname(n *Node, t *Type) *Node { - s := methodsym(n.Sym, t, 0) - if s == nil { - return n - } - return newname(s) -} - -func methodname1(n *Node, t *Node) *Node { - star := "" - if t.Op == OIND { - star = "*" - t = t.Left - } - - if t.Sym == nil || isblank(n) { - return newfuncname(n.Sym) - } - - var p string - if star != "" { - p = fmt.Sprintf("(%s%v).%v", star, t.Sym, n.Sym) - } else { - p = fmt.Sprintf("%v.%v", t.Sym, n.Sym) - } - - if exportname(t.Sym.Name) { - n = newfuncname(Lookup(p)) - } else { - n = newfuncname(Pkglookup(p, t.Sym.Pkg)) - } - - return n -} - -/* - * add a method, declared as a function, - * n is fieldname, pa is base type, t is function type - */ -func addmethod(sf *Sym, t *Type, local bool, nointerface bool) { - // get field sym - if sf == nil { - Fatal("no method symbol") - } - - // get parent type sym - pa := getthisx(t).Type // ptr to this structure - if pa == nil { - Yyerror("missing receiver") - return - } - - pa = pa.Type - f := methtype(pa, 1) - if f == nil { - t = pa - if t == nil { // rely on typecheck having complained before - return - } - if t != nil { - if Isptr[t.Etype] { - if t.Sym != nil { - Yyerror("invalid receiver type %v (%v is a pointer type)", pa, t) - return - } - - t = t.Type - } - - if t.Broke != 0 { // rely on typecheck having complained before - return - } - if t.Sym == nil { - Yyerror("invalid receiver type %v (%v is an unnamed type)", pa, t) - return - } - - if Isptr[t.Etype] { - Yyerror("invalid receiver type %v (%v is a pointer type)", pa, t) - return - } - - if t.Etype == TINTER { - Yyerror("invalid receiver type %v (%v is an interface type)", pa, t) - return - } - } - - // Should have picked off all the reasons above, - // but just in case, fall back to generic error. - Yyerror("invalid receiver type %v (%v / %v)", pa, Tconv(pa, obj.FmtLong), Tconv(t, obj.FmtLong)) - - return - } - - pa = f - if pa.Etype == TSTRUCT { - for f := pa.Type; f != nil; f = f.Down { - if f.Sym == sf { - Yyerror("type %v has both field and method named %v", pa, sf) - return - } - } - } - - if local && !pa.Local { - // defining method on non-local type. - Yyerror("cannot define new methods on non-local type %v", pa) - - return - } - - n := Nod(ODCLFIELD, newname(sf), nil) - n.Type = t - - var d *Type // last found - for f := pa.Method; f != nil; f = f.Down { - d = f - if f.Etype != TFIELD { - Fatal("addmethod: not TFIELD: %v", Tconv(f, obj.FmtLong)) - } - if sf.Name != f.Sym.Name { - continue - } - if !Eqtype(t, f.Type) { - Yyerror("method redeclared: %v.%v\n\t%v\n\t%v", pa, sf, f.Type, t) - } - return - } - - f = structfield(n) - f.Nointerface = nointerface - - // during import unexported method names should be in the type's package - if importpkg != nil && f.Sym != nil && !exportname(f.Sym.Name) && f.Sym.Pkg != structpkg { - Fatal("imported method name %v in wrong package %s\n", Sconv(f.Sym, obj.FmtSign), structpkg.Name) - } - - if d == nil { - pa.Method = f - } else { - d.Down = f - } - return -} - -func funccompile(n *Node) { - Stksize = BADWIDTH - Maxarg = 0 - - if n.Type == nil { - if nerrors == 0 { - Fatal("funccompile missing type") - } - return - } - - // assign parameter offsets - checkwidth(n.Type) - - if Curfn != nil { - Fatal("funccompile %v inside %v", n.Nname.Sym, Curfn.Nname.Sym) - } - - Stksize = 0 - dclcontext = PAUTO - Funcdepth = n.Funcdepth + 1 - compile(n) - Curfn = nil - Funcdepth = 0 - dclcontext = PEXTERN -} - -func funcsym(s *Sym) *Sym { - if s.Fsym != nil { - return s.Fsym - } - - s1 := Pkglookup(s.Name+"·f", s.Pkg) - if s1.Def == nil { - s1.Def = newfuncname(s1) - s1.Def.Func.Shortname = newname(s) - funcsyms = list(funcsyms, s1.Def) - } - s.Fsym = s1 - - return s1 -} diff --git a/src/cmd/internal/gc/esc.go b/src/cmd/internal/gc/esc.go deleted file mode 100644 index a9a1748b9a..0000000000 --- a/src/cmd/internal/gc/esc.go +++ /dev/null @@ -1,1758 +0,0 @@ -// Copyright 2011 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 gc - -import ( - "cmd/internal/obj" - "fmt" - "strings" -) - -// Run analysis on minimal sets of mutually recursive functions -// or single non-recursive functions, bottom up. -// -// Finding these sets is finding strongly connected components -// in the static call graph. The algorithm for doing that is taken -// from Sedgewick, Algorithms, Second Edition, p. 482, with two -// adaptations. -// -// First, a hidden closure function (n->curfn != N) cannot be the -// root of a connected component. Refusing to use it as a root -// forces it into the component of the function in which it appears. -// This is more convenient for escape analysis. -// -// Second, each function becomes two virtual nodes in the graph, -// with numbers n and n+1. We record the function's node number as n -// but search from node n+1. If the search tells us that the component -// number (min) is n+1, we know that this is a trivial component: one function -// plus its closures. If the search tells us that the component number is -// n, then there was a path from node n+1 back to node n, meaning that -// the function set is mutually recursive. The escape analysis can be -// more precise when analyzing a single non-recursive function than -// when analyzing a set of mutually recursive functions. - -// TODO(rsc): Look into using a map[*Node]bool instead of walkgen, -// to allow analysis passes to use walkgen themselves. - -type bottomUpVisitor struct { - analyze func(*NodeList, bool) - visitgen uint32 - stack *NodeList -} - -// visitBottomUp invokes analyze on the ODCLFUNC nodes listed in list. -// It calls analyze with successive groups of functions, working from -// the bottom of the call graph upward. Each time analyze is called with -// a list of functions, every function on that list only calls other functions -// on the list or functions that have been passed in previous invocations of -// analyze. Closures appear in the same list as their outer functions. -// The lists are as short as possible while preserving those requirements. -// (In a typical program, many invocations of analyze will be passed just -// a single function.) The boolean argument 'recursive' passed to analyze -// specifies whether the functions on the list are mutually recursive. -// If recursive is false, the list consists of only a single function and its closures. -// If recursive is true, the list may still contain only a single function, -// if that function is itself recursive. -func visitBottomUp(list *NodeList, analyze func(list *NodeList, recursive bool)) { - for l := list; l != nil; l = l.Next { - l.N.Walkgen = 0 - } - - var v bottomUpVisitor - v.analyze = analyze - for l := list; l != nil; l = l.Next { - if l.N.Op == ODCLFUNC && l.N.Curfn == nil { - v.visit(l.N) - } - } - - for l := list; l != nil; l = l.Next { - l.N.Walkgen = 0 - } -} - -func (v *bottomUpVisitor) visit(n *Node) uint32 { - if n.Walkgen > 0 { - // already visited - return n.Walkgen - } - - v.visitgen++ - n.Walkgen = v.visitgen - v.visitgen++ - min := v.visitgen - - l := new(NodeList) - l.Next = v.stack - l.N = n - v.stack = l - min = v.visitcodelist(n.Nbody, min) - if (min == n.Walkgen || min == n.Walkgen+1) && n.Curfn == nil { - // This node is the root of a strongly connected component. - - // The original min passed to visitcodelist was n->walkgen+1. - // If visitcodelist found its way back to n->walkgen, then this - // block is a set of mutually recursive functions. - // Otherwise it's just a lone function that does not recurse. - recursive := min == n.Walkgen - - // Remove connected component from stack. - // Mark walkgen so that future visits return a large number - // so as not to affect the caller's min. - block := v.stack - - var l *NodeList - for l = v.stack; l.N != n; l = l.Next { - l.N.Walkgen = ^uint32(0) - } - n.Walkgen = ^uint32(0) - v.stack = l.Next - l.Next = nil - - // Run escape analysis on this set of functions. - v.analyze(block, recursive) - } - - return min -} - -func (v *bottomUpVisitor) visitcodelist(l *NodeList, min uint32) uint32 { - for ; l != nil; l = l.Next { - min = v.visitcode(l.N, min) - } - return min -} - -func (v *bottomUpVisitor) visitcode(n *Node, min uint32) uint32 { - if n == nil { - return min - } - - min = v.visitcodelist(n.Ninit, min) - min = v.visitcode(n.Left, min) - min = v.visitcode(n.Right, min) - min = v.visitcodelist(n.List, min) - min = v.visitcode(n.Ntest, min) - min = v.visitcode(n.Nincr, min) - min = v.visitcodelist(n.Nbody, min) - min = v.visitcodelist(n.Nelse, min) - min = v.visitcodelist(n.Rlist, min) - - if n.Op == OCALLFUNC || n.Op == OCALLMETH { - fn := n.Left - if n.Op == OCALLMETH { - fn = n.Left.Right.Sym.Def - } - if fn != nil && fn.Op == ONAME && fn.Class == PFUNC && fn.Defn != nil { - m := v.visit(fn.Defn) - if m < min { - min = m - } - } - } - - if n.Op == OCLOSURE { - m := v.visit(n.Closure) - if m < min { - min = m - } - } - - return min -} - -// Escape analysis. - -// An escape analysis pass for a set of functions. -// The analysis assumes that closures and the functions in which they -// appear are analyzed together, so that the aliasing between their -// variables can be modeled more precisely. -// -// First escfunc, esc and escassign recurse over the ast of each -// function to dig out flow(dst,src) edges between any -// pointer-containing nodes and store them in dst->escflowsrc. For -// variables assigned to a variable in an outer scope or used as a -// return value, they store a flow(theSink, src) edge to a fake node -// 'the Sink'. For variables referenced in closures, an edge -// flow(closure, &var) is recorded and the flow of a closure itself to -// an outer scope is tracked the same way as other variables. -// -// Then escflood walks the graph starting at theSink and tags all -// variables of it can reach an & node as escaping and all function -// parameters it can reach as leaking. -// -// If a value's address is taken but the address does not escape, -// then the value can stay on the stack. If the value new(T) does -// not escape, then new(T) can be rewritten into a stack allocation. -// The same is true of slice literals. -// -// If optimizations are disabled (-N), this code is not used. -// Instead, the compiler assumes that any value whose address -// is taken without being immediately dereferenced -// needs to be moved to the heap, and new(T) and slice -// literals are always real allocations. - -func escapes(all *NodeList) { - visitBottomUp(all, escAnalyze) -} - -const ( - EscFuncUnknown = 0 + iota - EscFuncPlanned - EscFuncStarted - EscFuncTagged -) - -// There appear to be some loops in the escape graph, causing -// arbitrary recursion into deeper and deeper levels. -// Cut this off safely by making minLevel sticky: once you -// get that deep, you cannot go down any further but you also -// cannot go up any further. This is a conservative fix. -// Making minLevel smaller (more negative) would handle more -// complex chains of indirections followed by address-of operations, -// at the cost of repeating the traversal once for each additional -// allowed level when a loop is encountered. Using -2 suffices to -// pass all the tests we have written so far, which we assume matches -// the level of complexity we want the escape analysis code to handle. -const ( - MinLevel = -2 -) - -// A Level encodes the reference state and context applied to -// (stack, heap) allocated memory. -// -// value is the overall sum of *(1) and &(-1) operations encountered -// along a path from a destination (sink, return value) to a source -// (allocation, parameter). -// -// suffixValue is the maximum-copy-started-suffix-level applied to a sink. -// For example: -// sink = x.left.left --> level=2, x is dereferenced twice and does not escape to sink. -// sink = &Node{x} --> level=-1, x is accessible from sink via one "address of" -// sink = &Node{&Node{x}} --> level=-2, x is accessible from sink via two "address of" -// sink = &Node{&Node{x.left}} --> level=-1, but x is NOT accessible from sink because it was indirected and then copied. -// (The copy operations are sometimes implicit in the source code; in this case, -// value of x.left was copied into a field of a newly allocated Node) -// -// There's one of these for each Node, and the integer values -// rarely exceed even what can be stored in 4 bits, never mind 8. -type Level struct { - value, suffixValue int8 -} - -func (l Level) int() int { - return int(l.value) -} - -func levelFrom(i int) Level { - if i <= MinLevel { - return Level{value: MinLevel} - } - return Level{value: int8(i)} -} - -func satInc8(x int8) int8 { - if x == 127 { - return 127 - } - return x + 1 -} - -func satAdd8(x, y int8) int8 { - z := x + y - if x^y < 0 || x^z >= 0 { - return z - } - if x < 0 { - return -128 - } - return 127 -} - -func min8(a, b int8) int8 { - if a < b { - return a - } - return b -} - -func max8(a, b int8) int8 { - if a > b { - return a - } - return b -} - -// inc returns the level l + 1, representing the effect of an indirect (*) operation. -func (l Level) inc() Level { - if l.value <= MinLevel { - return Level{value: MinLevel} - } - return Level{value: satInc8(l.value), suffixValue: satInc8(l.suffixValue)} -} - -// dec returns the level l - 1, representing the effect of an address-of (&) operation. -func (l Level) dec() Level { - if l.value <= MinLevel { - return Level{value: MinLevel} - } - return Level{value: l.value - 1, suffixValue: l.suffixValue - 1} -} - -// copy returns the level for a copy of a value with level l. -func (l Level) copy() Level { - return Level{value: l.value, suffixValue: max8(l.suffixValue, 0)} -} - -func (l1 Level) min(l2 Level) Level { - return Level{ - value: min8(l1.value, l2.value), - suffixValue: min8(l1.suffixValue, l2.suffixValue)} -} - -// guaranteedDereference returns the number of dereferences -// applied to a pointer before addresses are taken/generated. -// This is the maximum level computed from path suffixes starting -// with copies where paths flow from destination to source. -func (l Level) guaranteedDereference() int { - return int(l.suffixValue) -} - -// Escape constants are numbered in order of increasing "escapiness" -// to help make inferences be monotonic. With the exception of -// EscNever which is sticky, eX < eY means that eY is more exposed -// than eX, and hence replaces it in a conservative analysis. -const ( - EscUnknown = iota - EscNone // Does not escape to heap, result, or parameters. - EscReturn // Is returned or reachable from returned. - EscScope // Allocated in an inner loop scope, assigned to an outer loop scope, - // which allows the construction of non-escaping but arbitrarily large linked - // data structures (i.e., not eligible for allocation in a fixed-size stack frame). - EscHeap // Reachable from the heap - EscNever // By construction will not escape. - EscBits = 3 - EscMask = (1 << EscBits) - 1 - EscContentEscapes = 1 << EscBits // value obtained by indirect of parameter escapes to heap - EscReturnBits = EscBits + 1 - // Node.esc encoding = | escapeReturnEncoding:(width-4) | contentEscapes:1 | escEnum:3 -) - -// escMax returns the maximum of an existing escape value -// (and its additional parameter flow flags) and a new escape type. -func escMax(e, etype uint16) uint16 { - if e&EscMask >= EscScope { - // normalize - if e&^EscMask != 0 { - Fatal("Escape information had unexpected return encoding bits (w/ EscScope, EscHeap, EscNever), e&EscMask=%v", e&EscMask) - } - } - if e&EscMask > etype { - return e - } - if etype == EscNone || etype == EscReturn { - return (e &^ EscMask) | etype - } - return etype -} - -// For each input parameter to a function, the escapeReturnEncoding describes -// how the parameter may leak to the function's outputs. This is currently the -// "level" of the leak where level is 0 or larger (negative level means stored into -// something whose address is returned -- but that implies stored into the heap, -// hence EscHeap, which means that the details are not currently relevant. ) -const ( - bitsPerOutputInTag = 3 // For each output, the number of bits for a tag - bitsMaskForTag = uint16(1<dsts, %d edges\n", e->dstcount, e->edgecount); - - // visit the upstream of each dst, mark address nodes with - // addrescapes, mark parameters unsafe - for l := e.dsts; l != nil; l = l.Next { - escflood(e, l.N) - } - - // for all top level functions, tag the typenodes corresponding to the param nodes - for l := all; l != nil; l = l.Next { - if l.N.Op == ODCLFUNC { - esctag(e, l.N) - } - } - - if Debug['m'] != 0 { - for l := e.noesc; l != nil; l = l.Next { - if l.N.Esc == EscNone { - Warnl(int(l.N.Lineno), "%v %v does not escape", curfnSym(l.N), Nconv(l.N, obj.FmtShort)) - } - } - } -} - -func escfunc(e *EscState, func_ *Node) { - // print("escfunc %N %s\n", func->nname, e->recursive?"(recursive)":""); - - if func_.Esc != 1 { - Fatal("repeat escfunc %v", func_.Nname) - } - func_.Esc = EscFuncStarted - - saveld := e.loopdepth - e.loopdepth = 1 - savefn := Curfn - Curfn = func_ - - for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { - if ll.N.Op != ONAME { - continue - } - switch ll.N.Class { - // out params are in a loopdepth between the sink and all local variables - case PPARAMOUT: - ll.N.Escloopdepth = 0 - - case PPARAM: - ll.N.Escloopdepth = 1 - if ll.N.Type != nil && !haspointers(ll.N.Type) { - break - } - if Curfn.Nbody == nil && !Curfn.Noescape { - ll.N.Esc = EscHeap - } else { - ll.N.Esc = EscNone // prime for escflood later - } - e.noesc = list(e.noesc, ll.N) - } - } - - // in a mutually recursive group we lose track of the return values - if e.recursive { - for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { - if ll.N.Op == ONAME && ll.N.Class == PPARAMOUT { - escflows(e, &e.theSink, ll.N) - } - } - } - - escloopdepthlist(e, Curfn.Nbody) - esclist(e, Curfn.Nbody, Curfn) - Curfn = savefn - e.loopdepth = saveld -} - -// Mark labels that have no backjumps to them as not increasing e->loopdepth. -// Walk hasn't generated (goto|label)->left->sym->label yet, so we'll cheat -// and set it to one of the following two. Then in esc we'll clear it again. -var looping Label - -var nonlooping Label - -func escloopdepthlist(e *EscState, l *NodeList) { - for ; l != nil; l = l.Next { - escloopdepth(e, l.N) - } -} - -func escloopdepth(e *EscState, n *Node) { - if n == nil { - return - } - - escloopdepthlist(e, n.Ninit) - - switch n.Op { - case OLABEL: - if n.Left == nil || n.Left.Sym == nil { - Fatal("esc:label without label: %v", Nconv(n, obj.FmtSign)) - } - - // Walk will complain about this label being already defined, but that's not until - // after escape analysis. in the future, maybe pull label & goto analysis out of walk and put before esc - // if(n->left->sym->label != nil) - // fatal("escape analysis messed up analyzing label: %+N", n); - n.Left.Sym.Label = &nonlooping - - case OGOTO: - if n.Left == nil || n.Left.Sym == nil { - Fatal("esc:goto without label: %v", Nconv(n, obj.FmtSign)) - } - - // If we come past one that's uninitialized, this must be a (harmless) forward jump - // but if it's set to nonlooping the label must have preceded this goto. - if n.Left.Sym.Label == &nonlooping { - n.Left.Sym.Label = &looping - } - } - - escloopdepth(e, n.Left) - escloopdepth(e, n.Right) - escloopdepthlist(e, n.List) - escloopdepth(e, n.Ntest) - escloopdepth(e, n.Nincr) - escloopdepthlist(e, n.Nbody) - escloopdepthlist(e, n.Nelse) - escloopdepthlist(e, n.Rlist) -} - -func esclist(e *EscState, l *NodeList, up *Node) { - for ; l != nil; l = l.Next { - esc(e, l.N, up) - } -} - -func esc(e *EscState, n *Node, up *Node) { - if n == nil { - return - } - - lno := int(setlineno(n)) - - // ninit logically runs at a different loopdepth than the rest of the for loop. - esclist(e, n.Ninit, n) - - if n.Op == OFOR || n.Op == ORANGE { - e.loopdepth++ - } - - // type switch variables have no ODCL. - // process type switch as declaration. - // must happen before processing of switch body, - // so before recursion. - if n.Op == OSWITCH && n.Ntest != nil && n.Ntest.Op == OTYPESW { - for ll := n.List; ll != nil; ll = ll.Next { // cases - - // ll->n->nname is the variable per case - if ll.N.Nname != nil { - ll.N.Nname.Escloopdepth = e.loopdepth - } - } - } - - esc(e, n.Left, n) - esc(e, n.Right, n) - esc(e, n.Ntest, n) - esc(e, n.Nincr, n) - esclist(e, n.Nbody, n) - esclist(e, n.Nelse, n) - esclist(e, n.List, n) - esclist(e, n.Rlist, n) - - if n.Op == OFOR || n.Op == ORANGE { - e.loopdepth-- - } - - if Debug['m'] > 1 { - fmt.Printf("%v:[%d] %v esc: %v\n", Ctxt.Line(int(lineno)), e.loopdepth, funcSym(Curfn), n) - } - - switch n.Op { - // Record loop depth at declaration. - case ODCL: - if n.Left != nil { - n.Left.Escloopdepth = e.loopdepth - } - - case OLABEL: - if n.Left.Sym.Label == &nonlooping { - if Debug['m'] > 1 { - fmt.Printf("%v:%v non-looping label\n", Ctxt.Line(int(lineno)), n) - } - } else if n.Left.Sym.Label == &looping { - if Debug['m'] > 1 { - fmt.Printf("%v: %v looping label\n", Ctxt.Line(int(lineno)), n) - } - e.loopdepth++ - } - - // See case OLABEL in escloopdepth above - // else if(n->left->sym->label == nil) - // fatal("escape analysis missed or messed up a label: %+N", n); - - n.Left.Sym.Label = nil - - // Everything but fixed array is a dereference. - case ORANGE: - 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: - if n.Ntest != nil && n.Ntest.Op == OTYPESW { - for ll := n.List; ll != nil; ll = ll.Next { // cases - - // ntest->right is the argument of the .(type), - // ll->n->nname is the variable per case - escassign(e, ll.N.Nname, n.Ntest.Right) - } - } - - // Filter out the following special case. - // - // func (b *Buffer) Foo() { - // n, m := ... - // b.buf = b.buf[n:m] - // } - // - // This assignment is a no-op for escape analysis, - // it does not store any new pointers into b that were not already there. - // However, without this special case b will escape, because we assign to OIND/ODOTPTR. - case OAS, OASOP, OASWB: - if (n.Left.Op == OIND || n.Left.Op == ODOTPTR) && n.Left.Left.Op == ONAME && // dst is ONAME dereference - (n.Right.Op == OSLICE || n.Right.Op == OSLICE3 || n.Right.Op == OSLICESTR) && // src is slice operation - (n.Right.Left.Op == OIND || n.Right.Left.Op == ODOTPTR) && n.Right.Left.Left.Op == ONAME && // slice is applied to ONAME dereference - n.Left.Left == n.Right.Left.Left { // dst and src reference the same base ONAME - - // Here we also assume that the statement will not contain calls, - // that is, that order will move any calls to init. - // Otherwise base ONAME value could change between the moments - // when we evaluate it for dst and for src. - // - // Note, this optimization does not apply to OSLICEARR, - // because it does introduce a new pointer into b that was not already there - // (pointer to b itself). After such assignment, if b contents escape, - // 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 { - Warnl(int(n.Lineno), "%v ignoring self-assignment to %v", curfnSym(n), Nconv(n.Left, obj.FmtShort)) - } - - break - } - - escassign(e, n.Left, n.Right) - - case OAS2: // x,y = a,b - if count(n.List) == count(n.Rlist) { - ll := n.List - lr := n.Rlist - for ; ll != nil; ll, lr = ll.Next, lr.Next { - escassign(e, ll.N, lr.N) - } - } - - case OAS2RECV, // v, ok = <-ch - OAS2MAPR, // v, ok = m[k] - OAS2DOTTYPE: // v, ok = x.(type) - escassign(e, n.List.N, n.Rlist.N) - - case OSEND: // ch <- x - escassign(e, &e.theSink, n.Right) - - case ODEFER: - if e.loopdepth == 1 { // top level - break - } - // arguments leak out of scope - // TODO: leak to a dummy node instead - fallthrough - - case OPROC: - // go f(x) - f and x escape - escassign(e, &e.theSink, n.Left.Left) - - escassign(e, &e.theSink, n.Left.Right) // ODDDARG for call - for ll := n.Left.List; ll != nil; ll = ll.Next { - escassign(e, &e.theSink, ll.N) - } - - case OCALLMETH, OCALLFUNC, OCALLINTER: - esccall(e, n, up) - - // esccall already done on n->rlist->n. tie it's escretval to n->list - case OAS2FUNC: // x,y = f() - lr := n.Rlist.N.Escretval - - var ll *NodeList - for ll = n.List; lr != nil && ll != nil; lr, ll = lr.Next, ll.Next { - escassign(e, ll.N, lr.N) - } - if lr != nil || ll != nil { - Fatal("esc oas2func") - } - - case ORETURN: - ll := n.List - if count(n.List) == 1 && Curfn.Type.Outtuple > 1 { - // OAS2FUNC in disguise - // esccall already done on n->list->n - // tie n->list->n->escretval to curfn->dcl PPARAMOUT's - ll = n.List.N.Escretval - } - - for lr := Curfn.Func.Dcl; lr != nil && ll != nil; lr = lr.Next { - if lr.N.Op != ONAME || lr.N.Class != PPARAMOUT { - continue - } - escassign(e, lr.N, ll.N) - ll = ll.Next - } - - if ll != nil { - Fatal("esc return list") - } - - // Argument could leak through recover. - case OPANIC: - escassign(e, &e.theSink, n.Left) - - case OAPPEND: - if !n.Isddd { - 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) - - case OCONVIFACE: - n.Esc = EscNone // until proven otherwise - e.noesc = list(e.noesc, n) - n.Escloopdepth = e.loopdepth - escassign(e, n, n.Left) - - case OARRAYLIT: - if Isslice(n.Type) { - // Slice itself is not leaked until proven otherwise - n.Esc = EscNone - e.noesc = list(e.noesc, n) - n.Escloopdepth = e.loopdepth - } - - // Link values to array/slice - for ll := n.List; ll != nil; ll = ll.Next { - escassign(e, n, ll.N.Right) - } - - // Link values to struct. - case OSTRUCTLIT: - for ll := n.List; ll != nil; ll = ll.Next { - escassign(e, n, ll.N.Right) - } - - case OPTRLIT: - n.Esc = EscNone // until proven otherwise - e.noesc = list(e.noesc, n) - n.Escloopdepth = e.loopdepth - - // Link OSTRUCTLIT to OPTRLIT; if OPTRLIT escapes, OSTRUCTLIT elements do too. - escassign(e, n, n.Left) - - case OCALLPART: - n.Esc = EscNone // until proven otherwise - e.noesc = list(e.noesc, n) - n.Escloopdepth = e.loopdepth - - // Contents make it to memory, lose track. - escassign(e, &e.theSink, n.Left) - - case OMAPLIT: - n.Esc = EscNone // until proven otherwise - e.noesc = list(e.noesc, n) - n.Escloopdepth = e.loopdepth - - // Keys and values make it to memory, lose track. - for ll := n.List; ll != nil; ll = ll.Next { - escassign(e, &e.theSink, ll.N.Left) - escassign(e, &e.theSink, ll.N.Right) - } - - // Link addresses of captured variables to closure. - case OCLOSURE: - var a *Node - var v *Node - for ll := n.Func.Cvars; ll != nil; ll = ll.Next { - v = ll.N - if v.Op == OXXX { // unnamed out argument; see dcl.c:/^funcargs - continue - } - a = v.Closure - if !v.Name.Byval { - a = Nod(OADDR, a, nil) - a.Lineno = v.Lineno - a.Escloopdepth = e.loopdepth - typecheck(&a, Erv) - } - - escassign(e, n, a) - } - fallthrough - - case OMAKECHAN, - OMAKEMAP, - OMAKESLICE, - ONEW, - OARRAYRUNESTR, - OARRAYBYTESTR, - OSTRARRAYRUNE, - OSTRARRAYBYTE, - ORUNESTR: - n.Escloopdepth = e.loopdepth - - n.Esc = EscNone // until proven otherwise - e.noesc = list(e.noesc, n) - - case OADDSTR: - n.Escloopdepth = e.loopdepth - n.Esc = EscNone // until proven otherwise - e.noesc = list(e.noesc, n) - - // Arguments of OADDSTR do not escape. - - case OADDR: - n.Esc = EscNone // until proven otherwise - e.noesc = list(e.noesc, n) - - // current loop depth is an upper bound on actual loop depth - // of addressed value. - n.Escloopdepth = e.loopdepth - - // for &x, use loop depth of x if known. - // it should always be known, but if not, be conservative - // and keep the current loop depth. - if n.Left.Op == ONAME { - switch n.Left.Class { - case PAUTO: - if n.Left.Escloopdepth != 0 { - n.Escloopdepth = n.Left.Escloopdepth - } - - // PPARAM is loop depth 1 always. - // PPARAMOUT is loop depth 0 for writes - // but considered loop depth 1 for address-of, - // so that writing the address of one result - // to another (or the same) result makes the - // first result move to the heap. - case PPARAM, PPARAMOUT: - n.Escloopdepth = 1 - } - } - } - - lineno = int32(lno) -} - -// Assert that expr somehow gets assigned to dst, if non nil. for -// dst==nil, any name node expr still must be marked as being -// evaluated in curfn. For expr==nil, dst must still be examined for -// evaluations inside it (e.g *f(x) = y) -func escassign(e *EscState, dst *Node, src *Node) { - if isblank(dst) || dst == nil || src == nil || src.Op == ONONAME || src.Op == OXXX { - return - } - - if Debug['m'] > 1 { - fmt.Printf("%v:[%d] %v escassign: %v(%v)[%v] = %v(%v)[%v]\n", - 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)) - } - - setlineno(dst) - - // Analyze lhs of assignment. - // Replace dst with e->theSink if we can't track it. - switch dst.Op { - default: - Dump("dst", dst) - Fatal("escassign: unexpected dst") - - case OARRAYLIT, - OCLOSURE, - OCONV, - OCONVIFACE, - OCONVNOP, - OMAPLIT, - OSTRUCTLIT, - OPTRLIT, - OCALLPART: - break - - case ONAME: - if dst.Class == PEXTERN { - dst = &e.theSink - } - - case ODOT: // treat "dst.x = src" as "dst = src" - escassign(e, dst.Left, src) - - return - - case OINDEX: - if Isfixedarray(dst.Left.Type) { - escassign(e, dst.Left, src) - return - } - - dst = &e.theSink // lose track of dereference - - case OIND, ODOTPTR: - dst = &e.theSink // lose track of dereference - - // lose track of key and value - case OINDEXMAP: - escassign(e, &e.theSink, dst.Right) - - dst = &e.theSink - } - - lno := int(setlineno(src)) - e.pdepth++ - - switch src.Op { - case OADDR, // dst = &x - OIND, // dst = *x - ODOTPTR, // dst = (*x).f - ONAME, - OPARAM, - ODDDARG, - OPTRLIT, - OARRAYLIT, - OMAPLIT, - OSTRUCTLIT, - OMAKECHAN, - OMAKEMAP, - OMAKESLICE, - OARRAYRUNESTR, - OARRAYBYTESTR, - OSTRARRAYRUNE, - OSTRARRAYBYTE, - OADDSTR, - ONEW, - OCALLPART, - ORUNESTR, - OCONVIFACE: - escflows(e, dst, src) - - case OCLOSURE: - // OCLOSURE is lowered to OPTRLIT, - // insert OADDR to account for the additional indirection. - a := Nod(OADDR, src, nil) - a.Lineno = src.Lineno - a.Escloopdepth = src.Escloopdepth - a.Type = Ptrto(src.Type) - escflows(e, dst, a) - - // Flowing multiple returns to a single dst happens when - // analyzing "go f(g())": here g() flows to sink (issue 4529). - case OCALLMETH, OCALLFUNC, OCALLINTER: - for ll := src.Escretval; ll != nil; ll = ll.Next { - escflows(e, dst, ll.N) - } - - // A non-pointer escaping from a struct does not concern us. - case ODOT: - if src.Type != nil && !haspointers(src.Type) { - break - } - fallthrough - - // Conversions, field access, slice all preserve the input value. - case OCONV, - OCONVNOP, - ODOTMETH, - // treat recv.meth as a value with recv in it, only happens in ODEFER and OPROC - // iface.method already leaks iface in esccall, no need to put in extra ODOTINTER edge here - ODOTTYPE, - ODOTTYPE2, - OSLICE, - OSLICE3, - OSLICEARR, - OSLICE3ARR, - OSLICESTR: - // Conversions, field access, slice all preserve the input value. - escassign(e, dst, src.Left) - - 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 - // the operands flow into the result. - // TODO(rsc): Decide what the story is here. This is unsettling. - case OADD, - OSUB, - OOR, - OXOR, - OMUL, - ODIV, - OMOD, - OLSH, - ORSH, - OAND, - OANDNOT, - OPLUS, - OMINUS, - OCOM: - escassign(e, dst, src.Left) - - escassign(e, dst, src.Right) - } - - e.pdepth-- - lineno = int32(lno) -} - -// Common case for escapes is 16 bits 000000000xxxEEEE -// where commonest cases for xxx encoding in-to-out pointer -// flow are 000, 001, 010, 011 and EEEE is computed Esc bits. -// Note width of xxx depends on value of constant -// bitsPerOutputInTag -- expect 2 or 3, so in practice the -// tag cache array is 64 or 128 long. Some entries will -// never be populated. -var tags [1 << (bitsPerOutputInTag + EscReturnBits)]string - -// mktag returns the string representation for an escape analysis tag. -func mktag(mask int) *string { - switch mask & EscMask { - case EscNone, EscReturn: - break - - default: - Fatal("escape mktag") - } - - if mask < len(tags) && tags[mask] != "" { - return &tags[mask] - } - - s := fmt.Sprintf("esc:0x%x", mask) - if mask < len(tags) { - tags[mask] = s - } - return &s -} - -// parsetag decodes an escape analysis tag and returns the esc value. -func parsetag(note *string) uint16 { - if note == nil || !strings.HasPrefix(*note, "esc:") { - return EscUnknown - } - em := uint16(atoi((*note)[4:])) - if em == 0 { - return EscNone - } - return em -} - -// describeEscape returns a string describing the escape tag. -// The result is either one of {EscUnknown, EscNone, EscHeap} which all have no further annotation -// or a description of parameter flow, which takes the form of an optional "contentToHeap" -// indicating that the content of this parameter is leaked to the heap, followed by a sequence -// of level encodings separated by spaces, one for each parameter, where _ means no flow, -// = means direct flow, and N asterisks (*) encodes content (obtained by indirection) flow. -// e.g., "contentToHeap _ =" means that a parameter's content (one or more dereferences) -// escapes to the heap, the parameter does not leak to the first output, but does leak directly -// to the second output (and if there are more than two outputs, there is no flow to those.) -func describeEscape(em uint16) string { - var s string - if em&EscMask == EscUnknown { - s = "EscUnknown" - } - if em&EscMask == EscNone { - s = "EscNone" - } - if em&EscMask == EscHeap { - s = "EscHeap" - } - if em&EscMask == EscReturn { - s = "EscReturn" - } - if em&EscMask == EscScope { - s = "EscScope" - } - if em&EscContentEscapes != 0 { - if s != "" { - s += " " - } - s += "contentToHeap" - } - for em >>= EscReturnBits; em != 0; em = em >> bitsPerOutputInTag { - // See encoding description above - if s != "" { - s += " " - } - switch embits := em & bitsMaskForTag; embits { - case 0: - s += "_" - case 1: - s += "=" - default: - for i := uint16(0); i < embits-1; i++ { - s += "*" - } - } - - } - return s -} - -// escassignfromtag models the input-to-output assignment flow of one of a function -// calls arguments, where the flow is encoded in "note". -func escassignfromtag(e *EscState, note *string, dsts *NodeList, src *Node) uint16 { - em := parsetag(note) - - if Debug['m'] > 2 { - fmt.Printf("%v::assignfromtag:: src=%v, em=%s\n", - Ctxt.Line(int(lineno)), Nconv(src, obj.FmtShort), describeEscape(em)) - } - - if em == EscUnknown { - escassign(e, &e.theSink, src) - return em - } - - if em == EscNone { - return em - } - - // If content inside parameter (reached via indirection) - // escapes to heap, mark as such. - if em&EscContentEscapes != 0 { - escassign(e, &e.theSink, addDereference(src)) - } - - em0 := em - for em >>= EscReturnBits; em != 0 && dsts != nil; em, dsts = em>>bitsPerOutputInTag, dsts.Next { - // Prefer the lowest-level path to the reference (for escape purposes). - // Two-bit encoding (for example. 1, 3, and 4 bits are other options) - // 01 = 0-level - // 10 = 1-level, (content escapes), - // 11 = 2-level, (content of content escapes), - embits := em & bitsMaskForTag - if embits > 0 { - n := src - for i := uint16(0); i < embits-1; i++ { - n = addDereference(n) // encode level>0 as indirections - } - escassign(e, dsts.N, n) - } - } - // If there are too many outputs to fit in the tag, - // that is handled at the encoding end as EscHeap, - // so there is no need to check here. - - if em != 0 && dsts == nil { - Fatal("corrupt esc tag %q or messed up escretval list\n", note) - } - return em0 -} - -// addDereference constructs a suitable OIND note applied to src. -// Because this is for purposes of escape accounting, not execution, -// some semantically dubious node combinations are (currently) possible. -func addDereference(n *Node) *Node { - ind := Nod(OIND, n, nil) - ind.Escloopdepth = n.Escloopdepth - ind.Lineno = n.Lineno - t := n.Type - if Istype(t, Tptr) { - // This should model our own sloppy use of OIND to encode - // decreasing levels of indirection; i.e., "indirecting" an array - // might yield the type of an element. To be enhanced... - t = t.Type - } - ind.Type = t - return ind -} - -// escNoteOutputParamFlow encodes maxEncodedLevel/.../1/0-level flow to the vargen'th parameter. -// Levels greater than maxEncodedLevel are replaced with maxEncodedLevel. -// If the encoding cannot describe the modified input level and output number, then EscHeap is returned. -func escNoteOutputParamFlow(e uint16, vargen int32, level Level) uint16 { - // Flow+level is encoded in two bits. - // 00 = not flow, xx = level+1 for 0 <= level <= maxEncodedLevel - // 16 bits for Esc allows 6x2bits or 4x3bits or 3x4bits if additional information would be useful. - if level.int() <= 0 && level.guaranteedDereference() > 0 { - return escMax(e|EscContentEscapes, EscNone) // At least one deref, thus only content. - } - if level.int() < 0 { - return EscHeap - } - if level.int() > maxEncodedLevel { - // Cannot encode larger values than maxEncodedLevel. - level = levelFrom(maxEncodedLevel) - } - encoded := uint16(level.int() + 1) - - shift := uint(bitsPerOutputInTag*(vargen-1) + EscReturnBits) - old := (e >> shift) & bitsMaskForTag - if old == 0 || encoded != 0 && encoded < old { - old = encoded - } - - encodedFlow := old << shift - if (encodedFlow>>shift)&bitsMaskForTag != old { - // Encoding failure defaults to heap. - return EscHeap - } - - return (e &^ (bitsMaskForTag << shift)) | encodedFlow -} - -// 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 -// nodes, which may be marked "noescape". Navigating the ast is slightly -// different for methods vs plain functions and for imported vs -// this-package -func esccall(e *EscState, n *Node, up *Node) { - var fntype *Type - - var fn *Node - switch n.Op { - default: - Fatal("esccall") - - case OCALLFUNC: - fn = n.Left - fntype = fn.Type - - case OCALLMETH: - fn = n.Left.Right.Sym.Def - if fn != nil { - fntype = fn.Type - } else { - fntype = n.Left.Type - } - - case OCALLINTER: - fntype = n.Left.Type - } - - ll := n.List - if n.List != nil && n.List.Next == nil { - a := n.List.N - if a.Type.Etype == TSTRUCT && a.Type.Funarg != 0 { // f(g()). - ll = a.Escretval - } - } - - 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 { - fmt.Printf("%v::esccall:: %v in recursive group\n", Ctxt.Line(int(lineno)), Nconv(n, obj.FmtShort)) - } - - // function in same mutually recursive group. Incorporate into flow graph. - // print("esc local fn: %N\n", fn->ntype); - if fn.Defn.Esc == EscFuncUnknown || n.Escretval != nil { - Fatal("graph inconsistency") - } - - // set up out list on this call node - for lr := fn.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) - } - - var src *Node - for lr := fn.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. - src = Nod(ODDDARG, nil, nil) - - src.Type = typ(TARRAY) - src.Type.Type = lr.N.Type.Type - src.Type.Bound = int64(count(ll)) - src.Type = Ptrto(src.Type) // make pointer so it will be tracked - src.Escloopdepth = e.loopdepth - src.Lineno = n.Lineno - src.Esc = EscNone // until we find otherwise - e.noesc = list(e.noesc, src) - n.Right = src - } - - if lr.N.Left != nil { - escassign(e, lr.N.Left, src) - } - if src != ll.N { - break - } - } - - // "..." arguments are untracked - for ; ll != nil; ll = ll.Next { - if Debug['m'] > 2 { - fmt.Printf("%v::esccall:: ... <- %v, untracked\n", Ctxt.Line(int(lineno)), Nconv(ll.N, obj.FmtShort)) - } - escassign(e, &e.theSink, ll.N) - } - - return - } - - // Imported or completely analyzed function. Use the escape tags. - if n.Escretval != nil { - Fatal("esc already decorated call %v\n", Nconv(n, obj.FmtSign)) - } - - if Debug['m'] > 2 { - fmt.Printf("%v::esccall:: %v not recursive\n", Ctxt.Line(int(lineno)), Nconv(n, obj.FmtShort)) - } - - // 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) - } - - // print("esc analyzed fn: %#N (%+T) returning (%+H)\n", fn, fntype, n->escretval); - - // Receiver. - if n.Op != OCALLFUNC { - t := getthisx(fntype).Type - src := n.Left.Left - if haspointers(t.Type) { - escassignfromtag(e, t.Note, n.Escretval, src) - } - } - - var a *Node - for t := getinargx(fntype).Type; ll != nil; ll = ll.Next { - src = ll.N - if t.Isddd && !n.Isddd { - // Introduce ODDDARG node to represent ... allocation. - src = Nod(ODDDARG, nil, nil) - - src.Escloopdepth = e.loopdepth - src.Lineno = n.Lineno - src.Type = typ(TARRAY) - src.Type.Type = t.Type.Type - src.Type.Bound = int64(count(ll)) - src.Type = Ptrto(src.Type) // make pointer so it will be tracked - src.Esc = EscNone // until we find otherwise - e.noesc = list(e.noesc, src) - n.Right = src - } - - if haspointers(t.Type) { - if escassignfromtag(e, t.Note, n.Escretval, src) == EscNone && up.Op != ODEFER && up.Op != OPROC { - a = src - for a.Op == OCONVNOP { - a = a.Left - } - switch a.Op { - // The callee has already been analyzed, so its arguments have esc tags. - // The argument is marked as not escaping at all. - // Record that fact so that any temporary used for - // synthesizing this expression can be reclaimed when - // the function returns. - // This 'noescape' is even stronger than the usual esc == EscNone. - // src->esc == EscNone means that src does not escape the current function. - // src->noescape = 1 here means that src does not escape this statement - // in the current function. - case OCALLPART, - OCLOSURE, - ODDDARG, - OARRAYLIT, - OPTRLIT, - OSTRUCTLIT: - a.Noescape = true - } - } - } - - if src != ll.N { - break - } - t = t.Down - } - - // "..." arguments are untracked - for ; ll != nil; ll = ll.Next { - escassign(e, &e.theSink, ll.N) - if Debug['m'] > 2 { - fmt.Printf("%v::esccall:: ... <- %v, untracked\n", Ctxt.Line(int(lineno)), Nconv(ll.N, obj.FmtShort)) - } - } -} - -// escflows records the link src->dst in dst, throwing out some quick wins, -// and also ensuring that dst is noted as a flow destination. -func escflows(e *EscState, dst *Node, src *Node) { - if dst == nil || src == nil || dst == src { - return - } - - // Don't bother building a graph for scalars. - if src.Type != nil && !haspointers(src.Type) { - return - } - - if Debug['m'] > 2 { - fmt.Printf("%v::flows:: %v <- %v\n", Ctxt.Line(int(lineno)), Nconv(dst, obj.FmtShort), Nconv(src, obj.FmtShort)) - } - - if dst.Escflowsrc == nil { - e.dsts = list(e.dsts, dst) - e.dstcount++ - } - - e.edgecount++ - - dst.Escflowsrc = list(dst.Escflowsrc, src) -} - -// Whenever we hit a reference node, the level goes up by one, and whenever -// we hit an OADDR, the level goes down by one. as long as we're on a level > 0 -// finding an OADDR just means we're following the upstream of a dereference, -// so this address doesn't leak (yet). -// If level == 0, it means the /value/ of this node can reach the root of this flood. -// so if this node is an OADDR, it's argument should be marked as escaping iff -// it's currfn/e->loopdepth are different from the flood's root. -// Once an object has been moved to the heap, all of it's upstream should be considered -// escaping to the global scope. -func escflood(e *EscState, dst *Node) { - switch dst.Op { - case ONAME, OCLOSURE: - break - - default: - return - } - - if Debug['m'] > 1 { - 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 { - walkgen++ - escwalk(e, levelFrom(0), dst, l.N) - } -} - -// funcOutputAndInput reports whether dst and src correspond to output and input parameters of the same function. -func funcOutputAndInput(dst, src *Node) bool { - // Note if dst is marked as escaping, then "returned" is too weak. - return dst.Op == ONAME && dst.Class == PPARAMOUT && - src.Op == ONAME && src.Class == PPARAM && src.Curfn == dst.Curfn -} - -func escwalk(e *EscState, level Level, dst *Node, src *Node) { - - if src.Walkgen == walkgen { - // Esclevels are vectors, do not compare as integers, - // and must use "min" of old and new to guarantee - // convergence. - level = level.min(src.Esclevel) - if level == src.Esclevel { - return - } - } - - src.Walkgen = walkgen - src.Esclevel = level - - if Debug['m'] > 1 { - 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), curfnSym(src), src.Escloopdepth) - } - - e.pdepth++ - - // Input parameter flowing to output parameter? - var leaks bool - if funcOutputAndInput(dst, src) && src.Esc&EscMask < EscScope && dst.Esc != EscHeap { - // This case handles: - // 1. return in - // 2. return &in - // 3. tmp := in; return &tmp - // 4. return *in - if Debug['m'] != 0 { - if Debug['m'] == 1 { - Warnl(int(src.Lineno), "leaking param: %v to result %v level=%v", Nconv(src, obj.FmtShort), dst.Sym, level.int()) - } else { - Warnl(int(src.Lineno), "leaking param: %v to result %v level=%v", Nconv(src, obj.FmtShort), dst.Sym, level) - } - } - if src.Esc&EscMask != EscReturn { - src.Esc = EscReturn | src.Esc&EscContentEscapes - } - src.Esc = escNoteOutputParamFlow(src.Esc, dst.Vargen, level) - goto recurse - } - - // If parameter content escapes to heap, set EscContentEscapes - // Note minor confusion around escape from pointer-to-struct vs escape from struct - if dst.Esc == EscHeap && - src.Op == ONAME && src.Class == PPARAM && src.Esc&EscMask < EscScope && - level.int() > 0 { - src.Esc = escMax(EscContentEscapes|src.Esc, EscNone) - if Debug['m'] != 0 { - Warnl(int(src.Lineno), "mark escaped content: %v", Nconv(src, obj.FmtShort)) - } - } - - leaks = level.int() <= 0 && level.guaranteedDereference() <= 0 && dst.Escloopdepth < src.Escloopdepth - - switch src.Op { - case ONAME: - if src.Class == PPARAM && (leaks || dst.Escloopdepth < 0) && src.Esc&EscMask < EscScope { - if level.guaranteedDereference() > 0 { - src.Esc = escMax(EscContentEscapes|src.Esc, EscNone) - if Debug['m'] != 0 { - if Debug['m'] == 1 { - Warnl(int(src.Lineno), "leaking param content: %v", Nconv(src, obj.FmtShort)) - } else { - Warnl(int(src.Lineno), "leaking param content: %v level=%v dst.eld=%v src.eld=%v dst=%v", - Nconv(src, obj.FmtShort), level, dst.Escloopdepth, src.Escloopdepth, Nconv(dst, obj.FmtShort)) - } - } - } else { - src.Esc = EscScope - if Debug['m'] != 0 { - if Debug['m'] == 1 { - Warnl(int(src.Lineno), "leaking param: %v", Nconv(src, obj.FmtShort)) - } else { - Warnl(int(src.Lineno), "leaking param: %v level=%v dst.eld=%v src.eld=%v dst=%v", - Nconv(src, obj.FmtShort), level, dst.Escloopdepth, src.Escloopdepth, Nconv(dst, obj.FmtShort)) - } - } - } - } - - // Treat a PPARAMREF closure variable as equivalent to the - // original variable. - if src.Class == PPARAMREF { - if leaks && Debug['m'] != 0 { - Warnl(int(src.Lineno), "leaking closure reference %v", Nconv(src, obj.FmtShort)) - } - escwalk(e, level, dst, src.Closure) - } - - case OPTRLIT, OADDR: - if leaks { - src.Esc = EscHeap - addrescapes(src.Left) - if Debug['m'] != 0 { - p := src - if p.Left.Op == OCLOSURE { - p = p.Left // merely to satisfy error messages in tests - } - if Debug['m'] > 1 { - Warnl(int(src.Lineno), "%v escapes to heap, level=%v, dst.eld=%v, src.eld=%v", - Nconv(p, obj.FmtShort), level, dst.Escloopdepth, src.Escloopdepth) - } else { - Warnl(int(src.Lineno), "%v escapes to heap", Nconv(p, obj.FmtShort)) - } - } - } - - escwalk(e, level.dec(), dst, src.Left) - - case OAPPEND: - escwalk(e, level, dst, src.List.N) - - case OARRAYLIT: - 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, - OMAKECHAN, - OMAKEMAP, - OMAKESLICE, - OARRAYRUNESTR, - OARRAYBYTESTR, - OSTRARRAYRUNE, - OSTRARRAYBYTE, - OADDSTR, - OMAPLIT, - ONEW, - OCLOSURE, - OCALLPART, - ORUNESTR, - OCONVIFACE: - if leaks { - src.Esc = EscHeap - if Debug['m'] != 0 { - Warnl(int(src.Lineno), "%v escapes to heap", Nconv(src, obj.FmtShort)) - } - } - - case ODOT, - OSLICE, - OSLICEARR, - OSLICE3, - OSLICE3ARR, - OSLICESTR: - escwalk(e, level, dst, src.Left) - - case OINDEX: - if Isfixedarray(src.Left.Type) { - escwalk(e, level, dst, src.Left) - break - } - fallthrough - - case ODOTPTR, OINDEXMAP, OIND: - escwalk(e, level.inc(), dst, src.Left) - - // In this case a link went directly to a call, but should really go - // to the dummy .outN outputs that were created for the call that - // themselves link to the inputs with levels adjusted. - // See e.g. #10466 - // This can only happen with functions returning a single result. - case OCALLMETH, OCALLFUNC, OCALLINTER: - if src.Escretval != nil { - if Debug['m'] > 1 { - fmt.Printf("%v:[%d] dst %v escwalk replace src: %v with %v\n", - Ctxt.Line(int(lineno)), e.loopdepth, - Nconv(dst, obj.FmtShort), Nconv(src, obj.FmtShort), Nconv(src.Escretval.N, obj.FmtShort)) - } - src = src.Escretval.N - } - } - -recurse: - level = level.copy() - for ll := src.Escflowsrc; ll != nil; ll = ll.Next { - escwalk(e, level, dst, ll.N) - } - - e.pdepth-- -} - -func esctag(e *EscState, func_ *Node) { - func_.Esc = EscFuncTagged - - // External functions are assumed unsafe, - // unless //go:noescape is given before the declaration. - if func_.Nbody == nil { - if func_.Noescape { - for t := getinargx(func_.Type).Type; t != nil; t = t.Down { - if haspointers(t.Type) { - t.Note = mktag(EscNone) - } - } - } - - return - } - - savefn := Curfn - Curfn = func_ - - for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { - if ll.N.Op != ONAME { - continue - } - - switch ll.N.Esc & EscMask { - case EscNone, // not touched by escflood - EscReturn: - if haspointers(ll.N.Type) { // don't bother tagging for scalars - ll.N.Paramfld.Note = mktag(int(ll.N.Esc)) - } - - case EscHeap, // touched by escflood, moved to heap - EscScope: // touched by escflood, value leaves scope - break - } - } - - Curfn = savefn -} diff --git a/src/cmd/internal/gc/export.go b/src/cmd/internal/gc/export.go deleted file mode 100644 index 614de4e2ce..0000000000 --- a/src/cmd/internal/gc/export.go +++ /dev/null @@ -1,560 +0,0 @@ -// Copyright 2009 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 gc - -import ( - "cmd/internal/obj" - "fmt" - "sort" - "unicode" - "unicode/utf8" -) - -var asmlist *NodeList - -// Mark n's symbol as exported -func exportsym(n *Node) { - if n == nil || n.Sym == nil { - return - } - if n.Sym.Flags&(SymExport|SymPackage) != 0 { - if n.Sym.Flags&SymPackage != 0 { - Yyerror("export/package mismatch: %v", n.Sym) - } - return - } - - n.Sym.Flags |= SymExport - - if Debug['E'] != 0 { - fmt.Printf("export symbol %v\n", n.Sym) - } - exportlist = list(exportlist, n) -} - -func exportname(s string) bool { - if s[0] < utf8.RuneSelf { - return 'A' <= s[0] && s[0] <= 'Z' - } - r, _ := utf8.DecodeRuneInString(s) - return unicode.IsUpper(r) -} - -func initname(s string) bool { - return s == "init" -} - -// exportedsym reports whether a symbol will be visible -// to files that import our package. -func exportedsym(sym *Sym) bool { - // Builtins are visible everywhere. - if sym.Pkg == builtinpkg || sym.Origpkg == builtinpkg { - return true - } - - return sym.Pkg == localpkg && exportname(sym.Name) -} - -func autoexport(n *Node, ctxt uint8) { - if n == nil || n.Sym == nil { - return - } - if (ctxt != PEXTERN && ctxt != PFUNC) || dclcontext != PEXTERN { - return - } - if n.Param != nil && n.Ntype != nil && n.Ntype.Op == OTFUNC && n.Ntype.Left != nil { // method - return - } - - // -A is for cmd/gc/mkbuiltin script, so export everything - if Debug['A'] != 0 || exportname(n.Sym.Name) || initname(n.Sym.Name) { - exportsym(n) - } - if asmhdr != "" && n.Sym.Pkg == localpkg && n.Sym.Flags&SymAsm == 0 { - n.Sym.Flags |= SymAsm - asmlist = list(asmlist, n) - } -} - -func dumppkg(p *Pkg) { - if p == nil || p == localpkg || p.Exported != 0 || p == builtinpkg { - return - } - p.Exported = 1 - suffix := "" - if p.Direct == 0 { - suffix = " // indirect" - } - fmt.Fprintf(bout, "\timport %s %q%s\n", p.Name, p.Path, suffix) -} - -// Look for anything we need for the inline body -func reexportdeplist(ll *NodeList) { - for ; ll != nil; ll = ll.Next { - reexportdep(ll.N) - } -} - -func reexportdep(n *Node) { - if n == nil { - return - } - - //print("reexportdep %+hN\n", n); - switch n.Op { - case ONAME: - switch n.Class &^ PHEAP { - // methods will be printed along with their type - // nodes for T.Method expressions - case PFUNC: - if n.Left != nil && n.Left.Op == OTYPE { - break - } - - // nodes for method calls. - if n.Type == nil || n.Type.Thistuple > 0 { - break - } - fallthrough - - case PEXTERN: - if n.Sym != nil && !exportedsym(n.Sym) { - if Debug['E'] != 0 { - fmt.Printf("reexport name %v\n", n.Sym) - } - exportlist = list(exportlist, n) - } - } - - // Local variables in the bodies need their type. - case ODCL: - t := n.Left.Type - - if t != Types[t.Etype] && t != idealbool && t != idealstring { - if Isptr[t.Etype] { - t = t.Type - } - if t != nil && t.Sym != nil && t.Sym.Def != nil && !exportedsym(t.Sym) { - if Debug['E'] != 0 { - fmt.Printf("reexport type %v from declaration\n", t.Sym) - } - exportlist = list(exportlist, t.Sym.Def) - } - } - - case OLITERAL: - t := n.Type - if t != Types[n.Type.Etype] && t != idealbool && t != idealstring { - if Isptr[t.Etype] { - t = t.Type - } - if t != nil && t.Sym != nil && t.Sym.Def != nil && !exportedsym(t.Sym) { - if Debug['E'] != 0 { - fmt.Printf("reexport literal type %v\n", t.Sym) - } - exportlist = list(exportlist, t.Sym.Def) - } - } - fallthrough - - case OTYPE: - if n.Sym != nil && !exportedsym(n.Sym) { - if Debug['E'] != 0 { - fmt.Printf("reexport literal/type %v\n", n.Sym) - } - exportlist = list(exportlist, n) - } - - // for operations that need a type when rendered, put the type on the export list. - case OCONV, - OCONVIFACE, - OCONVNOP, - ORUNESTR, - OARRAYBYTESTR, - OARRAYRUNESTR, - OSTRARRAYBYTE, - OSTRARRAYRUNE, - ODOTTYPE, - ODOTTYPE2, - OSTRUCTLIT, - OARRAYLIT, - OPTRLIT, - OMAKEMAP, - OMAKESLICE, - OMAKECHAN: - t := n.Type - - if t.Sym == nil && t.Type != nil { - t = t.Type - } - if t != nil && t.Sym != nil && t.Sym.Def != nil && !exportedsym(t.Sym) { - if Debug['E'] != 0 { - fmt.Printf("reexport type for expression %v\n", t.Sym) - } - exportlist = list(exportlist, t.Sym.Def) - } - } - - reexportdep(n.Left) - reexportdep(n.Right) - reexportdeplist(n.List) - reexportdeplist(n.Rlist) - reexportdeplist(n.Ninit) - reexportdep(n.Ntest) - reexportdep(n.Nincr) - reexportdeplist(n.Nbody) - reexportdeplist(n.Nelse) -} - -func dumpexportconst(s *Sym) { - n := s.Def - typecheck(&n, Erv) - if n == nil || n.Op != OLITERAL { - Fatal("dumpexportconst: oconst nil: %v", s) - } - - t := n.Type // may or may not be specified - dumpexporttype(t) - - if t != nil && !isideal(t) { - fmt.Fprintf(bout, "\tconst %v %v = %v\n", Sconv(s, obj.FmtSharp), Tconv(t, obj.FmtSharp), Vconv(&n.Val, obj.FmtSharp)) - } else { - fmt.Fprintf(bout, "\tconst %v = %v\n", Sconv(s, obj.FmtSharp), Vconv(&n.Val, obj.FmtSharp)) - } -} - -func dumpexportvar(s *Sym) { - n := s.Def - typecheck(&n, Erv|Ecall) - if n == nil || n.Type == nil { - Yyerror("variable exported but not defined: %v", s) - return - } - - t := n.Type - dumpexporttype(t) - - if t.Etype == TFUNC && n.Class == PFUNC { - if n.Func != nil && n.Func.Inl != nil { - // when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet. - // currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package - if Debug['l'] < 2 { - typecheckinl(n) - } - - // NOTE: The space after %#S here is necessary for ld's export data parser. - fmt.Fprintf(bout, "\tfunc %v %v { %v }\n", Sconv(s, obj.FmtSharp), Tconv(t, obj.FmtShort|obj.FmtSharp), Hconv(n.Func.Inl, obj.FmtSharp)) - - reexportdeplist(n.Func.Inl) - } else { - fmt.Fprintf(bout, "\tfunc %v %v\n", Sconv(s, obj.FmtSharp), Tconv(t, obj.FmtShort|obj.FmtSharp)) - } - } else { - fmt.Fprintf(bout, "\tvar %v %v\n", Sconv(s, obj.FmtSharp), Tconv(t, obj.FmtSharp)) - } -} - -type methodbyname []*Type - -func (x methodbyname) Len() int { - return len(x) -} - -func (x methodbyname) Swap(i, j int) { - x[i], x[j] = x[j], x[i] -} - -func (x methodbyname) Less(i, j int) bool { - a := x[i] - b := x[j] - return stringsCompare(a.Sym.Name, b.Sym.Name) < 0 -} - -func dumpexporttype(t *Type) { - if t == nil { - return - } - if t.Printed != 0 || t == Types[t.Etype] || t == bytetype || t == runetype || t == errortype { - return - } - t.Printed = 1 - - if t.Sym != nil && t.Etype != TFIELD { - dumppkg(t.Sym.Pkg) - } - - dumpexporttype(t.Type) - dumpexporttype(t.Down) - - if t.Sym == nil || t.Etype == TFIELD { - return - } - - n := 0 - for f := t.Method; f != nil; f = f.Down { - dumpexporttype(f) - n++ - } - - m := make([]*Type, n) - i := 0 - for f := t.Method; f != nil; f = f.Down { - m[i] = f - i++ - } - sort.Sort(methodbyname(m[:n])) - - fmt.Fprintf(bout, "\ttype %v %v\n", Sconv(t.Sym, obj.FmtSharp), Tconv(t, obj.FmtSharp|obj.FmtLong)) - var f *Type - for i := 0; i < n; i++ { - f = m[i] - if f.Nointerface { - fmt.Fprintf(bout, "\t//go:nointerface\n") - } - if f.Type.Nname != nil && f.Type.Nname.Func.Inl != nil { // nname was set by caninl - - // when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet. - // currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package - if Debug['l'] < 2 { - typecheckinl(f.Type.Nname) - } - fmt.Fprintf(bout, "\tfunc (%v) %v %v { %v }\n", Tconv(getthisx(f.Type).Type, obj.FmtSharp), Sconv(f.Sym, obj.FmtShort|obj.FmtByte|obj.FmtSharp), Tconv(f.Type, obj.FmtShort|obj.FmtSharp), Hconv(f.Type.Nname.Func.Inl, obj.FmtSharp)) - reexportdeplist(f.Type.Nname.Func.Inl) - } else { - fmt.Fprintf(bout, "\tfunc (%v) %v %v\n", Tconv(getthisx(f.Type).Type, obj.FmtSharp), Sconv(f.Sym, obj.FmtShort|obj.FmtByte|obj.FmtSharp), Tconv(f.Type, obj.FmtShort|obj.FmtSharp)) - } - } -} - -func dumpsym(s *Sym) { - if s.Flags&SymExported != 0 { - return - } - s.Flags |= SymExported - - if s.Def == nil { - Yyerror("unknown export symbol: %v", s) - return - } - - // print("dumpsym %O %+S\n", s->def->op, s); - dumppkg(s.Pkg) - - switch s.Def.Op { - default: - Yyerror("unexpected export symbol: %v %v", Oconv(int(s.Def.Op), 0), s) - - case OLITERAL: - dumpexportconst(s) - - case OTYPE: - if s.Def.Type.Etype == TFORW { - Yyerror("export of incomplete type %v", s) - } else { - dumpexporttype(s.Def.Type) - } - - case ONAME: - dumpexportvar(s) - } -} - -func dumpexport() { - lno := lineno - - fmt.Fprintf(bout, "\n$$\npackage %s", localpkg.Name) - if safemode != 0 { - fmt.Fprintf(bout, " safe") - } - fmt.Fprintf(bout, "\n") - - for _, p := range pkgs { - if p.Direct != 0 { - dumppkg(p) - } - } - - for l := exportlist; l != nil; l = l.Next { - lineno = l.N.Lineno - dumpsym(l.N.Sym) - } - - fmt.Fprintf(bout, "\n$$\n") - lineno = lno -} - -/* - * import - */ - -/* - * return the sym for ss, which should match lexical - */ -func importsym(s *Sym, op int) *Sym { - if s.Def != nil && int(s.Def.Op) != op { - pkgstr := fmt.Sprintf("during import %q", importpkg.Path) - redeclare(s, pkgstr) - } - - // mark the symbol so it is not reexported - if s.Def == nil { - if exportname(s.Name) || initname(s.Name) { - s.Flags |= SymExport - } else { - s.Flags |= SymPackage // package scope - } - } - - return s -} - -/* - * return the type pkg.name, forward declaring if needed - */ -func pkgtype(s *Sym) *Type { - importsym(s, OTYPE) - if s.Def == nil || s.Def.Op != OTYPE { - t := typ(TFORW) - t.Sym = s - s.Def = typenod(t) - } - - if s.Def.Type == nil { - Yyerror("pkgtype %v", s) - } - return s.Def.Type -} - -var numImport = make(map[string]int) - -func importimport(s *Sym, path string) { - // Informational: record package name - // associated with import path, for use in - // human-readable messages. - - if isbadimport(path) { - errorexit() - } - p := mkpkg(path) - if p.Name == "" { - p.Name = s.Name - numImport[s.Name]++ - } else if p.Name != s.Name { - Yyerror("conflicting names %s and %s for package %q", p.Name, s.Name, p.Path) - } - - if incannedimport == 0 && myimportpath != "" && path == myimportpath { - Yyerror("import %q: package depends on %q (import cycle)", importpkg.Path, path) - errorexit() - } -} - -func importconst(s *Sym, t *Type, n *Node) { - importsym(s, OLITERAL) - Convlit(&n, t) - - if s.Def != nil { // TODO: check if already the same. - return - } - - if n.Op != OLITERAL { - Yyerror("expression must be a constant") - return - } - - if n.Sym != nil { - n1 := Nod(OXXX, nil, nil) - *n1 = *n - n = n1 - } - - n.Orig = newname(s) - n.Sym = s - declare(n, PEXTERN) - - if Debug['E'] != 0 { - fmt.Printf("import const %v\n", s) - } -} - -func importvar(s *Sym, t *Type) { - importsym(s, ONAME) - if s.Def != nil && s.Def.Op == ONAME { - if Eqtype(t, s.Def.Type) { - return - } - Yyerror("inconsistent definition for var %v during import\n\t%v (in %q)\n\t%v (in %q)", s, s.Def.Type, s.Importdef.Path, t, importpkg.Path) - } - - n := newname(s) - s.Importdef = importpkg - n.Type = t - declare(n, PEXTERN) - - if Debug['E'] != 0 { - fmt.Printf("import var %v %v\n", s, Tconv(t, obj.FmtLong)) - } -} - -func importtype(pt *Type, t *Type) { - // override declaration in unsafe.go for Pointer. - // there is no way in Go code to define unsafe.Pointer - // so we have to supply it. - if incannedimport != 0 && importpkg.Name == "unsafe" && pt.Nod.Sym.Name == "Pointer" { - t = Types[TUNSAFEPTR] - } - - if pt.Etype == TFORW { - n := pt.Nod - copytype(pt.Nod, t) - pt.Nod = n // unzero nod - pt.Sym.Importdef = importpkg - pt.Sym.Lastlineno = int32(parserline()) - declare(n, PEXTERN) - checkwidth(pt) - } else if !Eqtype(pt.Orig, t) { - Yyerror("inconsistent definition for type %v during import\n\t%v (in %q)\n\t%v (in %q)", pt.Sym, Tconv(pt, obj.FmtLong), pt.Sym.Importdef.Path, Tconv(t, obj.FmtLong), importpkg.Path) - } - - if Debug['E'] != 0 { - fmt.Printf("import type %v %v\n", pt, Tconv(t, obj.FmtLong)) - } -} - -func dumpasmhdr() { - var b *obj.Biobuf - - b, err := obj.Bopenw(asmhdr) - if err != nil { - Fatal("%v", err) - } - fmt.Fprintf(b, "// generated by %cg -asmhdr from package %s\n\n", Thearch.Thechar, localpkg.Name) - var n *Node - var t *Type - for l := asmlist; l != nil; l = l.Next { - n = l.N - if isblanksym(n.Sym) { - continue - } - switch n.Op { - case OLITERAL: - fmt.Fprintf(b, "#define const_%s %v\n", n.Sym.Name, Vconv(&n.Val, obj.FmtSharp)) - - case OTYPE: - t = n.Type - if t.Etype != TSTRUCT || t.Map != nil || t.Funarg != 0 { - break - } - fmt.Fprintf(b, "#define %s__size %d\n", t.Sym.Name, int(t.Width)) - for t = t.Type; t != nil; t = t.Down { - if !isblanksym(t.Sym) { - fmt.Fprintf(b, "#define %s_%s %d\n", n.Sym.Name, t.Sym.Name, int(t.Width)) - } - } - } - } - - obj.Bterm(b) -} diff --git a/src/cmd/internal/gc/fmt.go b/src/cmd/internal/gc/fmt.go deleted file mode 100644 index 9d8482bf76..0000000000 --- a/src/cmd/internal/gc/fmt.go +++ /dev/null @@ -1,1736 +0,0 @@ -// Copyright 2011 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 gc - -import ( - "bytes" - "cmd/internal/obj" - "fmt" - "strconv" - "strings" - "unicode/utf8" -) - -// -// Format conversions -// %L int Line numbers -// -// %E int etype values (aka 'Kind') -// -// %O int Node Opcodes -// Flags: "%#O": print go syntax. (automatic unless fmtmode == FDbg) -// -// %J Node* Node details -// Flags: "%hJ" suppresses things not relevant until walk. -// -// %V Val* Constant values -// -// %S Sym* Symbols -// Flags: +,- #: mode (see below) -// "%hS" unqualified identifier in any mode -// "%hhS" in export mode: unqualified identifier if exported, qualified if not -// -// %T Type* Types -// Flags: +,- #: mode (see below) -// 'l' definition instead of name. -// 'h' omit "func" and receiver in function types -// 'u' (only in -/Sym mode) print type identifiers wit package name instead of prefix. -// -// %N Node* Nodes -// Flags: +,- #: mode (see below) -// 'h' (only in +/debug mode) suppress recursion -// 'l' (only in Error mode) print "foo (type Bar)" -// -// %H NodeList* NodeLists -// Flags: those of %N -// ',' separate items with ',' instead of ';' -// -// In mparith1.c: -// %B Mpint* Big integers -// %F Mpflt* Big floats -// -// %S, %T and %N obey use the following flags to set the format mode: -const ( - FErr = iota - FDbg - FExp - FTypeId -) - -var fmtmode int = FErr - -var fmtpkgpfx int // %uT stickyness - -// -// E.g. for %S: %+S %#S %-S print an identifier properly qualified for debug/export/internal mode. -// -// The mode flags +, - and # are sticky, meaning they persist through -// recursions of %N, %T and %S, but not the h and l flags. The u flag is -// sticky only on %T recursions and only used in %-/Sym mode. - -// -// Useful format combinations: -// -// %+N %+H multiline recursive debug dump of node/nodelist -// %+hN %+hH non recursive debug dump -// -// %#N %#T export format -// %#lT type definition instead of name -// %#hT omit"func" and receiver in function signature -// -// %lN "foo (type Bar)" for error messages -// -// %-T type identifiers -// %-hT type identifiers without "func" and arg names in type signatures (methodsym) -// %-uT type identifiers with package name instead of prefix (typesym, dcommontype, typehash) -// - -func setfmode(flags *int) int { - fm := fmtmode - if *flags&obj.FmtSign != 0 { - fmtmode = FDbg - } else if *flags&obj.FmtSharp != 0 { - fmtmode = FExp - } else if *flags&obj.FmtLeft != 0 { - fmtmode = FTypeId - } - - *flags &^= (obj.FmtSharp | obj.FmtLeft | obj.FmtSign) - return fm -} - -// Fmt "%L": Linenumbers - -var goopnames = []string{ - OADDR: "&", - OADD: "+", - OADDSTR: "+", - OANDAND: "&&", - OANDNOT: "&^", - OAND: "&", - OAPPEND: "append", - OAS: "=", - OAS2: "=", - OBREAK: "break", - OCALL: "function call", // not actual syntax - OCAP: "cap", - OCASE: "case", - OCLOSE: "close", - OCOMPLEX: "complex", - OCOM: "^", - OCONTINUE: "continue", - OCOPY: "copy", - ODEC: "--", - ODELETE: "delete", - ODEFER: "defer", - ODIV: "/", - OEQ: "==", - OFALL: "fallthrough", - OFOR: "for", - OGE: ">=", - OGOTO: "goto", - OGT: ">", - OIF: "if", - OIMAG: "imag", - OINC: "++", - OIND: "*", - OLEN: "len", - OLE: "<=", - OLSH: "<<", - OLT: "<", - OMAKE: "make", - OMINUS: "-", - OMOD: "%", - OMUL: "*", - ONEW: "new", - ONE: "!=", - ONOT: "!", - OOROR: "||", - OOR: "|", - OPANIC: "panic", - OPLUS: "+", - OPRINTN: "println", - OPRINT: "print", - ORANGE: "range", - OREAL: "real", - ORECV: "<-", - ORECOVER: "recover", - ORETURN: "return", - ORSH: ">>", - OSELECT: "select", - OSEND: "<-", - OSUB: "-", - OSWITCH: "switch", - OXOR: "^", -} - -// Fmt "%O": Node opcodes -func Oconv(o int, flag int) string { - if (flag&obj.FmtSharp != 0) || fmtmode != FDbg { - if o >= 0 && o < len(goopnames) && goopnames[o] != "" { - return goopnames[o] - } - } - - if o >= 0 && o < len(opnames) && opnames[o] != "" { - return opnames[o] - } - - return fmt.Sprintf("O-%d", o) -} - -var classnames = []string{ - "Pxxx", - "PEXTERN", - "PAUTO", - "PPARAM", - "PPARAMOUT", - "PPARAMREF", - "PFUNC", -} - -// Fmt "%J": Node details. -func Jconv(n *Node, flag int) string { - var buf bytes.Buffer - - c := flag & obj.FmtShort - - if c == 0 && n.Ullman != 0 { - fmt.Fprintf(&buf, " u(%d)", n.Ullman) - } - - if c == 0 && n.Addable { - fmt.Fprintf(&buf, " a(%v)", n.Addable) - } - - if c == 0 && n.Vargen != 0 { - fmt.Fprintf(&buf, " g(%d)", n.Vargen) - } - - if n.Lineno != 0 { - fmt.Fprintf(&buf, " l(%d)", n.Lineno) - } - - if c == 0 && n.Xoffset != BADWIDTH { - fmt.Fprintf(&buf, " x(%d%+d)", n.Xoffset, n.Stkdelta) - } - - if n.Class != 0 { - s := "" - if n.Class&PHEAP != 0 { - s = ",heap" - } - if int(n.Class&^PHEAP) < len(classnames) { - fmt.Fprintf(&buf, " class(%s%s)", classnames[n.Class&^PHEAP], s) - } else { - fmt.Fprintf(&buf, " class(%d?%s)", n.Class&^PHEAP, s) - } - } - - if n.Colas { - fmt.Fprintf(&buf, " colas(%v)", n.Colas) - } - - if n.Funcdepth != 0 { - fmt.Fprintf(&buf, " f(%d)", n.Funcdepth) - } - - switch n.Esc { - case EscUnknown: - break - - case EscHeap: - buf.WriteString(" esc(h)") - - case EscScope: - buf.WriteString(" esc(s)") - - case EscNone: - buf.WriteString(" esc(no)") - - case EscNever: - if c == 0 { - buf.WriteString(" esc(N)") - } - - default: - fmt.Fprintf(&buf, " esc(%d)", n.Esc) - } - - if n.Escloopdepth != 0 { - fmt.Fprintf(&buf, " ld(%d)", n.Escloopdepth) - } - - if c == 0 && n.Typecheck != 0 { - fmt.Fprintf(&buf, " tc(%d)", n.Typecheck) - } - - if c == 0 && n.Dodata != 0 { - fmt.Fprintf(&buf, " dd(%d)", n.Dodata) - } - - if n.Isddd { - fmt.Fprintf(&buf, " isddd(%v)", n.Isddd) - } - - if n.Implicit { - fmt.Fprintf(&buf, " implicit(%v)", n.Implicit) - } - - if n.Embedded != 0 { - fmt.Fprintf(&buf, " embedded(%d)", n.Embedded) - } - - if n.Addrtaken { - buf.WriteString(" addrtaken") - } - - if n.Assigned { - buf.WriteString(" assigned") - } - - if c == 0 && n.Used { - fmt.Fprintf(&buf, " used(%v)", n.Used) - } - return buf.String() -} - -// Fmt "%V": Values -func Vconv(v *Val, flag int) string { - switch v.Ctype { - case CTINT: - if (flag&obj.FmtSharp != 0) || fmtmode == FExp { - return Bconv(v.U.(*Mpint), obj.FmtSharp) - } - return Bconv(v.U.(*Mpint), 0) - - case CTRUNE: - x := Mpgetfix(v.U.(*Mpint)) - if ' ' <= x && x < 0x80 && x != '\\' && x != '\'' { - return fmt.Sprintf("'%c'", int(x)) - } - if 0 <= x && x < 1<<16 { - return fmt.Sprintf("'\\u%04x'", uint(int(x))) - } - if 0 <= x && x <= utf8.MaxRune { - return fmt.Sprintf("'\\U%08x'", uint64(x)) - } - return fmt.Sprintf("('\\x00' + %v)", v.U.(*Mpint)) - - case CTFLT: - if (flag&obj.FmtSharp != 0) || fmtmode == FExp { - return Fconv(v.U.(*Mpflt), 0) - } - return Fconv(v.U.(*Mpflt), obj.FmtSharp) - - case CTCPLX: - if (flag&obj.FmtSharp != 0) || fmtmode == FExp { - return fmt.Sprintf("(%v+%vi)", &v.U.(*Mpcplx).Real, &v.U.(*Mpcplx).Imag) - } - if mpcmpfltc(&v.U.(*Mpcplx).Real, 0) == 0 { - return fmt.Sprintf("%vi", Fconv(&v.U.(*Mpcplx).Imag, obj.FmtSharp)) - } - if mpcmpfltc(&v.U.(*Mpcplx).Imag, 0) == 0 { - return Fconv(&v.U.(*Mpcplx).Real, 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.(*Mpcplx).Real, obj.FmtSharp), Fconv(&v.U.(*Mpcplx).Imag, obj.FmtSharp)) - - case CTSTR: - return strconv.Quote(v.U.(string)) - - case CTBOOL: - if v.U.(bool) { - return "true" - } - return "false" - - case CTNIL: - return "nil" - } - - return fmt.Sprintf("", v.Ctype) -} - -/* -s%,%,\n%g -s%\n+%\n%g -s%^[ ]*T%%g -s%,.*%%g -s%.+% [T&] = "&",%g -s%^ ........*\]%&~%g -s%~ %%g -*/ -var etnames = []string{ - TINT: "INT", - TUINT: "UINT", - TINT8: "INT8", - TUINT8: "UINT8", - TINT16: "INT16", - TUINT16: "UINT16", - TINT32: "INT32", - TUINT32: "UINT32", - TINT64: "INT64", - TUINT64: "UINT64", - TUINTPTR: "UINTPTR", - TFLOAT32: "FLOAT32", - TFLOAT64: "FLOAT64", - TCOMPLEX64: "COMPLEX64", - TCOMPLEX128: "COMPLEX128", - TBOOL: "BOOL", - TPTR32: "PTR32", - TPTR64: "PTR64", - TFUNC: "FUNC", - TARRAY: "ARRAY", - TSTRUCT: "STRUCT", - TCHAN: "CHAN", - TMAP: "MAP", - TINTER: "INTER", - TFORW: "FORW", - TFIELD: "FIELD", - TSTRING: "STRING", - TANY: "ANY", -} - -// Fmt "%E": etype -func Econv(et int, flag int) string { - if et >= 0 && et < len(etnames) && etnames[et] != "" { - return etnames[et] - } - return fmt.Sprintf("E-%d", et) -} - -// Fmt "%S": syms -func symfmt(s *Sym, flag int) string { - if s.Pkg != nil && flag&obj.FmtShort == 0 { - switch fmtmode { - case FErr: // This is for the user - if s.Pkg == localpkg { - return s.Name - } - - // If the name was used by multiple packages, display the full path, - if s.Pkg.Name != "" && numImport[s.Pkg.Name] > 1 { - return fmt.Sprintf("%q.%s", s.Pkg.Path, s.Name) - } - return fmt.Sprintf("%s.%s", s.Pkg.Name, s.Name) - - case FDbg: - return fmt.Sprintf("%s.%s", s.Pkg.Name, s.Name) - - case FTypeId: - if flag&obj.FmtUnsigned != 0 { - return fmt.Sprintf("%s.%s", s.Pkg.Name, s.Name) // dcommontype, typehash - } - return fmt.Sprintf("%s.%s", s.Pkg.Prefix, s.Name) // (methodsym), typesym, weaksym - - case FExp: - if s.Name != "" && s.Name[0] == '.' { - Fatal("exporting synthetic symbol %s", s.Name) - } - if s.Pkg != builtinpkg { - return fmt.Sprintf("@%q.%s", s.Pkg.Path, s.Name) - } - } - } - - if flag&obj.FmtByte != 0 { - // FmtByte (hh) implies FmtShort (h) - // skip leading "type." in method name - p := s.Name - if i := strings.LastIndex(s.Name, "."); i >= 0 { - p = s.Name[i+1:] - } - - // exportname needs to see the name without the prefix too. - if (fmtmode == FExp && !exportname(p)) || fmtmode == FDbg { - return fmt.Sprintf("@%q.%s", s.Pkg.Path, p) - } - - return p - } - - return s.Name -} - -var basicnames = []string{ - TINT: "int", - TUINT: "uint", - TINT8: "int8", - TUINT8: "uint8", - TINT16: "int16", - TUINT16: "uint16", - TINT32: "int32", - TUINT32: "uint32", - TINT64: "int64", - TUINT64: "uint64", - TUINTPTR: "uintptr", - TFLOAT32: "float32", - TFLOAT64: "float64", - TCOMPLEX64: "complex64", - TCOMPLEX128: "complex128", - TBOOL: "bool", - TANY: "any", - TSTRING: "string", - TNIL: "nil", - TIDEAL: "untyped number", - TBLANK: "blank", -} - -func typefmt(t *Type, flag int) string { - if t == nil { - return "" - } - - if t == bytetype || t == runetype { - // in %-T mode collapse rune and byte with their originals. - if fmtmode != FTypeId { - return Sconv(t.Sym, obj.FmtShort) - } - t = Types[t.Etype] - } - - if t == errortype { - return "error" - } - - // Unless the 'l' flag was specified, if the type has a name, just print that name. - if flag&obj.FmtLong == 0 && t.Sym != nil && t.Etype != TFIELD && t != Types[t.Etype] { - switch fmtmode { - case FTypeId: - if flag&obj.FmtShort != 0 { - if t.Vargen != 0 { - return fmt.Sprintf("%v·%d", Sconv(t.Sym, obj.FmtShort), t.Vargen) - } - return Sconv(t.Sym, obj.FmtShort) - } - - if flag&obj.FmtUnsigned != 0 { - return Sconv(t.Sym, obj.FmtUnsigned) - } - fallthrough - - case FExp: - if t.Sym.Pkg == localpkg && t.Vargen != 0 { - return fmt.Sprintf("%v·%d", t.Sym, t.Vargen) - } - } - - return Sconv(t.Sym, 0) - } - - if int(t.Etype) < len(basicnames) && basicnames[t.Etype] != "" { - prefix := "" - if fmtmode == FErr && (t == idealbool || t == idealstring) { - prefix = "untyped " - } - return prefix + basicnames[t.Etype] - } - - if fmtmode == FDbg { - fmtmode = 0 - str := Econv(int(t.Etype), 0) + "-" + typefmt(t, flag) - fmtmode = FDbg - return str - } - - switch t.Etype { - case TPTR32, TPTR64: - if fmtmode == FTypeId && (flag&obj.FmtShort != 0) { - return fmt.Sprintf("*%v", Tconv(t.Type, obj.FmtShort)) - } - return fmt.Sprintf("*%v", t.Type) - - case TARRAY: - if t.Bound >= 0 { - return fmt.Sprintf("[%d]%v", t.Bound, t.Type) - } - if t.Bound == -100 { - return fmt.Sprintf("[...]%v", t.Type) - } - return fmt.Sprintf("[]%v", t.Type) - - case TCHAN: - switch t.Chan { - case Crecv: - return fmt.Sprintf("<-chan %v", t.Type) - - case Csend: - return fmt.Sprintf("chan<- %v", t.Type) - } - - if t.Type != nil && t.Type.Etype == TCHAN && t.Type.Sym == nil && t.Type.Chan == Crecv { - return fmt.Sprintf("chan (%v)", t.Type) - } - return fmt.Sprintf("chan %v", t.Type) - - case TMAP: - return fmt.Sprintf("map[%v]%v", t.Down, t.Type) - - case TINTER: - var buf bytes.Buffer - buf.WriteString("interface {") - for t1 := t.Type; t1 != nil; t1 = t1.Down { - buf.WriteString(" ") - if exportname(t1.Sym.Name) { - buf.WriteString(Sconv(t1.Sym, obj.FmtShort)) - } else { - buf.WriteString(Sconv(t1.Sym, obj.FmtUnsigned)) - } - buf.WriteString(Tconv(t1.Type, obj.FmtShort)) - if t1.Down != nil { - buf.WriteString(";") - } - } - if t.Type != nil { - buf.WriteString(" ") - } - buf.WriteString("}") - return buf.String() - - case TFUNC: - var buf bytes.Buffer - if flag&obj.FmtShort != 0 { - // no leading func - } else { - if t.Thistuple != 0 { - buf.WriteString("method") - buf.WriteString(Tconv(getthisx(t), 0)) - buf.WriteString(" ") - } - buf.WriteString("func") - } - buf.WriteString(Tconv(getinargx(t), 0)) - - switch t.Outtuple { - case 0: - break - - case 1: - if fmtmode != FExp { - buf.WriteString(" ") - buf.WriteString(Tconv(getoutargx(t).Type.Type, 0)) // struct->field->field's type - break - } - fallthrough - - default: - buf.WriteString(" ") - buf.WriteString(Tconv(getoutargx(t), 0)) - } - return buf.String() - - case TSTRUCT: - if t.Map != nil { - // Format the bucket struct for map[x]y as map.bucket[x]y. - // This avoids a recursive print that generates very long names. - if t.Map.Bucket == t { - return fmt.Sprintf("map.bucket[%v]%v", t.Map.Down, t.Map.Type) - } - - if t.Map.Hmap == t { - return fmt.Sprintf("map.hdr[%v]%v", t.Map.Down, t.Map.Type) - } - - if t.Map.Hiter == t { - return fmt.Sprintf("map.iter[%v]%v", t.Map.Down, t.Map.Type) - } - - Yyerror("unknown internal map type") - } - - var buf bytes.Buffer - if t.Funarg != 0 { - buf.WriteString("(") - if fmtmode == FTypeId || fmtmode == FErr { // no argument names on function signature, and no "noescape"/"nosplit" tags - for t1 := t.Type; t1 != nil; t1 = t1.Down { - buf.WriteString(Tconv(t1, obj.FmtShort)) - if t1.Down != nil { - buf.WriteString(", ") - } - } - } else { - for t1 := t.Type; t1 != nil; t1 = t1.Down { - buf.WriteString(Tconv(t1, 0)) - if t1.Down != nil { - buf.WriteString(", ") - } - } - } - buf.WriteString(")") - } else { - buf.WriteString("struct {") - for t1 := t.Type; t1 != nil; t1 = t1.Down { - buf.WriteString(" ") - buf.WriteString(Tconv(t1, obj.FmtLong)) - if t1.Down != nil { - buf.WriteString(";") - } - } - if t.Type != nil { - buf.WriteString(" ") - } - buf.WriteString("}") - } - return buf.String() - - case TFIELD: - var name string - if flag&obj.FmtShort == 0 { - s := t.Sym - - // Take the name from the original, lest we substituted it with ~r%d or ~b%d. - // ~r%d is a (formerly) unnamed result. - if (fmtmode == FErr || fmtmode == FExp) && t.Nname != nil { - if t.Nname.Orig != nil { - s = t.Nname.Orig.Sym - if s != nil && s.Name[0] == '~' { - if s.Name[1] == 'r' { // originally an unnamed result - s = nil - } else if s.Name[1] == 'b' { // originally the blank identifier _ - s = Lookup("_") - } - } - } else { - s = nil - } - } - - if s != nil && t.Embedded == 0 { - if t.Funarg != 0 { - name = Nconv(t.Nname, 0) - } else if flag&obj.FmtLong != 0 { - name = Sconv(s, obj.FmtShort|obj.FmtByte) // qualify non-exported names (used on structs, not on funarg) - } else { - name = Sconv(s, 0) - } - } else if fmtmode == FExp { - // TODO(rsc) this breaks on the eliding of unused arguments in the backend - // when this is fixed, the special case in dcl.c checkarglist can go. - //if(t->funarg) - // fmtstrcpy(fp, "_ "); - //else - if t.Embedded != 0 && s.Pkg != nil && len(s.Pkg.Path) > 0 { - name = fmt.Sprintf("@%q.?", s.Pkg.Path) - } else { - name = "?" - } - } - } - - var typ string - if t.Isddd { - typ = "..." + Tconv(t.Type.Type, 0) - } else { - typ = Tconv(t.Type, 0) - } - - str := typ - if name != "" { - str = name + " " + typ - } - if flag&obj.FmtShort == 0 && t.Note != nil { - str += " " + strconv.Quote(*t.Note) - } - return str - - case TFORW: - if t.Sym != nil { - return fmt.Sprintf("undefined %v", t.Sym) - } - return "undefined" - - case TUNSAFEPTR: - if fmtmode == FExp { - return "@\"unsafe\".Pointer" - } - return "unsafe.Pointer" - } - - if fmtmode == FExp { - Fatal("missing %v case during export", Econv(int(t.Etype), 0)) - } - - // Don't know how to handle - fall back to detailed prints. - return fmt.Sprintf("%v <%v> %v", Econv(int(t.Etype), 0), t.Sym, t.Type) -} - -// Statements which may be rendered with a simplestmt as init. -func stmtwithinit(op int) bool { - switch op { - case OIF, OFOR, OSWITCH: - return true - } - - return false -} - -func stmtfmt(n *Node) string { - var f string - - // some statements allow for an init, but at most one, - // but we may have an arbitrary number added, eg by typecheck - // and inlining. If it doesn't fit the syntax, emit an enclosing - // block starting with the init statements. - - // if we can just say "for" n->ninit; ... then do so - simpleinit := n.Ninit != nil && n.Ninit.Next == nil && n.Ninit.N.Ninit == nil && stmtwithinit(int(n.Op)) - - // otherwise, print the inits as separate statements - complexinit := n.Ninit != nil && !simpleinit && (fmtmode != FErr) - - // but if it was for if/for/switch, put in an extra surrounding block to limit the scope - extrablock := complexinit && stmtwithinit(int(n.Op)) - - if extrablock { - f += "{" - } - - if complexinit { - f += fmt.Sprintf(" %v; ", n.Ninit) - } - - switch n.Op { - case ODCL: - if fmtmode == FExp { - switch n.Left.Class &^ PHEAP { - case PPARAM, PPARAMOUT, PAUTO: - f += fmt.Sprintf("var %v %v", n.Left, n.Left.Type) - goto ret - } - } - - f += fmt.Sprintf("var %v %v", n.Left.Sym, n.Left.Type) - - case ODCLFIELD: - if n.Left != nil { - f += fmt.Sprintf("%v %v", n.Left, n.Right) - } else { - f += Nconv(n.Right, 0) - } - - // Don't export "v = " initializing statements, hope they're always - // preceded by the DCL which will be re-parsed and typecheck to reproduce - // the "v = " again. - case OAS, OASWB: - if fmtmode == FExp && n.Right == nil { - break - } - - if n.Colas && !complexinit { - f += fmt.Sprintf("%v := %v", n.Left, n.Right) - } else { - f += fmt.Sprintf("%v = %v", n.Left, n.Right) - } - - case OASOP: - if n.Implicit { - if n.Etype == OADD { - f += fmt.Sprintf("%v++", n.Left) - } else { - f += fmt.Sprintf("%v--", n.Left) - } - break - } - - f += fmt.Sprintf("%v %v= %v", n.Left, Oconv(int(n.Etype), obj.FmtSharp), n.Right) - - case OAS2: - if n.Colas && !complexinit { - f += fmt.Sprintf("%v := %v", Hconv(n.List, obj.FmtComma), Hconv(n.Rlist, obj.FmtComma)) - break - } - fallthrough - - case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV: - f += fmt.Sprintf("%v = %v", Hconv(n.List, obj.FmtComma), Hconv(n.Rlist, obj.FmtComma)) - - case ORETURN: - f += fmt.Sprintf("return %v", Hconv(n.List, obj.FmtComma)) - - case ORETJMP: - f += fmt.Sprintf("retjmp %v", n.Sym) - - case OPROC: - f += fmt.Sprintf("go %v", n.Left) - - case ODEFER: - f += fmt.Sprintf("defer %v", n.Left) - - case OIF: - if simpleinit { - f += fmt.Sprintf("if %v; %v { %v }", n.Ninit.N, n.Ntest, n.Nbody) - } else { - f += fmt.Sprintf("if %v { %v }", n.Ntest, n.Nbody) - } - if n.Nelse != nil { - f += fmt.Sprintf(" else { %v }", n.Nelse) - } - - case OFOR: - if fmtmode == FErr { // TODO maybe only if FmtShort, same below - f += "for loop" - break - } - - f += "for" - if simpleinit { - f += fmt.Sprintf(" %v;", n.Ninit.N) - } else if n.Nincr != nil { - f += " ;" - } - - if n.Ntest != nil { - f += fmt.Sprintf(" %v", n.Ntest) - } - - if n.Nincr != nil { - f += fmt.Sprintf("; %v", n.Nincr) - } else if simpleinit { - f += ";" - } - - f += fmt.Sprintf(" { %v }", n.Nbody) - - case ORANGE: - if fmtmode == FErr { - f += "for loop" - break - } - - if n.List == nil { - f += fmt.Sprintf("for range %v { %v }", n.Right, n.Nbody) - break - } - - f += fmt.Sprintf("for %v = range %v { %v }", Hconv(n.List, obj.FmtComma), n.Right, n.Nbody) - - case OSELECT, OSWITCH: - if fmtmode == FErr { - f += fmt.Sprintf("%v statement", Oconv(int(n.Op), 0)) - break - } - - f += Oconv(int(n.Op), obj.FmtSharp) - if simpleinit { - f += fmt.Sprintf(" %v;", n.Ninit.N) - } - if n.Ntest != nil { - f += Nconv(n.Ntest, 0) - } - - f += fmt.Sprintf(" { %v }", n.List) - - case OCASE, OXCASE: - if n.List != nil { - f += fmt.Sprintf("case %v: %v", Hconv(n.List, obj.FmtComma), n.Nbody) - } else { - f += fmt.Sprintf("default: %v", n.Nbody) - } - - case OBREAK, - OCONTINUE, - OGOTO, - OFALL, - OXFALL: - if n.Left != nil { - f += fmt.Sprintf("%v %v", Oconv(int(n.Op), obj.FmtSharp), n.Left) - } else { - f += Oconv(int(n.Op), obj.FmtSharp) - } - - case OEMPTY: - break - - case OLABEL: - f += fmt.Sprintf("%v: ", n.Left) - } - -ret: - if extrablock { - f += "}" - } - - return f -} - -var opprec = []int{ - OAPPEND: 8, - OARRAYBYTESTR: 8, - OARRAYLIT: 8, - OARRAYRUNESTR: 8, - OCALLFUNC: 8, - OCALLINTER: 8, - OCALLMETH: 8, - OCALL: 8, - OCAP: 8, - OCLOSE: 8, - OCONVIFACE: 8, - OCONVNOP: 8, - OCONV: 8, - OCOPY: 8, - ODELETE: 8, - OGETG: 8, - OLEN: 8, - OLITERAL: 8, - OMAKESLICE: 8, - OMAKE: 8, - OMAPLIT: 8, - ONAME: 8, - ONEW: 8, - ONONAME: 8, - OPACK: 8, - OPANIC: 8, - OPAREN: 8, - OPRINTN: 8, - OPRINT: 8, - ORUNESTR: 8, - OSTRARRAYBYTE: 8, - OSTRARRAYRUNE: 8, - OSTRUCTLIT: 8, - OTARRAY: 8, - OTCHAN: 8, - OTFUNC: 8, - OTINTER: 8, - OTMAP: 8, - OTSTRUCT: 8, - OINDEXMAP: 8, - OINDEX: 8, - OSLICE: 8, - OSLICESTR: 8, - OSLICEARR: 8, - OSLICE3: 8, - OSLICE3ARR: 8, - ODOTINTER: 8, - ODOTMETH: 8, - ODOTPTR: 8, - ODOTTYPE2: 8, - ODOTTYPE: 8, - ODOT: 8, - OXDOT: 8, - OCALLPART: 8, - OPLUS: 7, - ONOT: 7, - OCOM: 7, - OMINUS: 7, - OADDR: 7, - OIND: 7, - ORECV: 7, - OMUL: 6, - ODIV: 6, - OMOD: 6, - OLSH: 6, - ORSH: 6, - OAND: 6, - OANDNOT: 6, - OADD: 5, - OSUB: 5, - OOR: 5, - OXOR: 5, - OEQ: 4, - OLT: 4, - OLE: 4, - OGE: 4, - OGT: 4, - ONE: 4, - OCMPSTR: 4, - OCMPIFACE: 4, - OSEND: 3, - OANDAND: 2, - OOROR: 1, - // Statements handled by stmtfmt - OAS: -1, - OAS2: -1, - OAS2DOTTYPE: -1, - OAS2FUNC: -1, - OAS2MAPR: -1, - OAS2RECV: -1, - OASOP: -1, - OBREAK: -1, - OCASE: -1, - OCONTINUE: -1, - ODCL: -1, - ODCLFIELD: -1, - ODEFER: -1, - OEMPTY: -1, - OFALL: -1, - OFOR: -1, - OGOTO: -1, - OIF: -1, - OLABEL: -1, - OPROC: -1, - ORANGE: -1, - ORETURN: -1, - OSELECT: -1, - OSWITCH: -1, - OXCASE: -1, - OXFALL: -1, - OEND: 0, -} - -func exprfmt(n *Node, prec int) string { - for n != nil && n.Implicit && (n.Op == OIND || n.Op == OADDR) { - n = n.Left - } - - if n == nil { - return "" - } - - nprec := opprec[n.Op] - if n.Op == OTYPE && n.Sym != nil { - nprec = 8 - } - - if prec > nprec { - return fmt.Sprintf("(%v)", n) - } - - switch n.Op { - case OPAREN: - return fmt.Sprintf("(%v)", n.Left) - - case ODDDARG: - return "... argument" - - case OREGISTER: - return obj.Rconv(int(n.Reg)) - - case OLITERAL: // this is a bit of a mess - if fmtmode == FErr { - if n.Orig != nil && n.Orig != n { - return exprfmt(n.Orig, prec) - } - if n.Sym != nil { - return Sconv(n.Sym, 0) - } - } - if n.Val.Ctype == CTNIL && n.Orig != nil && n.Orig != n { - return exprfmt(n.Orig, prec) - } - if n.Type != nil && n.Type != Types[n.Type.Etype] && n.Type != idealbool && n.Type != idealstring { - // Need parens when type begins with what might - // be misinterpreted as a unary operator: * or <-. - if Isptr[n.Type.Etype] || (n.Type.Etype == TCHAN && n.Type.Chan == Crecv) { - return fmt.Sprintf("(%v)(%v)", n.Type, Vconv(&n.Val, 0)) - } else { - return fmt.Sprintf("%v(%v)", n.Type, Vconv(&n.Val, 0)) - } - } - - return Vconv(&n.Val, 0) - - // Special case: name used as local variable in export. - // _ becomes ~b%d internally; print as _ for export - case ONAME: - 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 { - return fmt.Sprintf("%v·%d", n.Sym, n.Vargen) - } - - // Special case: explicit name of func (*T) method(...) is turned into pkg.(*T).method, - // but for export, this should be rendered as (*pkg.T).meth. - // These nodes have the special property that they are names with a left OTYPE and a right ONAME. - if fmtmode == FExp && n.Left != nil && n.Left.Op == OTYPE && n.Right != nil && n.Right.Op == ONAME { - if Isptr[n.Left.Type.Etype] { - return fmt.Sprintf("(%v).%v", n.Left.Type, Sconv(n.Right.Sym, obj.FmtShort|obj.FmtByte)) - } else { - return fmt.Sprintf("%v.%v", n.Left.Type, Sconv(n.Right.Sym, obj.FmtShort|obj.FmtByte)) - } - } - fallthrough - - //fallthrough - case OPACK, ONONAME: - return Sconv(n.Sym, 0) - - case OTYPE: - if n.Type == nil && n.Sym != nil { - return Sconv(n.Sym, 0) - } - return Tconv(n.Type, 0) - - case OTARRAY: - if n.Left != nil { - return fmt.Sprintf("[]%v", n.Left) - } - var f string - f += fmt.Sprintf("[]%v", n.Right) - return f // happens before typecheck - - case OTMAP: - return fmt.Sprintf("map[%v]%v", n.Left, n.Right) - - case OTCHAN: - switch n.Etype { - case Crecv: - return fmt.Sprintf("<-chan %v", n.Left) - - case Csend: - return fmt.Sprintf("chan<- %v", n.Left) - - default: - if n.Left != nil && n.Left.Op == OTCHAN && n.Left.Sym == nil && n.Left.Etype == Crecv { - return fmt.Sprintf("chan (%v)", n.Left) - } else { - return fmt.Sprintf("chan %v", n.Left) - } - } - - case OTSTRUCT: - return "" - - case OTINTER: - return "" - - case OTFUNC: - return "" - - case OCLOSURE: - if fmtmode == FErr { - return "func literal" - } - if n.Nbody != nil { - return fmt.Sprintf("%v { %v }", n.Type, n.Nbody) - } - return fmt.Sprintf("%v { %v }", n.Type, n.Closure.Nbody) - - case OCOMPLIT: - ptrlit := n.Right != nil && n.Right.Implicit && n.Right.Type != nil && Isptr[n.Right.Type.Etype] - if fmtmode == FErr { - if n.Right != nil && n.Right.Type != nil && !n.Implicit { - if ptrlit { - return fmt.Sprintf("&%v literal", n.Right.Type.Type) - } else { - return fmt.Sprintf("%v literal", n.Right.Type) - } - } - - return "composite literal" - } - - if fmtmode == FExp && ptrlit { - // typecheck has overwritten OIND by OTYPE with pointer type. - return fmt.Sprintf("(&%v{ %v })", n.Right.Type.Type, Hconv(n.List, obj.FmtComma)) - } - - return fmt.Sprintf("(%v{ %v })", n.Right, Hconv(n.List, obj.FmtComma)) - - case OPTRLIT: - if fmtmode == FExp && n.Left.Implicit { - return Nconv(n.Left, 0) - } - return fmt.Sprintf("&%v", n.Left) - - case OSTRUCTLIT: - if fmtmode == FExp { // requires special handling of field names - var f string - if n.Implicit { - f += "{" - } else { - f += fmt.Sprintf("(%v{", n.Type) - } - for l := n.List; l != nil; l = l.Next { - f += fmt.Sprintf(" %v:%v", Sconv(l.N.Left.Sym, obj.FmtShort|obj.FmtByte), l.N.Right) - - if l.Next != nil { - f += "," - } else { - f += " " - } - } - - if !n.Implicit { - f += "})" - return f - } - f += "}" - return f - } - fallthrough - - case OARRAYLIT, OMAPLIT: - if fmtmode == FErr { - return fmt.Sprintf("%v literal", n.Type) - } - if fmtmode == FExp && n.Implicit { - return fmt.Sprintf("{ %v }", Hconv(n.List, obj.FmtComma)) - } - return fmt.Sprintf("(%v{ %v })", n.Type, Hconv(n.List, obj.FmtComma)) - - case OKEY: - if n.Left != nil && n.Right != nil { - if fmtmode == FExp && n.Left.Type != nil && n.Left.Type.Etype == TFIELD { - // requires special handling of field names - return fmt.Sprintf("%v:%v", Sconv(n.Left.Sym, obj.FmtShort|obj.FmtByte), n.Right) - } else { - return fmt.Sprintf("%v:%v", n.Left, n.Right) - } - } - - if n.Left == nil && n.Right != nil { - return fmt.Sprintf(":%v", n.Right) - } - if n.Left != nil && n.Right == nil { - return fmt.Sprintf("%v:", n.Left) - } - return ":" - - case OXDOT, - ODOT, - ODOTPTR, - ODOTINTER, - ODOTMETH, - OCALLPART: - var f string - f += exprfmt(n.Left, nprec) - if n.Right == nil || n.Right.Sym == nil { - f += "." - return f - } - f += fmt.Sprintf(".%v", Sconv(n.Right.Sym, obj.FmtShort|obj.FmtByte)) - return f - - case ODOTTYPE, ODOTTYPE2: - var f string - f += exprfmt(n.Left, nprec) - if n.Right != nil { - f += fmt.Sprintf(".(%v)", n.Right) - return f - } - f += fmt.Sprintf(".(%v)", n.Type) - return f - - case OINDEX, - OINDEXMAP, - OSLICE, - OSLICESTR, - OSLICEARR, - OSLICE3, - OSLICE3ARR: - var f string - f += exprfmt(n.Left, nprec) - f += fmt.Sprintf("[%v]", n.Right) - return f - - case OCOPY, OCOMPLEX: - return fmt.Sprintf("%v(%v, %v)", Oconv(int(n.Op), obj.FmtSharp), n.Left, n.Right) - - case OCONV, - OCONVIFACE, - OCONVNOP, - OARRAYBYTESTR, - OARRAYRUNESTR, - OSTRARRAYBYTE, - OSTRARRAYRUNE, - ORUNESTR: - if n.Type == nil || n.Type.Sym == nil { - return fmt.Sprintf("(%v)(%v)", n.Type, n.Left) - } - if n.Left != nil { - return fmt.Sprintf("%v(%v)", n.Type, n.Left) - } - return fmt.Sprintf("%v(%v)", n.Type, Hconv(n.List, obj.FmtComma)) - - case OREAL, - OIMAG, - OAPPEND, - OCAP, - OCLOSE, - ODELETE, - OLEN, - OMAKE, - ONEW, - OPANIC, - ORECOVER, - OPRINT, - OPRINTN: - if n.Left != nil { - return fmt.Sprintf("%v(%v)", Oconv(int(n.Op), obj.FmtSharp), n.Left) - } - if n.Isddd { - return fmt.Sprintf("%v(%v...)", Oconv(int(n.Op), obj.FmtSharp), Hconv(n.List, obj.FmtComma)) - } - return fmt.Sprintf("%v(%v)", Oconv(int(n.Op), obj.FmtSharp), Hconv(n.List, obj.FmtComma)) - - case OCALL, OCALLFUNC, OCALLINTER, OCALLMETH, OGETG: - var f string - f += exprfmt(n.Left, nprec) - if n.Isddd { - f += fmt.Sprintf("(%v...)", Hconv(n.List, obj.FmtComma)) - return f - } - f += fmt.Sprintf("(%v)", Hconv(n.List, obj.FmtComma)) - return f - - case OMAKEMAP, OMAKECHAN, OMAKESLICE: - if n.List != nil { // pre-typecheck - return fmt.Sprintf("make(%v, %v)", n.Type, Hconv(n.List, obj.FmtComma)) - } - if n.Right != nil { - return fmt.Sprintf("make(%v, %v, %v)", n.Type, n.Left, n.Right) - } - if n.Left != nil && (n.Op == OMAKESLICE || !isideal(n.Left.Type)) { - return fmt.Sprintf("make(%v, %v)", n.Type, n.Left) - } - return fmt.Sprintf("make(%v)", n.Type) - - // Unary - case OPLUS, - OMINUS, - OADDR, - OCOM, - OIND, - ONOT, - ORECV: - var f string - if n.Left.Op == n.Op { - f += fmt.Sprintf("%v ", Oconv(int(n.Op), obj.FmtSharp)) - } else { - f += Oconv(int(n.Op), obj.FmtSharp) - } - f += exprfmt(n.Left, nprec+1) - return f - - // Binary - case OADD, - OAND, - OANDAND, - OANDNOT, - ODIV, - OEQ, - OGE, - OGT, - OLE, - OLT, - OLSH, - OMOD, - OMUL, - ONE, - OOR, - OOROR, - ORSH, - OSEND, - OSUB, - OXOR: - var f string - f += exprfmt(n.Left, nprec) - - f += fmt.Sprintf(" %v ", Oconv(int(n.Op), obj.FmtSharp)) - f += exprfmt(n.Right, nprec+1) - return f - - case OADDSTR: - var f string - for l := n.List; l != nil; l = l.Next { - if l != n.List { - f += " + " - } - f += exprfmt(l.N, nprec) - } - - return f - - case OCMPSTR, OCMPIFACE: - var f string - f += exprfmt(n.Left, nprec) - f += fmt.Sprintf(" %v ", Oconv(int(n.Etype), obj.FmtSharp)) - f += exprfmt(n.Right, nprec+1) - return f - } - - return fmt.Sprintf("", Oconv(int(n.Op), 0)) -} - -func nodefmt(n *Node, flag int) string { - t := n.Type - - // we almost always want the original, except in export mode for literals - // this saves the importer some work, and avoids us having to redo some - // special casing for package unsafe - if (fmtmode != FExp || n.Op != OLITERAL) && n.Orig != nil { - n = n.Orig - } - - if flag&obj.FmtLong != 0 && t != nil { - if t.Etype == TNIL { - return "nil" - } else { - return fmt.Sprintf("%v (type %v)", n, t) - } - } - - // TODO inlining produces expressions with ninits. we can't print these yet. - - if opprec[n.Op] < 0 { - return stmtfmt(n) - } - - return exprfmt(n, 0) -} - -var dumpdepth int - -func indent(buf *bytes.Buffer) { - buf.WriteString("\n") - for i := 0; i < dumpdepth; i++ { - buf.WriteString(". ") - } -} - -func nodedump(n *Node, flag int) string { - if n == nil { - return "" - } - - recur := flag&obj.FmtShort == 0 - - var buf bytes.Buffer - if recur { - indent(&buf) - if dumpdepth > 10 { - buf.WriteString("...") - return buf.String() - } - - if n.Ninit != nil { - fmt.Fprintf(&buf, "%v-init%v", Oconv(int(n.Op), 0), n.Ninit) - indent(&buf) - } - } - - switch n.Op { - default: - fmt.Fprintf(&buf, "%v%v", Oconv(int(n.Op), 0), Jconv(n, 0)) - - case OREGISTER, OINDREG: - fmt.Fprintf(&buf, "%v-%v%v", Oconv(int(n.Op), 0), obj.Rconv(int(n.Reg)), Jconv(n, 0)) - - case OLITERAL: - fmt.Fprintf(&buf, "%v-%v%v", Oconv(int(n.Op), 0), Vconv(&n.Val, 0), Jconv(n, 0)) - - case ONAME, ONONAME: - if n.Sym != nil { - fmt.Fprintf(&buf, "%v-%v%v", Oconv(int(n.Op), 0), n.Sym, Jconv(n, 0)) - } else { - fmt.Fprintf(&buf, "%v%v", Oconv(int(n.Op), 0), Jconv(n, 0)) - } - if recur && n.Type == nil && n.Ntype != nil { - indent(&buf) - fmt.Fprintf(&buf, "%v-ntype%v", Oconv(int(n.Op), 0), n.Ntype) - } - - case OASOP: - fmt.Fprintf(&buf, "%v-%v%v", Oconv(int(n.Op), 0), Oconv(int(n.Etype), 0), Jconv(n, 0)) - - 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 { - indent(&buf) - fmt.Fprintf(&buf, "%v-ntype%v", Oconv(int(n.Op), 0), n.Ntype) - } - } - - if n.Sym != nil && n.Op != ONAME { - fmt.Fprintf(&buf, " %v G%d", n.Sym, n.Vargen) - } - - if n.Type != nil { - fmt.Fprintf(&buf, " %v", n.Type) - } - - if recur { - if n.Left != nil { - buf.WriteString(Nconv(n.Left, 0)) - } - if n.Right != nil { - buf.WriteString(Nconv(n.Right, 0)) - } - if n.List != nil { - indent(&buf) - fmt.Fprintf(&buf, "%v-list%v", Oconv(int(n.Op), 0), n.List) - } - - if n.Rlist != nil { - indent(&buf) - fmt.Fprintf(&buf, "%v-rlist%v", Oconv(int(n.Op), 0), n.Rlist) - } - - if n.Ntest != nil { - indent(&buf) - fmt.Fprintf(&buf, "%v-test%v", Oconv(int(n.Op), 0), n.Ntest) - } - - if n.Nbody != nil { - indent(&buf) - fmt.Fprintf(&buf, "%v-body%v", Oconv(int(n.Op), 0), n.Nbody) - } - - if n.Nelse != nil { - indent(&buf) - fmt.Fprintf(&buf, "%v-else%v", Oconv(int(n.Op), 0), n.Nelse) - } - - if n.Nincr != nil { - indent(&buf) - fmt.Fprintf(&buf, "%v-incr%v", Oconv(int(n.Op), 0), n.Nincr) - } - } - - return buf.String() -} - -func (s *Sym) String() string { - return Sconv(s, 0) -} - -// Fmt "%S": syms -// Flags: "%hS" suppresses qualifying with package -func Sconv(s *Sym, flag int) string { - if flag&obj.FmtLong != 0 { - panic("linksymfmt") - } - - if s == nil { - return "" - } - - if s.Name == "_" { - return "_" - } - - sf := flag - sm := setfmode(&flag) - var r int - _ = r - str := symfmt(s, flag) - flag = sf - fmtmode = sm - return str -} - -func (t *Type) String() string { - return Tconv(t, 0) -} - -// Fmt "%T": types. -// Flags: 'l' print definition, not name -// 'h' omit 'func' and receiver from function types, short type names -// 'u' package name, not prefix (FTypeId mode, sticky) -func Tconv(t *Type, flag int) string { - if t == nil { - return "" - } - - if t.Trecur > 4 { - return "<...>" - } - - t.Trecur++ - sf := flag - sm := setfmode(&flag) - - if fmtmode == FTypeId && (sf&obj.FmtUnsigned != 0) { - fmtpkgpfx++ - } - if fmtpkgpfx != 0 { - flag |= obj.FmtUnsigned - } - - var r int - _ = r - str := typefmt(t, flag) - - if fmtmode == FTypeId && (sf&obj.FmtUnsigned != 0) { - fmtpkgpfx-- - } - - flag = sf - fmtmode = sm - t.Trecur-- - return str -} - -func (n *Node) String() string { - return Nconv(n, 0) -} - -// Fmt '%N': Nodes. -// Flags: 'l' suffix with "(type %T)" where possible -// '+h' in debug mode, don't recurse, no multiline output -func Nconv(n *Node, flag int) string { - if n == nil { - return "" - } - sf := flag - sm := setfmode(&flag) - - var r int - _ = r - var str string - switch fmtmode { - case FErr, FExp: - str = nodefmt(n, flag) - - case FDbg: - dumpdepth++ - str = nodedump(n, flag) - dumpdepth-- - - default: - Fatal("unhandled %%N mode") - } - - flag = sf - fmtmode = sm - return str -} - -func (l *NodeList) String() string { - return Hconv(l, 0) -} - -// Fmt '%H': NodeList. -// Flags: all those of %N plus ',': separate with comma's instead of semicolons. -func Hconv(l *NodeList, flag int) string { - if l == nil && fmtmode == FDbg { - return "" - } - - sf := flag - sm := setfmode(&flag) - var r int - _ = r - sep := "; " - if fmtmode == FDbg { - sep = "\n" - } else if flag&obj.FmtComma != 0 { - sep = ", " - } - - var buf bytes.Buffer - for ; l != nil; l = l.Next { - buf.WriteString(Nconv(l.N, 0)) - if l.Next != nil { - buf.WriteString(sep) - } - } - - flag = sf - fmtmode = sm - return buf.String() -} - -func dumplist(s string, l *NodeList) { - fmt.Printf("%s%v\n", s, Hconv(l, obj.FmtSign)) -} - -func Dump(s string, n *Node) { - fmt.Printf("%s [%p]%v\n", s, n, Nconv(n, obj.FmtSign)) -} diff --git a/src/cmd/internal/gc/gen.go b/src/cmd/internal/gc/gen.go deleted file mode 100644 index d3c6387a4e..0000000000 --- a/src/cmd/internal/gc/gen.go +++ /dev/null @@ -1,1279 +0,0 @@ -// Copyright 2009 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 gc - -import ( - "cmd/internal/obj" - "fmt" -) - -/* - * portable half of code generator. - * mainly statements and control flow. - */ -var labellist *Label - -var lastlabel *Label - -func Sysfunc(name string) *Node { - n := newname(Pkglookup(name, Runtimepkg)) - n.Class = PFUNC - return n -} - -// addrescapes tags node n as having had its address taken -// by "increasing" the "value" of n.Esc to EscHeap. -// Storage is allocated as necessary to allow the address -// to be taken. -func addrescapes(n *Node) { - switch n.Op { - // probably a type error already. - // dump("addrescapes", n); - default: - break - - case ONAME: - if n == nodfp { - break - } - - // if this is a tmpname (PAUTO), it was tagged by tmpname as not escaping. - // on PPARAM it means something different. - if n.Class == PAUTO && n.Esc == EscNever { - break - } - - switch n.Class { - case PPARAMREF: - addrescapes(n.Defn) - - // if func param, need separate temporary - // to hold heap pointer. - // the function type has already been checked - // (we're in the function body) - // so the param already has a valid xoffset. - - // expression to refer to stack copy - case PPARAM, PPARAMOUT: - n.Stackparam = Nod(OPARAM, n, nil) - - n.Stackparam.Type = n.Type - n.Stackparam.Addable = true - if n.Xoffset == BADWIDTH { - Fatal("addrescapes before param assignment") - } - n.Stackparam.Xoffset = n.Xoffset - fallthrough - - case PAUTO: - n.Class |= PHEAP - - n.Addable = false - n.Ullman = 2 - n.Xoffset = 0 - - // create stack variable to hold pointer to heap - oldfn := Curfn - - Curfn = n.Curfn - n.Name.Heapaddr = temp(Ptrto(n.Type)) - buf := fmt.Sprintf("&%v", n.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) - } - Curfn = oldfn - } - - case OIND, ODOTPTR: - break - - // ODOTPTR has already been introduced, - // so these are the non-pointer ODOT and OINDEX. - // In &x[0], if x is a slice, then x does not - // escape--the pointer inside x does, but that - // is always a heap pointer anyway. - case ODOT, OINDEX, OPAREN, OCONVNOP: - if !Isslice(n.Left.Type) { - addrescapes(n.Left) - } - } -} - -func clearlabels() { - for l := labellist; l != nil; l = l.Link { - l.Sym.Label = nil - } - - labellist = nil - lastlabel = nil -} - -func newlab(n *Node) *Label { - s := n.Left.Sym - lab := s.Label - if lab == nil { - lab = new(Label) - if lastlabel == nil { - labellist = lab - } else { - lastlabel.Link = lab - } - lastlabel = lab - lab.Sym = s - s.Label = lab - } - - if n.Op == OLABEL { - if lab.Def != nil { - Yyerror("label %v already defined at %v", s, lab.Def.Line()) - } else { - lab.Def = n - } - } else { - lab.Use = list(lab.Use, n) - } - - return lab -} - -func checkgoto(from *Node, to *Node) { - if from.Sym == to.Sym { - return - } - - nf := 0 - for fs := from.Sym; fs != nil; fs = fs.Link { - nf++ - } - nt := 0 - for fs := to.Sym; fs != nil; fs = fs.Link { - nt++ - } - fs := from.Sym - for ; nf > nt; nf-- { - fs = fs.Link - } - if fs != to.Sym { - lno := int(lineno) - setlineno(from) - - // decide what to complain about. - // prefer to complain about 'into block' over declarations, - // so scan backward to find most recent block or else dcl. - var block *Sym - - var dcl *Sym - ts := to.Sym - for ; nt > nf; nt-- { - if ts.Pkg == nil { - block = ts - } else { - dcl = ts - } - ts = ts.Link - } - - for ts != fs { - if ts.Pkg == nil { - block = ts - } else { - dcl = ts - } - ts = ts.Link - fs = fs.Link - } - - if block != nil { - Yyerror("goto %v jumps into block starting at %v", from.Left.Sym, Ctxt.Line(int(block.Lastlineno))) - } else { - Yyerror("goto %v jumps over declaration of %v at %v", from.Left.Sym, dcl, Ctxt.Line(int(dcl.Lastlineno))) - } - lineno = int32(lno) - } -} - -func stmtlabel(n *Node) *Label { - if n.Sym != nil { - lab := n.Sym.Label - if lab != nil { - if lab.Def != nil { - if lab.Def.Defn == n { - return lab - } - } - } - } - return nil -} - -/* - * compile statements - */ -func Genlist(l *NodeList) { - for ; l != nil; l = l.Next { - gen(l.N) - } -} - -/* - * generate code to start new proc running call n. - */ -func cgen_proc(n *Node, proc int) { - switch n.Left.Op { - default: - Fatal("cgen_proc: unknown call %v", Oconv(int(n.Left.Op), 0)) - - case OCALLMETH: - cgen_callmeth(n.Left, proc) - - case OCALLINTER: - cgen_callinter(n.Left, nil, proc) - - case OCALLFUNC: - cgen_call(n.Left, proc) - } -} - -/* - * generate declaration. - * have to allocate heap copy - * for escaped variables. - */ -func cgen_dcl(n *Node) { - if Debug['g'] != 0 { - Dump("\ncgen-dcl", n) - } - if n.Op != ONAME { - Dump("cgen_dcl", n) - Fatal("cgen_dcl") - } - - if n.Class&PHEAP == 0 { - return - } - if compiling_runtime != 0 { - Yyerror("%v escapes to heap, not allowed in runtime.", n) - } - if n.Alloc == nil { - n.Alloc = callnew(n.Type) - } - Cgen_as(n.Name.Heapaddr, n.Alloc) -} - -/* - * generate discard of value - */ -func cgen_discard(nr *Node) { - if nr == nil { - return - } - - switch nr.Op { - case ONAME: - if nr.Class&PHEAP == 0 && nr.Class != PEXTERN && nr.Class != PFUNC && nr.Class != PPARAMREF { - gused(nr) - } - - // unary - case OADD, - OAND, - ODIV, - OEQ, - OGE, - OGT, - OLE, - OLSH, - OLT, - OMOD, - OMUL, - ONE, - OOR, - ORSH, - OSUB, - OXOR: - cgen_discard(nr.Left) - - cgen_discard(nr.Right) - - // binary - case OCAP, - OCOM, - OLEN, - OMINUS, - ONOT, - OPLUS: - cgen_discard(nr.Left) - - case OIND: - Cgen_checknil(nr.Left) - - // special enough to just evaluate - default: - var tmp Node - Tempname(&tmp, nr.Type) - - Cgen_as(&tmp, nr) - gused(&tmp) - } -} - -/* - * clearslim generates code to zero a slim node. - */ -func Clearslim(n *Node) { - var z Node - z.Op = OLITERAL - z.Type = n.Type - z.Addable = true - - switch Simtype[n.Type.Etype] { - case TCOMPLEX64, TCOMPLEX128: - 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 = &zero - - case TPTR32, TPTR64, TCHAN, TMAP: - z.Val.Ctype = CTNIL - - case TBOOL: - z.Val.Ctype = CTBOOL - z.Val.U = false - - case TINT8, - TINT16, - TINT32, - TINT64, - TUINT8, - TUINT16, - TUINT32, - TUINT64: - z.Val.Ctype = CTINT - z.Val.U = new(Mpint) - Mpmovecfix(z.Val.U.(*Mpint), 0) - - default: - Fatal("clearslim called on type %v", n.Type) - } - - ullmancalc(&z) - Cgen(&z, n) -} - -/* - * generate: - * res = iface{typ, data} - * n->left is typ - * n->right is data - */ -func Cgen_eface(n *Node, res *Node) { - /* - * the right node of an eface may contain function calls that uses res as an argument, - * so it's important that it is done first - */ - - tmp := temp(Types[Tptr]) - Cgen(n.Right, tmp) - - Gvardef(res) - - dst := *res - dst.Type = Types[Tptr] - dst.Xoffset += int64(Widthptr) - Cgen(tmp, &dst) - - dst.Xoffset -= int64(Widthptr) - Cgen(n.Left, &dst) -} - -/* - * generate one of: - * res, resok = x.(T) - * res = x.(T) (when resok == nil) - * n.Left is x - * n.Type is T - */ -func cgen_dottype(n *Node, res, resok *Node, wb bool) { - if Debug_typeassert > 0 { - Warn("type assertion inlined") - } - // iface := n.Left - // r1 := iword(iface) - // if n.Left is non-empty interface { - // r1 = *r1 - // } - // if r1 == T { - // res = idata(iface) - // resok = true - // } else { - // assert[EI]2T(x, T, nil) // (when resok == nil; does not return) - // resok = false // (when resok != nil) - // } - // - var iface Node - Igen(n.Left, &iface, res) - var r1, r2 Node - byteptr := Ptrto(Types[TUINT8]) // type used in runtime prototypes for runtime type (*byte) - Regalloc(&r1, byteptr, nil) - iface.Type = byteptr - Cgen(&iface, &r1) - if !isnilinter(n.Left.Type) { - // Holding itab, want concrete type in second word. - p := Thearch.Ginscmp(OEQ, byteptr, &r1, Nodintconst(0), -1) - r2 = r1 - r2.Op = OINDREG - r2.Xoffset = int64(Widthptr) - Cgen(&r2, &r1) - Patch(p, Pc) - } - Regalloc(&r2, byteptr, nil) - Cgen(typename(n.Type), &r2) - 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) - Regfree(&iface) - - if resok == nil { - r1.Type = res.Type - cgen_wb(&r1, res, wb) - q := Gbranch(obj.AJMP, nil, 0) - Patch(p, Pc) - Regrealloc(&r2) // reclaim from above, for this failure path - fn := syslook("panicdottype", 0) - dowidth(fn.Type) - call := Nod(OCALLFUNC, fn, nil) - r1.Type = byteptr - r2.Type = byteptr - call.List = list(list(list1(&r1), &r2), typename(n.Left.Type)) - call.List = ascompatte(OCALLFUNC, call, false, getinarg(fn.Type), call.List, 0, nil) - gen(call) - Regfree(&r1) - Regfree(&r2) - Thearch.Gins(obj.AUNDEF, nil, nil) - Patch(q, Pc) - } else { - // This half is handling the res, resok = x.(T) case, - // which is called from gen, not cgen, and is consequently fussier - // about blank assignments. We have to avoid calling cgen for those. - r1.Type = res.Type - if !isblank(res) { - cgen_wb(&r1, res, wb) - } - Regfree(&r1) - if !isblank(resok) { - Cgen(Nodbool(true), resok) - } - q := Gbranch(obj.AJMP, nil, 0) - Patch(p, Pc) - if !isblank(res) { - n := nodnil() - n.Type = res.Type - Cgen(n, res) - } - if !isblank(resok) { - Cgen(Nodbool(false), resok) - } - Patch(q, Pc) - } -} - -/* - * generate: - * res, resok = x.(T) - * n.Left is x - * n.Type is T - */ -func Cgen_As2dottype(n, res, resok *Node) { - if Debug_typeassert > 0 { - Warn("type assertion inlined") - } - // iface := n.Left - // r1 := iword(iface) - // if n.Left is non-empty interface { - // r1 = *r1 - // } - // if r1 == T { - // res = idata(iface) - // resok = true - // } else { - // res = nil - // resok = false - // } - // - var iface Node - Igen(n.Left, &iface, nil) - var r1, r2 Node - byteptr := Ptrto(Types[TUINT8]) // type used in runtime prototypes for runtime type (*byte) - Regalloc(&r1, byteptr, res) - iface.Type = byteptr - Cgen(&iface, &r1) - if !isnilinter(n.Left.Type) { - // Holding itab, want concrete type in second word. - p := Thearch.Ginscmp(OEQ, byteptr, &r1, Nodintconst(0), -1) - r2 = r1 - r2.Op = OINDREG - r2.Xoffset = int64(Widthptr) - Cgen(&r2, &r1) - Patch(p, Pc) - } - Regalloc(&r2, byteptr, nil) - Cgen(typename(n.Type), &r2) - p := Thearch.Ginscmp(ONE, byteptr, &r1, &r2, -1) - iface.Type = n.Type - iface.Xoffset += int64(Widthptr) - Cgen(&iface, &r1) - if iface.Op != 0 { - Regfree(&iface) - } - Cgen(&r1, res) - q := Gbranch(obj.AJMP, nil, 0) - Patch(p, Pc) - - fn := syslook("panicdottype", 0) - dowidth(fn.Type) - call := Nod(OCALLFUNC, fn, nil) - call.List = list(list(list1(&r1), &r2), typename(n.Left.Type)) - call.List = ascompatte(OCALLFUNC, call, false, getinarg(fn.Type), call.List, 0, nil) - gen(call) - Regfree(&r1) - Regfree(&r2) - Thearch.Gins(obj.AUNDEF, nil, nil) - Patch(q, Pc) -} - -/* - * gather series of offsets - * >=0 is direct addressed field - * <0 is pointer to next field (+1) - */ -func Dotoffset(n *Node, oary []int64, nn **Node) int { - var i int - - switch n.Op { - case ODOT: - if n.Xoffset == BADWIDTH { - Dump("bad width in dotoffset", n) - Fatal("bad width in dotoffset") - } - - i = Dotoffset(n.Left, oary, nn) - if i > 0 { - if oary[i-1] >= 0 { - oary[i-1] += n.Xoffset - } else { - oary[i-1] -= n.Xoffset - } - break - } - - if i < 10 { - oary[i] = n.Xoffset - i++ - } - - case ODOTPTR: - if n.Xoffset == BADWIDTH { - Dump("bad width in dotoffset", n) - Fatal("bad width in dotoffset") - } - - i = Dotoffset(n.Left, oary, nn) - if i < 10 { - oary[i] = -(n.Xoffset + 1) - i++ - } - - default: - *nn = n - return 0 - } - - if i >= 10 { - *nn = nil - } - return i -} - -/* - * make a new off the books - */ -func Tempname(nn *Node, t *Type) { - if Curfn == nil { - Fatal("no curfn for tempname") - } - - if t == nil { - Yyerror("tempname called with nil type") - t = Types[TINT32] - } - - // give each tmp a different name so that there - // a chance to registerizer them - s := Lookupf("autotmp_%.4d", statuniqgen) - statuniqgen++ - n := Nod(ONAME, nil, nil) - n.Sym = s - s.Def = n - n.Type = t - n.Class = PAUTO - n.Addable = true - n.Ullman = 1 - n.Esc = EscNever - n.Curfn = Curfn - Curfn.Func.Dcl = list(Curfn.Func.Dcl, n) - - dowidth(t) - n.Xoffset = 0 - *nn = *n -} - -func temp(t *Type) *Node { - n := Nod(OXXX, nil, nil) - Tempname(n, t) - n.Sym.Def.Used = true - return n.Orig -} - -func gen(n *Node) { - //dump("gen", n); - - lno := setlineno(n) - - wasregalloc := Anyregalloc() - - if n == nil { - goto ret - } - - if n.Ninit != nil { - Genlist(n.Ninit) - } - - setlineno(n) - - switch n.Op { - default: - Fatal("gen: unknown op %v", Nconv(n, obj.FmtShort|obj.FmtSign)) - - case OCASE, - OFALL, - OXCASE, - OXFALL, - ODCLCONST, - ODCLFUNC, - ODCLTYPE: - break - - case OEMPTY: - break - - case OBLOCK: - Genlist(n.List) - - case OLABEL: - if isblanksym(n.Left.Sym) { - break - } - - lab := newlab(n) - - // if there are pending gotos, resolve them all to the current pc. - var p2 *obj.Prog - for p1 := lab.Gotopc; p1 != nil; p1 = p2 { - p2 = unpatch(p1) - Patch(p1, Pc) - } - - lab.Gotopc = nil - if lab.Labelpc == nil { - lab.Labelpc = Pc - } - - if n.Defn != nil { - switch n.Defn.Op { - // so stmtlabel can find the label - case OFOR, OSWITCH, OSELECT: - n.Defn.Sym = lab.Sym - } - } - - // if label is defined, emit jump to it. - // otherwise save list of pending gotos in lab->gotopc. - // the list is linked through the normal jump target field - // to avoid a second list. (the jumps are actually still - // valid code, since they're just going to another goto - // to the same label. we'll unwind it when we learn the pc - // of the label in the OLABEL case above.) - case OGOTO: - lab := newlab(n) - - if lab.Labelpc != nil { - gjmp(lab.Labelpc) - } else { - lab.Gotopc = gjmp(lab.Gotopc) - } - - case OBREAK: - if n.Left != nil { - lab := n.Left.Sym.Label - if lab == nil { - Yyerror("break label not defined: %v", n.Left.Sym) - break - } - - lab.Used = 1 - if lab.Breakpc == nil { - Yyerror("invalid break label %v", n.Left.Sym) - break - } - - gjmp(lab.Breakpc) - break - } - - if breakpc == nil { - Yyerror("break is not in a loop") - break - } - - gjmp(breakpc) - - case OCONTINUE: - if n.Left != nil { - lab := n.Left.Sym.Label - if lab == nil { - Yyerror("continue label not defined: %v", n.Left.Sym) - break - } - - lab.Used = 1 - if lab.Continpc == nil { - Yyerror("invalid continue label %v", n.Left.Sym) - break - } - - gjmp(lab.Continpc) - break - } - - if continpc == nil { - Yyerror("continue is not in a loop") - break - } - - gjmp(continpc) - - case OFOR: - sbreak := breakpc - p1 := gjmp(nil) // goto test - breakpc = gjmp(nil) // break: goto done - scontin := continpc - continpc = Pc - - // define break and continue labels - lab := stmtlabel(n) - if lab != nil { - lab.Breakpc = breakpc - lab.Continpc = continpc - } - - gen(n.Nincr) // contin: incr - Patch(p1, Pc) // test: - Bgen(n.Ntest, false, -1, breakpc) // if(!test) goto break - Genlist(n.Nbody) // body - gjmp(continpc) - Patch(breakpc, Pc) // done: - continpc = scontin - breakpc = sbreak - if lab != nil { - lab.Breakpc = nil - lab.Continpc = nil - } - - case OIF: - p1 := gjmp(nil) // goto test - p2 := gjmp(nil) // p2: goto else - Patch(p1, Pc) // test: - Bgen(n.Ntest, false, int(-n.Likely), p2) // if(!test) goto p2 - Genlist(n.Nbody) // then - p3 := gjmp(nil) // goto done - Patch(p2, Pc) // else: - Genlist(n.Nelse) // else - Patch(p3, Pc) // done: - - case OSWITCH: - sbreak := breakpc - p1 := gjmp(nil) // goto test - breakpc = gjmp(nil) // break: goto done - - // define break label - lab := stmtlabel(n) - if lab != nil { - lab.Breakpc = breakpc - } - - Patch(p1, Pc) // test: - Genlist(n.Nbody) // switch(test) body - Patch(breakpc, Pc) // done: - breakpc = sbreak - if lab != nil { - lab.Breakpc = nil - } - - case OSELECT: - sbreak := breakpc - p1 := gjmp(nil) // goto test - breakpc = gjmp(nil) // break: goto done - - // define break label - lab := stmtlabel(n) - if lab != nil { - lab.Breakpc = breakpc - } - - Patch(p1, Pc) // test: - Genlist(n.Nbody) // select() body - Patch(breakpc, Pc) // done: - breakpc = sbreak - if lab != nil { - lab.Breakpc = nil - } - - case ODCL: - cgen_dcl(n.Left) - - case OAS: - if gen_as_init(n) { - break - } - Cgen_as(n.Left, n.Right) - - case OASWB: - Cgen_as_wb(n.Left, n.Right, true) - - case OAS2DOTTYPE: - cgen_dottype(n.Rlist.N, n.List.N, n.List.Next.N, false) - - case OCALLMETH: - cgen_callmeth(n, 0) - - case OCALLINTER: - cgen_callinter(n, nil, 0) - - case OCALLFUNC: - cgen_call(n, 0) - - case OPROC: - cgen_proc(n, 1) - - case ODEFER: - cgen_proc(n, 2) - - case ORETURN, ORETJMP: - cgen_ret(n) - - // Function calls turned into compiler intrinsics. - // At top level, can just ignore the call and make sure to preserve side effects in the argument, if any. - case OGETG: - // nothing - case OSQRT: - cgen_discard(n.Left) - - case OCHECKNIL: - Cgen_checknil(n.Left) - - case OVARKILL: - gvarkill(n.Left) - } - -ret: - if Anyregalloc() != wasregalloc { - Dump("node", n) - Fatal("registers left allocated") - } - - lineno = lno -} - -func Cgen_as(nl, nr *Node) { - Cgen_as_wb(nl, nr, false) -} - -func Cgen_as_wb(nl, nr *Node, wb bool) { - if Debug['g'] != 0 { - op := "cgen_as" - if wb { - op = "cgen_as_wb" - } - Dump(op, nl) - Dump(op+" = ", nr) - } - - for nr != nil && nr.Op == OCONVNOP { - nr = nr.Left - } - - if nl == nil || isblank(nl) { - cgen_discard(nr) - return - } - - if nr == nil || iszero(nr) { - // heaps should already be clear - if nr == nil && (nl.Class&PHEAP != 0) { - return - } - - tl := nl.Type - if tl == nil { - return - } - if Isfat(tl) { - if nl.Op == ONAME { - Gvardef(nl) - } - Thearch.Clearfat(nl) - return - } - - Clearslim(nl) - return - } - - tl := nl.Type - if tl == nil { - return - } - - cgen_wb(nr, nl, wb) -} - -func cgen_callmeth(n *Node, proc int) { - // generate a rewrite in n2 for the method call - // (p.f)(...) goes to (f)(p,...) - - l := n.Left - - if l.Op != ODOTMETH { - Fatal("cgen_callmeth: not dotmethod: %v", l) - } - - n2 := *n - n2.Op = OCALLFUNC - n2.Left = l.Right - n2.Left.Type = l.Type - - if n2.Left.Op == ONAME { - n2.Left.Class = PFUNC - } - cgen_call(&n2, proc) -} - -// CgenTemp creates a temporary node, assigns n to it, and returns it. -func CgenTemp(n *Node) *Node { - var tmp Node - Tempname(&tmp, n.Type) - Cgen(n, &tmp) - return &tmp -} - -func checklabels() { - var l *NodeList - - for lab := labellist; lab != nil; lab = lab.Link { - if lab.Def == nil { - for l = lab.Use; l != nil; l = l.Next { - yyerrorl(int(l.N.Lineno), "label %v not defined", lab.Sym) - } - continue - } - - if lab.Use == nil && lab.Used == 0 { - yyerrorl(int(lab.Def.Lineno), "label %v defined and not used", lab.Sym) - continue - } - - if lab.Gotopc != nil { - Fatal("label %v never resolved", lab.Sym) - } - for l = lab.Use; l != nil; l = l.Next { - checkgoto(l.N, lab.Def) - } - } -} - -// Componentgen copies a composite value by moving its individual components. -// Slices, strings and interfaces are supported. Small structs or arrays with -// elements of basic type are also supported. -// nr is nil when assigning a zero value. -func Componentgen(nr, nl *Node) bool { - return componentgen_wb(nr, nl, false) -} - -// componentgen_wb is like componentgen but if wb==true emits write barriers for pointer updates. -func componentgen_wb(nr, nl *Node, wb bool) bool { - // Don't generate any code for complete copy of a variable into itself. - // It's useless, and the VARDEF will incorrectly mark the old value as dead. - // (This check assumes that the arguments passed to componentgen did not - // themselves come from Igen, or else we could have Op==ONAME but - // with a Type and Xoffset describing an individual field, not the entire - // variable.) - if nl.Op == ONAME && nl == nr { - return true - } - - // Count number of moves required to move components. - // If using write barrier, can only emit one pointer. - // TODO(rsc): Allow more pointers, for reflect.Value. - const maxMoves = 8 - n := 0 - numPtr := 0 - visitComponents(nl.Type, 0, func(t *Type, offset int64) bool { - n++ - if int(Simtype[t.Etype]) == Tptr && t != itable { - numPtr++ - } - return n <= maxMoves && (!wb || numPtr <= 1) - }) - if n > maxMoves || wb && numPtr > 1 { - return false - } - - // Must call emitVardef after evaluating rhs but before writing to lhs. - emitVardef := func() { - // Emit vardef if needed. - if nl.Op == ONAME { - switch nl.Type.Etype { - case TARRAY, TSTRING, TINTER, TSTRUCT: - Gvardef(nl) - } - } - } - - isConstString := Isconst(nr, CTSTR) - - if !cadable(nl) && nr != nil && !cadable(nr) && !isConstString { - return false - } - - var nodl Node - if cadable(nl) { - nodl = *nl - } else { - if nr != nil && !cadable(nr) && !isConstString { - return false - } - if nr == nil || isConstString || nl.Ullman >= nr.Ullman { - Igen(nl, &nodl, nil) - defer Regfree(&nodl) - } - } - lbase := nodl.Xoffset - - // Special case: zeroing. - var nodr Node - if nr == nil { - // When zeroing, prepare a register containing zero. - // TODO(rsc): Check that this is actually generating the best code. - if Thearch.REGZERO != 0 { - // cpu has a dedicated zero register - Nodreg(&nodr, Types[TUINT], Thearch.REGZERO) - } else { - // no dedicated zero register - var zero Node - Nodconst(&zero, nl.Type, 0) - Regalloc(&nodr, Types[TUINT], nil) - Thearch.Gmove(&zero, &nodr) - defer Regfree(&nodr) - } - - emitVardef() - visitComponents(nl.Type, 0, func(t *Type, offset int64) bool { - nodl.Type = t - nodl.Xoffset = lbase + offset - nodr.Type = t - if Isfloat[t.Etype] { - // TODO(rsc): Cache zero register like we do for integers? - Clearslim(&nodl) - } else { - Thearch.Gmove(&nodr, &nodl) - } - return true - }) - return true - } - - // Special case: assignment of string constant. - if isConstString { - emitVardef() - - // base - nodl.Type = Ptrto(Types[TUINT8]) - Regalloc(&nodr, Types[Tptr], nil) - p := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), nil, &nodr) - Datastring(nr.Val.U.(string), &p.From) - p.From.Type = obj.TYPE_ADDR - Thearch.Gmove(&nodr, &nodl) - Regfree(&nodr) - - // length - nodl.Type = Types[Simtype[TUINT]] - nodl.Xoffset += int64(Array_nel) - int64(Array_array) - Nodconst(&nodr, nodl.Type, int64(len(nr.Val.U.(string)))) - Thearch.Gmove(&nodr, &nodl) - return true - } - - // General case: copy nl = nr. - nodr = *nr - if !cadable(nr) { - if nr.Ullman >= UINF && nodl.Op == OINDREG { - Fatal("miscompile") - } - Igen(nr, &nodr, nil) - defer Regfree(&nodr) - } - rbase := nodr.Xoffset - - if nodl.Op == 0 { - Igen(nl, &nodl, nil) - defer Regfree(&nodl) - lbase = nodl.Xoffset - } - - emitVardef() - var ( - ptrType *Type - ptrOffset int64 - ) - visitComponents(nl.Type, 0, func(t *Type, offset int64) bool { - if wb && int(Simtype[t.Etype]) == Tptr && t != itable { - if ptrType != nil { - Fatal("componentgen_wb %v", Tconv(nl.Type, 0)) - } - ptrType = t - ptrOffset = offset - return true - } - nodl.Type = t - nodl.Xoffset = lbase + offset - nodr.Type = t - nodr.Xoffset = rbase + offset - Thearch.Gmove(&nodr, &nodl) - return true - }) - if ptrType != nil { - nodl.Type = ptrType - nodl.Xoffset = lbase + ptrOffset - nodr.Type = ptrType - nodr.Xoffset = rbase + ptrOffset - cgen_wbptr(&nodr, &nodl) - } - return true -} - -// visitComponents walks the individual components of the type t, -// walking into array elements, struct fields, the real and imaginary -// parts of complex numbers, and on 32-bit systems the high and -// low halves of 64-bit integers. -// It calls f for each such component, passing the component (aka element) -// type and memory offset, assuming t starts at startOffset. -// If f ever returns false, visitComponents returns false without any more -// calls to f. Otherwise visitComponents returns true. -func visitComponents(t *Type, startOffset int64, f func(elem *Type, elemOffset int64) bool) bool { - switch t.Etype { - case TINT64: - if Widthreg == 8 { - break - } - // NOTE: Assuming little endian (signed top half at offset 4). - // We don't have any 32-bit big-endian systems. - if Thearch.Thechar != '5' && Thearch.Thechar != '8' { - Fatal("unknown 32-bit architecture") - } - return f(Types[TUINT32], startOffset) && - f(Types[TINT32], startOffset+4) - - case TUINT64: - if Widthreg == 8 { - break - } - return f(Types[TUINT32], startOffset) && - f(Types[TUINT32], startOffset+4) - - case TCOMPLEX64: - return f(Types[TFLOAT32], startOffset) && - f(Types[TFLOAT32], startOffset+4) - - case TCOMPLEX128: - return f(Types[TFLOAT64], startOffset) && - f(Types[TFLOAT64], startOffset+8) - - case TINTER: - return f(itable, startOffset) && - f(Ptrto(Types[TUINT8]), startOffset+int64(Widthptr)) - return true - - case TSTRING: - return f(Ptrto(Types[TUINT8]), startOffset) && - f(Types[Simtype[TUINT]], startOffset+int64(Widthptr)) - - case TARRAY: - if Isslice(t) { - return f(Ptrto(t.Type), startOffset+int64(Array_array)) && - f(Types[Simtype[TUINT]], startOffset+int64(Array_nel)) && - f(Types[Simtype[TUINT]], startOffset+int64(Array_cap)) - } - - // Short-circuit [1e6]struct{}. - if t.Type.Width == 0 { - return true - } - - for i := int64(0); i < t.Bound; i++ { - if !visitComponents(t.Type, startOffset+i*t.Type.Width, f) { - return false - } - } - return true - - case TSTRUCT: - if t.Type != nil && t.Type.Width != 0 { - // NOTE(rsc): If this happens, the right thing to do is to say - // startOffset -= t.Type.Width - // but I want to see if it does. - // The old version of componentgen handled this, - // in code introduced in CL 6932045 to fix issue #4518. - // But the test case in issue 4518 does not trigger this anymore, - // so maybe this complication is no longer needed. - Fatal("struct not at offset 0") - } - - for field := t.Type; field != nil; field = field.Down { - if field.Etype != TFIELD { - Fatal("bad struct") - } - if !visitComponents(field.Type, startOffset+field.Width, f) { - return false - } - } - return true - } - return f(t, startOffset) -} - -func cadable(n *Node) bool { - // Note: Not sure why you can have n.Op == ONAME without n.Addable, but you can. - return n.Addable && n.Op == ONAME -} diff --git a/src/cmd/internal/gc/go.go b/src/cmd/internal/gc/go.go deleted file mode 100644 index 6a3379b896..0000000000 --- a/src/cmd/internal/gc/go.go +++ /dev/null @@ -1,841 +0,0 @@ -// Copyright 2009 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 gc - -import ( - "bytes" - "cmd/internal/gc/big" - "cmd/internal/obj" -) - -// avoid - -// The parser's maximum stack size. -// We have to use a #define macro here since yacc -// or bison will check for its definition and use -// a potentially smaller value if it is undefined. -const ( - NHUNK = 50000 - BUFSIZ = 8192 - NSYMB = 500 - NHASH = 1024 - MAXALIGN = 7 - UINF = 100 - PRIME1 = 3 - BADWIDTH = -1000000000 - MaxStackVarSize = 10 * 1024 * 1024 -) - -const ( - // These values are known by runtime. - // The MEMx and NOEQx values must run in parallel. See algtype. - AMEM = iota - AMEM0 - AMEM8 - AMEM16 - AMEM32 - AMEM64 - AMEM128 - ANOEQ - ANOEQ0 - ANOEQ8 - ANOEQ16 - ANOEQ32 - ANOEQ64 - ANOEQ128 - ASTRING - AINTER - ANILINTER - ASLICE - AFLOAT32 - AFLOAT64 - ACPLX64 - ACPLX128 - AUNK = 100 -) - -const ( - // Maximum size in bits for Mpints before signalling - // overflow and also mantissa precision for Mpflts. - Mpprec = 512 - // Turn on for constant arithmetic debugging output. - Mpdebug = false -) - -// Mpint represents an integer constant. -type Mpint struct { - Val big.Int - Ovf bool // set if Val overflowed compiler limit (sticky) -} - -// Mpflt represents a floating-point constant. -type Mpflt struct { - Val big.Float -} - -// Mpcplx represents a complex constant. -type Mpcplx struct { - Real Mpflt - Imag Mpflt -} - -type Val struct { - Ctype int16 - // 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 { - Name string // package name - Path string // string literal used in import statement - Pathsym *Sym - Prefix string // escaped path for use in symbol table - Imported uint8 // export data of this package was parsed - Exported int8 // import line written in export data - Direct int8 // imported directly - Safe bool // whether the package is marked as safe - Syms map[string]*Sym -} - -type Sym struct { - Lexical uint16 - Flags uint8 - Link *Sym - Uniqgen uint32 - Importdef *Pkg // where imported definition was found - Linkname string // link name - - // saved and restored by dcopy - Pkg *Pkg - Name string // variable name - Def *Node // definition: ONAME OTYPE OPACK or OLITERAL - Label *Label // corresponding label (ephemeral) - Block int32 // blocknumber to catch redeclaration - Lastlineno int32 // last declaration for diagnostic - Origpkg *Pkg // original package for . import - Lsym *obj.LSym - Fsym *Sym // funcsym -} - -type Type struct { - Etype uint8 - Nointerface bool - Noalg uint8 - Chan uint8 - Trecur uint8 // to detect loops - Printed uint8 - Embedded uint8 // TFIELD embedded type - Siggen uint8 - Funarg uint8 // on TSTRUCT and TFIELD - Copyany uint8 - Local bool // created in this file - Deferwidth uint8 - Broke uint8 // broken type definition. - Isddd bool // TFIELD is ... argument - Align uint8 - Haspointers uint8 // 0 unknown, 1 no, 2 yes - - Nod *Node // canonical OTYPE node - Orig *Type // original type (type literal or predefined type) - Lineno int - - // TFUNC - Thistuple int - Outtuple int - Intuple int - Outnamed uint8 - - Method *Type - Xmethod *Type - - Sym *Sym - Vargen int32 // unique name for OTYPE/ONAME - - Nname *Node - Argwid int64 - - // most nodes - Type *Type // actual type for TFIELD, element type for TARRAY, TCHAN, TMAP, TPTRxx - Width int64 // offset in TFIELD, width in all others - - // TFIELD - Down *Type // next struct field, also key type in TMAP - Outer *Type // outer struct - Note *string // literal string annotation - - // TARRAY - Bound int64 // negative is dynamic array - - // TMAP - Bucket *Type // internal type representing a hash bucket - Hmap *Type // internal type representing a Hmap (map header object) - Hiter *Type // internal type representing hash iterator state - Map *Type // link from the above 3 internal types back to the map type. - - Maplineno int32 // first use of TFORW as map key - Embedlineno int32 // first use of TFORW as embedded type - - // for TFORW, where to copy the eventual value to - Copyto *NodeList - - Lastfn *Node // for usefield -} - -type Label struct { - Used uint8 - Sym *Sym - Def *Node - Use *NodeList - Link *Label - - // for use during gen - Gotopc *obj.Prog // pointer to unresolved gotos - Labelpc *obj.Prog // pointer to code - Breakpc *obj.Prog // pointer to code - Continpc *obj.Prog // pointer to code -} - -type InitEntry struct { - Xoffset int64 // struct, array only - Expr *Node // bytes of run-time computed expressions -} - -type InitPlan struct { - Lit int64 - Zero int64 - Expr int64 - E []InitEntry -} - -const ( - SymExport = 1 << 0 // to be exported - SymPackage = 1 << 1 - SymExported = 1 << 2 // already written out by export - SymUniq = 1 << 3 - SymSiggen = 1 << 4 - SymAsm = 1 << 5 - SymAlgGen = 1 << 6 -) - -var dclstack *Sym - -type Iter struct { - Done int - Tfunc *Type - T *Type -} - -const ( - Txxx = iota - - TINT8 - TUINT8 - TINT16 - TUINT16 - TINT32 - TUINT32 - TINT64 - TUINT64 - TINT - TUINT - TUINTPTR - - TCOMPLEX64 - TCOMPLEX128 - - TFLOAT32 - TFLOAT64 - - TBOOL - - TPTR32 - TPTR64 - - TFUNC - TARRAY - T_old_DARRAY - TSTRUCT - TCHAN - TMAP - TINTER - TFORW - TFIELD - TANY - TSTRING - TUNSAFEPTR - - // pseudo-types for literals - TIDEAL - TNIL - TBLANK - - // pseudo-type for frame layout - TFUNCARGS - TCHANARGS - TINTERMETH - - NTYPE -) - -const ( - CTxxx = iota - - CTINT - CTRUNE - CTFLT - CTCPLX - CTSTR - CTBOOL - CTNIL -) - -const ( - /* types of channel */ - /* must match ../../pkg/nreflect/type.go:/Chandir */ - Cxxx = 0 - Crecv = 1 << 0 - Csend = 1 << 1 - Cboth = Crecv | Csend -) - -// declaration context -const ( - Pxxx = uint8(iota) - PEXTERN // global variable - PAUTO // local variables - PPARAM // input arguments - PPARAMOUT // output results - PPARAMREF // closure variable reference - PFUNC // global function - - PDISCARD // discard during parse of duplicate import - - PHEAP = uint8(1 << 7) // an extra bit to identify an escaped variable -) - -const ( - Etop = 1 << 1 // evaluated at statement level - Erv = 1 << 2 // evaluated in value context - Etype = 1 << 3 - Ecall = 1 << 4 // call-only expressions are ok - Efnstruct = 1 << 5 // multivalue function returns are ok - Eiota = 1 << 6 // iota is ok - Easgn = 1 << 7 // assigning to expression - Eindir = 1 << 8 // indirecting through expression - Eaddr = 1 << 9 // taking address of expression - Eproc = 1 << 10 // inside a go statement - Ecomplit = 1 << 11 // type in composite literal -) - -type Typedef struct { - Name string - Etype int - Sameas int -} - -type Sig struct { - name string - pkg *Pkg - isym *Sym - tsym *Sym - type_ *Type - mtype *Type - offset int32 - link *Sig -} - -type Io struct { - infile string - bin *obj.Biobuf - nlsemi int - eofnl int - last int - peekc int - peekc1 int // second peekc for ... - cp string // used for content when bin==nil - importsafe bool -} - -type Dlist struct { - field *Type -} - -type Idir struct { - link *Idir - dir string -} - -/* - * argument passing to/from - * smagic and umagic - */ -type Magic struct { - W int // input for both - width - S int // output for both - shift - Bad int // output for both - unexpected failure - - // magic multiplier for signed literal divisors - Sd int64 // input - literal divisor - Sm int64 // output - multiplier - - // magic multiplier for unsigned literal divisors - Ud uint64 // input - literal divisor - Um uint64 // output - multiplier - Ua int // output - adder -} - -/* - * note this is the runtime representation - * of the compilers arrays. - * - * typedef struct - * { // must not move anything - * uchar array[8]; // pointer to data - * uchar nel[4]; // number of elements - * uchar cap[4]; // allocated number of elements - * } Array; - */ -var Array_array int // runtime offsetof(Array,array) - same for String - -var Array_nel int // runtime offsetof(Array,nel) - same for String - -var Array_cap int // runtime offsetof(Array,cap) - -var sizeof_Array int // runtime sizeof(Array) - -/* - * note this is the runtime representation - * of the compilers strings. - * - * typedef struct - * { // must not move anything - * uchar array[8]; // pointer to data - * uchar nel[4]; // number of elements - * } String; - */ -var sizeof_String int // runtime sizeof(String) - -var dotlist [10]Dlist // size is max depth of embeddeds - -var curio Io - -var pushedio Io - -var lexlineno int32 - -var lineno int32 - -var prevlineno int32 - -var pragcgobuf string - -var infile string - -var outfile string - -var bout *obj.Biobuf - -var nerrors int - -var nsavederrors int - -var nsyntaxerrors int - -var decldepth int32 - -var safemode int - -var nolocalimports int - -var lexbuf bytes.Buffer -var strbuf bytes.Buffer - -var litbuf string - -var Debug [256]int - -var debugstr string - -var Debug_checknil int -var Debug_typeassert int - -var importmyname *Sym // my name for package - -var localpkg *Pkg // package being compiled - -var importpkg *Pkg // package being imported - -var structpkg *Pkg // package that declared struct, during import - -var builtinpkg *Pkg // fake package for builtins - -var gostringpkg *Pkg // fake pkg for Go strings - -var itabpkg *Pkg // fake pkg for itab cache - -var Runtimepkg *Pkg // package runtime - -var racepkg *Pkg // package runtime/race - -var typepkg *Pkg // fake package for runtime type info (headers) - -var typelinkpkg *Pkg // fake package for runtime type info (data) - -var weaktypepkg *Pkg // weak references to runtime type info - -var unsafepkg *Pkg // package unsafe - -var trackpkg *Pkg // fake package for field tracking - -var Tptr int // either TPTR32 or TPTR64 - -var myimportpath string - -var idirs *Idir - -var localimport string - -var asmhdr string - -var Types [NTYPE]*Type - -var idealstring *Type - -var idealbool *Type - -var bytetype *Type - -var runetype *Type - -var errortype *Type - -var Simtype [NTYPE]uint8 - -var ( - Isptr [NTYPE]bool - isforw [NTYPE]bool - Isint [NTYPE]bool - Isfloat [NTYPE]bool - Iscomplex [NTYPE]bool - Issigned [NTYPE]bool - issimple [NTYPE]bool -) - -var ( - okforeq [NTYPE]bool - okforadd [NTYPE]bool - okforand [NTYPE]bool - okfornone [NTYPE]bool - okforcmp [NTYPE]bool - okforbool [NTYPE]bool - okforcap [NTYPE]bool - okforlen [NTYPE]bool - okforarith [NTYPE]bool - okforconst [NTYPE]bool -) - -var ( - okfor [OEND][]bool - iscmp [OEND]bool -) - -var Minintval [NTYPE]*Mpint - -var Maxintval [NTYPE]*Mpint - -var minfltval [NTYPE]*Mpflt - -var maxfltval [NTYPE]*Mpflt - -var xtop *NodeList - -var externdcl *NodeList - -var exportlist *NodeList - -var importlist *NodeList // imported functions and methods with inlinable bodies - -var funcsyms *NodeList - -var dclcontext uint8 // PEXTERN/PAUTO - -var incannedimport int - -var statuniqgen int // name generator for static temps - -var loophack int - -var iota_ int32 - -var lastconst *NodeList - -var lasttype *Node - -var Maxarg int64 - -var Stksize int64 // stack size for current frame - -var stkptrsize int64 // prefix of stack containing pointers - -var blockgen int32 // max block number - -var block int32 // current block number - -var Hasdefer int // flag that curfn has defer statetment - -var Curfn *Node - -var Widthptr int - -var Widthint int - -var Widthreg int - -var typesw *Node - -var nblank *Node - -var hunk string - -var nhunk int32 - -var thunk int32 - -var Funcdepth int32 - -var typecheckok int - -var compiling_runtime int - -var compiling_wrappers int - -var inl_nonlocal int - -var use_writebarrier int - -var pure_go int - -var flag_installsuffix string - -var flag_race int - -var flag_largemodel int - -var noescape bool - -var nosplit bool - -var nowritebarrier bool - -var debuglive int - -var Ctxt *obj.Link - -var nointerface bool - -var writearchive int - -var bstdout obj.Biobuf - -var Nacl bool - -var continpc *obj.Prog - -var breakpc *obj.Prog - -var Pc *obj.Prog - -var nodfp *Node - -var Disable_checknil int - -var zerosize int64 - -type Flow struct { - Prog *obj.Prog // actual instruction - P1 *Flow // predecessors of this instruction: p1, - P2 *Flow // and then p2 linked though p2link. - P2link *Flow - S1 *Flow // successors of this instruction (at most two: s1 and s2). - S2 *Flow - Link *Flow // next instruction in function code - - Active int32 // usable by client - - Id int32 // sequence number in flow graph - Rpo int32 // reverse post ordering - Loop uint16 // x5 for every loop - Refset uint8 // diagnostic generated - - Data interface{} // for use by client -} - -type Graph struct { - Start *Flow - Num int - - // After calling flowrpo, rpo lists the flow nodes in reverse postorder, - // and each non-dead Flow node f has g->rpo[f->rpo] == f. - Rpo []*Flow -} - -/* - * interface to back end - */ - -const ( - // Pseudo-op, like TEXT, GLOBL, TYPE, PCDATA, FUNCDATA. - Pseudo = 1 << 1 - - // There's nothing to say about the instruction, - // but it's still okay to see. - OK = 1 << 2 - - // Size of right-side write, or right-side read if no write. - SizeB = 1 << 3 - SizeW = 1 << 4 - SizeL = 1 << 5 - SizeQ = 1 << 6 - SizeF = 1 << 7 - SizeD = 1 << 8 - - // Left side (Prog.from): address taken, read, write. - LeftAddr = 1 << 9 - LeftRead = 1 << 10 - LeftWrite = 1 << 11 - - // Register in middle (Prog.reg); only ever read. (arm, ppc64) - RegRead = 1 << 12 - CanRegRead = 1 << 13 - - // Right side (Prog.to): address taken, read, write. - RightAddr = 1 << 14 - RightRead = 1 << 15 - RightWrite = 1 << 16 - - // Instruction kinds - Move = 1 << 17 // straight move - Conv = 1 << 18 // size conversion - Cjmp = 1 << 19 // conditional jump - Break = 1 << 20 // breaks control flow (no fallthrough) - Call = 1 << 21 // function call - Jump = 1 << 22 // jump - Skip = 1 << 23 // data instruction - - // Set, use, or kill of carry bit. - // Kill means we never look at the carry bit after this kind of instruction. - SetCarry = 1 << 24 - UseCarry = 1 << 25 - KillCarry = 1 << 26 - - // Special cases for register use. (amd64, 386) - ShiftCX = 1 << 27 // possible shift by CX - ImulAXDX = 1 << 28 // possible multiply into DX:AX - - // Instruction updates whichever of from/to is type D_OREG. (ppc64) - PostInc = 1 << 29 -) - -type Arch struct { - Thechar int - Thestring string - Thelinkarch *obj.LinkArch - Typedefs []Typedef - REGSP int - REGCTXT int - REGCALLX int // BX - REGCALLX2 int // AX - REGRETURN int // AX - REGMIN int - REGMAX int - REGZERO int // architectural zero register, if available - FREGMIN int - FREGMAX int - MAXWIDTH int64 - ReservedRegs []int - - AddIndex func(*Node, int64, *Node) bool // optional - Betypeinit func() - Bgen_float func(*Node, bool, int, *obj.Prog) // optional - Cgen64 func(*Node, *Node) // only on 32-bit systems - Cgenindex func(*Node, *Node, bool) *obj.Prog - Cgen_bmul func(int, *Node, *Node, *Node) bool - Cgen_float func(*Node, *Node) // optional - Cgen_hmul func(*Node, *Node, *Node) - Cgen_shift func(int, bool, *Node, *Node, *Node) - Clearfat func(*Node) - Cmp64 func(*Node, *Node, int, int, *obj.Prog) // only on 32-bit systems - Defframe func(*obj.Prog) - Dodiv func(int, *Node, *Node, *Node) - Excise func(*Flow) - 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, 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 - // 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) - - Ginscon func(int, int64, *Node) - Ginsnop func() - Gmove func(*Node, *Node) - Igenindex func(*Node, *Node, bool) *obj.Prog - Linkarchinit func() - Peep func(*obj.Prog) - Proginfo func(*obj.Prog) // fills in Prog.Info - Regtyp func(*obj.Addr) bool - Sameaddr func(*obj.Addr, *obj.Addr) bool - Smallindir func(*obj.Addr, *obj.Addr) bool - Stackaddr func(*obj.Addr) bool - Blockcopy func(*Node, *Node, int64, int64, int64) - Sudoaddable func(int, *Node, *obj.Addr) bool - Sudoclean func() - Excludedregs func() uint64 - RtoB func(int) uint64 - FtoB func(int) uint64 - BtoR func(uint64) int - BtoF func(uint64) int - Optoas func(int, *Type) int - Doregbits func(int) uint64 - Regnames func(*int) []string - Use387 bool // should 8g use 387 FP instructions instead of sse2. -} - -var pcloc int32 - -var Thearch Arch - -var Newproc *Node - -var Deferproc *Node - -var Deferreturn *Node - -var Panicindex *Node - -var panicslice *Node - -var throwreturn *Node diff --git a/src/cmd/internal/gc/go.y b/src/cmd/internal/gc/go.y deleted file mode 100644 index 7d523ae7c0..0000000000 --- a/src/cmd/internal/gc/go.y +++ /dev/null @@ -1,2312 +0,0 @@ -// Copyright 2009 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. - -/* - * Go language grammar. - * - * The Go semicolon rules are: - * - * 1. all statements and declarations are terminated by semicolons. - * 2. semicolons can be omitted before a closing ) or }. - * 3. semicolons are inserted by the lexer before a newline - * following a specific list of tokens. - * - * Rules #1 and #2 are accomplished by writing the lists as - * semicolon-separated lists with an optional trailing semicolon. - * Rule #3 is implemented in yylex. - */ - -%{ -package gc - -import ( - "fmt" - "strings" -) -%} -%union { - node *Node - list *NodeList - typ *Type - sym *Sym - val Val - i int -} - -// |sed 's/.* //' |9 fmt -l1 |sort |9 fmt -l50 | sed 's/^/%xxx /' - -%token LLITERAL -%token LASOP LCOLAS -%token LBREAK LCASE LCHAN LCONST LCONTINUE LDDD -%token LDEFAULT LDEFER LELSE LFALL LFOR LFUNC LGO LGOTO -%token LIF LIMPORT LINTERFACE LMAP LNAME -%token LPACKAGE LRANGE LRETURN LSELECT LSTRUCT LSWITCH -%token LTYPE LVAR - -%token LANDAND LANDNOT LBODY LCOMM LDEC LEQ LGE LGT -%token LIGNORE LINC LLE LLSH LLT LNE LOROR LRSH - -%type lbrace import_here -%type sym packname -%type oliteral - -%type stmt ntype -%type arg_type -%type case caseblock -%type compound_stmt dotname embed expr complitexpr bare_complitexpr -%type expr_or_type -%type fndcl hidden_fndcl fnliteral -%type for_body for_header for_stmt if_header if_stmt non_dcl_stmt -%type interfacedcl keyval labelname name -%type name_or_type non_expr_type -%type new_name dcl_name oexpr typedclname -%type onew_name -%type osimple_stmt pexpr pexpr_no_paren -%type pseudocall range_stmt select_stmt -%type simple_stmt -%type switch_stmt uexpr -%type xfndcl typedcl start_complit - -%type xdcl fnbody fnres loop_body dcl_name_list -%type new_name_list expr_list keyval_list braced_keyval_list expr_or_type_list xdcl_list -%type oexpr_list caseblock_list elseif elseif_list else stmt_list oarg_type_list_ocomma arg_type_list -%type interfacedcl_list vardcl vardcl_list structdcl structdcl_list -%type common_dcl constdcl constdcl1 constdcl_list typedcl_list - -%type convtype comptype dotdotdot -%type indcl interfacetype structtype ptrtype -%type recvchantype non_recvchantype othertype fnret_type fntype - -%type hidden_importsym hidden_pkg_importsym - -%type hidden_constant hidden_literal hidden_funarg -%type hidden_interfacedcl hidden_structdcl - -%type hidden_funres -%type ohidden_funres -%type hidden_funarg_list ohidden_funarg_list -%type hidden_interfacedcl_list ohidden_interfacedcl_list -%type hidden_structdcl_list ohidden_structdcl_list - -%type hidden_type hidden_type_misc hidden_pkgtype -%type hidden_type_func -%type hidden_type_recv_chan hidden_type_non_recv_chan - -%left LCOMM /* outside the usual hierarchy; here for good error messages */ - -%left LOROR -%left LANDAND -%left LEQ LNE LLE LGE LLT LGT -%left '+' '-' '|' '^' -%left '*' '/' '%' '&' LLSH LRSH LANDNOT - -/* - * manual override of shift/reduce conflicts. - * the general form is that we assign a precedence - * to the token being shifted and then introduce - * NotToken with lower precedence or PreferToToken with higher - * and annotate the reducing rule accordingly. - */ -%left NotPackage -%left LPACKAGE - -%left NotParen -%left '(' - -%left ')' -%left PreferToRightParen - -%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: - loadsys - package - imports - xdcl_list - { - xtop = concat(xtop, $4); - } - -package: - %prec NotPackage - { - prevlineno = lineno; - Yyerror("package statement must be first"); - errorexit(); - } -| LPACKAGE sym ';' - { - mkpackage($2.Name); - } - -/* - * this loads the definitions for the low-level runtime functions, - * so that the compiler can generate calls to them, - * but does not make the name "runtime" visible as a package. - */ -loadsys: - { - importpkg = Runtimepkg; - - if Debug['A'] != 0 { - cannedimports("runtime.Builtin", "package runtime\n\n$$\n\n"); - } else { - cannedimports("runtime.Builtin", runtimeimport); - } - curio.importsafe = true - } - import_package - import_there - { - importpkg = nil; - } - -imports: -| imports import ';' - -import: - LIMPORT import_stmt -| LIMPORT '(' import_stmt_list osemi ')' -| LIMPORT '(' ')' - -import_stmt: - import_here import_package import_there - { - ipkg := importpkg; - my := importmyname; - importpkg = nil; - importmyname = nil; - - if my == nil { - my = Lookup(ipkg.Name); - } - - pack := Nod(OPACK, nil, nil); - pack.Sym = my; - pack.Pkg = ipkg; - pack.Lineno = int32($1); - - if strings.HasPrefix(my.Name, ".") { - importdot(ipkg, pack); - break; - } - if my.Name == "init" { - Yyerror("cannot import package as init - init must be a func"); - break; - } - if my.Name == "_" { - break; - } - if my.Def != nil { - lineno = int32($1); - redeclare(my, "as imported package name"); - } - my.Def = pack; - my.Lastlineno = int32($1); - my.Block = 1; // at top level - } -| import_here import_there - { - // When an invalid import path is passed to importfile, - // it calls Yyerror and then sets up a fake import with - // no package statement. This allows us to test more - // than one invalid import statement in a single file. - if nerrors == 0 { - Fatal("phase error in import"); - } - } - -import_stmt_list: - import_stmt -| import_stmt_list ';' import_stmt - -import_here: - LLITERAL - { - // import with original name - $$ = parserline(); - importmyname = nil; - importfile(&$1, $$); - } -| sym LLITERAL - { - // import with given name - $$ = parserline(); - importmyname = $1; - importfile(&$2, $$); - } -| '.' LLITERAL - { - // import into my name space - $$ = parserline(); - importmyname = Lookup("."); - importfile(&$2, $$); - } - -import_package: - LPACKAGE LNAME import_safety ';' - { - if importpkg.Name == "" { - importpkg.Name = $2.Name; - numImport[$2.Name]++ - } else if importpkg.Name != $2.Name { - Yyerror("conflicting names %s and %s for package %q", importpkg.Name, $2.Name, importpkg.Path); - } - importpkg.Direct = 1; - importpkg.Safe = curio.importsafe - - if safemode != 0 && !curio.importsafe { - Yyerror("cannot import unsafe package %q", importpkg.Path); - } - } - -import_safety: -| LNAME - { - if $1.Name == "safe" { - curio.importsafe = true - } - } - -import_there: - { - defercheckwidth(); - } - hidden_import_list '$' '$' - { - resumecheckwidth(); - unimportfile(); - } - -/* - * declarations - */ -xdcl: - { - Yyerror("empty top-level declaration"); - $$ = nil; - } -| common_dcl -| xfndcl - { - $$ = list1($1); - } -| non_dcl_stmt - { - Yyerror("non-declaration statement outside function body"); - $$ = nil; - } -| error - { - $$ = nil; - } - -common_dcl: - LVAR vardcl - { - $$ = $2; - } -| LVAR '(' vardcl_list osemi ')' - { - $$ = $3; - } -| LVAR '(' ')' - { - $$ = nil; - } -| lconst constdcl - { - $$ = $2; - iota_ = -100000; - lastconst = nil; - } -| lconst '(' constdcl osemi ')' - { - $$ = $3; - iota_ = -100000; - lastconst = nil; - } -| lconst '(' constdcl ';' constdcl_list osemi ')' - { - $$ = concat($3, $5); - iota_ = -100000; - lastconst = nil; - } -| lconst '(' ')' - { - $$ = nil; - iota_ = -100000; - } -| LTYPE typedcl - { - $$ = list1($2); - } -| LTYPE '(' typedcl_list osemi ')' - { - $$ = $3; - } -| LTYPE '(' ')' - { - $$ = nil; - } - -lconst: - LCONST - { - iota_ = 0; - } - -vardcl: - dcl_name_list ntype - { - $$ = variter($1, $2, nil); - } -| dcl_name_list ntype '=' expr_list - { - $$ = variter($1, $2, $4); - } -| dcl_name_list '=' expr_list - { - $$ = variter($1, nil, $3); - } - -constdcl: - dcl_name_list ntype '=' expr_list - { - $$ = constiter($1, $2, $4); - } -| dcl_name_list '=' expr_list - { - $$ = constiter($1, nil, $3); - } - -constdcl1: - constdcl -| dcl_name_list ntype - { - $$ = constiter($1, $2, nil); - } -| dcl_name_list - { - $$ = constiter($1, nil, nil); - } - -typedclname: - sym - { - // different from dclname because the name - // becomes visible right here, not at the end - // of the declaration. - $$ = typedcl0($1); - } - -typedcl: - typedclname ntype - { - $$ = typedcl1($1, $2, true); - } - -simple_stmt: - expr - { - $$ = $1; - - // These nodes do not carry line numbers. - // Since a bare name used as an expression is an error, - // introduce a wrapper node to give the correct line. - switch($$.Op) { - case ONAME, ONONAME, OTYPE, OPACK, OLITERAL: - $$ = Nod(OPAREN, $$, nil); - $$.Implicit = true; - break; - } - } -| expr LASOP expr - { - $$ = Nod(OASOP, $1, $3); - $$.Etype = uint8($2); // rathole to pass opcode - } -| expr_list '=' expr_list - { - if $1.Next == nil && $3.Next == nil { - // simple - $$ = Nod(OAS, $1.N, $3.N); - break; - } - // multiple - $$ = Nod(OAS2, nil, nil); - $$.List = $1; - $$.Rlist = $3; - } -| expr_list LCOLAS expr_list - { - if $3.N.Op == OTYPESW { - $$ = Nod(OTYPESW, nil, $3.N.Right); - if $3.Next != nil { - Yyerror("expr.(type) must be alone in list"); - } - if $1.Next != nil { - Yyerror("argument count mismatch: %d = %d", count($1), 1); - } else if ($1.N.Op != ONAME && $1.N.Op != OTYPE && $1.N.Op != ONONAME) || isblank($1.N) { - Yyerror("invalid variable name %s in type switch", $1.N); - } else { - $$.Left = dclname($1.N.Sym); - } // it's a colas, so must not re-use an oldname. - break; - } - $$ = colas($1, $3, int32($2)); - } -| expr LINC - { - $$ = Nod(OASOP, $1, Nodintconst(1)); - $$.Implicit = true; - $$.Etype = OADD; - } -| expr LDEC - { - $$ = Nod(OASOP, $1, Nodintconst(1)); - $$.Implicit = true; - $$.Etype = OSUB; - } - -case: - LCASE expr_or_type_list ':' - { - var n, nn *Node - - // will be converted to OCASE - // right will point to next case - // done in casebody() - markdcl(); - $$ = Nod(OXCASE, nil, nil); - $$.List = $2; - if typesw != nil && typesw.Right != nil { - n = typesw.Right.Left - if n != nil { - // type switch - declare variable - nn = newname(n.Sym); - declare(nn, dclcontext); - $$.Nname = nn; - - // keep track of the instances for reporting unused - nn.Defn = typesw.Right; - } - } - } -| LCASE expr_or_type_list '=' expr ':' - { - var n *Node - - // will be converted to OCASE - // right will point to next case - // done in casebody() - markdcl(); - $$ = Nod(OXCASE, nil, nil); - if $2.Next == nil { - n = Nod(OAS, $2.N, $4); - } else { - n = Nod(OAS2, nil, nil); - n.List = $2; - n.Rlist = list1($4); - } - $$.List = list1(n); - } -| LCASE expr_or_type_list LCOLAS expr ':' - { - // will be converted to OCASE - // right will point to next case - // done in casebody() - markdcl(); - $$ = Nod(OXCASE, nil, nil); - $$.List = list1(colas($2, list1($4), int32($3))); - } -| LDEFAULT ':' - { - var n, nn *Node - - markdcl(); - $$ = Nod(OXCASE, nil, nil); - if typesw != nil && typesw.Right != nil { - n = typesw.Right.Left - if n != nil { - // type switch - declare variable - nn = newname(n.Sym); - declare(nn, dclcontext); - $$.Nname = nn; - - // keep track of the instances for reporting unused - nn.Defn = typesw.Right; - } - } - } - -compound_stmt: - '{' - { - markdcl(); - } - stmt_list '}' - { - if $3 == nil { - $$ = Nod(OEMPTY, nil, nil); - } else { - $$ = liststmt($3); - } - popdcl(); - } - -caseblock: - case - { - // If the last token read by the lexer was consumed - // as part of the case, clear it (parser has cleared yychar). - // If the last token read by the lexer was the lookahead - // leave it alone (parser has it cached in yychar). - // This is so that the stmt_list action doesn't look at - // the case tokens if the stmt_list is empty. - yylast = yychar; - $1.Xoffset = int64(block); - } - stmt_list - { - // This is the only place in the language where a statement - // list is not allowed to drop the final semicolon, because - // it's the only place where a statement list is not followed - // by a closing brace. Handle the error for pedantry. - - // Find the final token of the statement list. - // yylast is lookahead; yyprev is last of stmt_list - last := yyprev; - - if last > 0 && last != ';' && yychar != '}' { - Yyerror("missing statement after label"); - } - $$ = $1; - $$.Nbody = $3; - popdcl(); - } - -caseblock_list: - { - $$ = nil; - } -| caseblock_list caseblock - { - $$ = list($1, $2); - } - -loop_body: - LBODY - { - markdcl(); - } - stmt_list '}' - { - $$ = $3; - popdcl(); - } - -range_stmt: - expr_list '=' LRANGE expr - { - $$ = Nod(ORANGE, nil, $4); - $$.List = $1; - $$.Etype = 0; // := flag - } -| expr_list LCOLAS LRANGE expr - { - $$ = Nod(ORANGE, nil, $4); - $$.List = $1; - $$.Colas = true; - colasdefn($1, $$); - } -| LRANGE expr - { - $$ = Nod(ORANGE, nil, $2); - $$.Etype = 0; // := flag - } - -for_header: - osimple_stmt ';' osimple_stmt ';' osimple_stmt - { - // init ; test ; incr - if $5 != nil && $5.Colas { - Yyerror("cannot declare in the for-increment"); - } - $$ = Nod(OFOR, nil, nil); - if $1 != nil { - $$.Ninit = list1($1); - } - $$.Ntest = $3; - $$.Nincr = $5; - } -| osimple_stmt - { - // normal test - $$ = Nod(OFOR, nil, nil); - $$.Ntest = $1; - } -| range_stmt - -for_body: - for_header loop_body - { - $$ = $1; - $$.Nbody = concat($$.Nbody, $2); - } - -for_stmt: - LFOR - { - markdcl(); - } - for_body - { - $$ = $3; - popdcl(); - } - -if_header: - osimple_stmt - { - // test - $$ = Nod(OIF, nil, nil); - $$.Ntest = $1; - } -| osimple_stmt ';' osimple_stmt - { - // init ; test - $$ = Nod(OIF, nil, nil); - if $1 != nil { - $$.Ninit = list1($1); - } - $$.Ntest = $3; - } - -/* IF cond body (ELSE IF cond body)* (ELSE block)? */ -if_stmt: - LIF - { - markdcl(); - } - if_header - { - if $3.Ntest == nil { - Yyerror("missing condition in if statement"); - } - } - loop_body - { - $3.Nbody = $5; - } - elseif_list else - { - var n *Node - var nn *NodeList - - $$ = $3; - n = $3; - popdcl(); - for nn = concat($7, $8); nn != nil; nn = nn.Next { - if nn.N.Op == OIF { - popdcl(); - } - n.Nelse = list1(nn.N); - n = nn.N; - } - } - -elseif: - LELSE LIF - { - markdcl(); - } - if_header loop_body - { - if $4.Ntest == nil { - Yyerror("missing condition in if statement"); - } - $4.Nbody = $5; - $$ = list1($4); - } - -elseif_list: - { - $$ = nil; - } -| elseif_list elseif - { - $$ = concat($1, $2); - } - -else: - { - $$ = nil; - } -| LELSE compound_stmt - { - l := &NodeList{N: $2} - l.End = l - $$ = l; - } - -switch_stmt: - LSWITCH - { - markdcl(); - } - if_header - { - var n *Node - n = $3.Ntest; - if n != nil && n.Op != OTYPESW { - n = nil; - } - typesw = Nod(OXXX, typesw, n); - } - LBODY caseblock_list '}' - { - $$ = $3; - $$.Op = OSWITCH; - $$.List = $6; - typesw = typesw.Left; - popdcl(); - } - -select_stmt: - LSELECT - { - typesw = Nod(OXXX, typesw, nil); - } - LBODY caseblock_list '}' - { - $$ = Nod(OSELECT, nil, nil); - $$.Lineno = typesw.Lineno; - $$.List = $4; - typesw = typesw.Left; - } - -/* - * expressions - */ -expr: - uexpr -| expr LOROR expr - { - $$ = Nod(OOROR, $1, $3); - } -| expr LANDAND expr - { - $$ = Nod(OANDAND, $1, $3); - } -| expr LEQ expr - { - $$ = Nod(OEQ, $1, $3); - } -| expr LNE expr - { - $$ = Nod(ONE, $1, $3); - } -| expr LLT expr - { - $$ = Nod(OLT, $1, $3); - } -| expr LLE expr - { - $$ = Nod(OLE, $1, $3); - } -| expr LGE expr - { - $$ = Nod(OGE, $1, $3); - } -| expr LGT expr - { - $$ = Nod(OGT, $1, $3); - } -| expr '+' expr - { - $$ = Nod(OADD, $1, $3); - } -| expr '-' expr - { - $$ = Nod(OSUB, $1, $3); - } -| expr '|' expr - { - $$ = Nod(OOR, $1, $3); - } -| expr '^' expr - { - $$ = Nod(OXOR, $1, $3); - } -| expr '*' expr - { - $$ = Nod(OMUL, $1, $3); - } -| expr '/' expr - { - $$ = Nod(ODIV, $1, $3); - } -| expr '%' expr - { - $$ = Nod(OMOD, $1, $3); - } -| expr '&' expr - { - $$ = Nod(OAND, $1, $3); - } -| expr LANDNOT expr - { - $$ = Nod(OANDNOT, $1, $3); - } -| expr LLSH expr - { - $$ = Nod(OLSH, $1, $3); - } -| expr LRSH expr - { - $$ = Nod(ORSH, $1, $3); - } - /* not an expression anymore, but left in so we can give a good error */ -| expr LCOMM expr - { - $$ = Nod(OSEND, $1, $3); - } - -uexpr: - pexpr -| '*' uexpr - { - $$ = Nod(OIND, $2, nil); - } -| '&' uexpr - { - if $2.Op == OCOMPLIT { - // Special case for &T{...}: turn into (*T){...}. - $$ = $2; - $$.Right = Nod(OIND, $$.Right, nil); - $$.Right.Implicit = true; - } else { - $$ = Nod(OADDR, $2, nil); - } - } -| '+' uexpr - { - $$ = Nod(OPLUS, $2, nil); - } -| '-' uexpr - { - $$ = Nod(OMINUS, $2, nil); - } -| '!' uexpr - { - $$ = Nod(ONOT, $2, nil); - } -| '~' uexpr - { - Yyerror("the bitwise complement operator is ^"); - $$ = Nod(OCOM, $2, nil); - } -| '^' uexpr - { - $$ = Nod(OCOM, $2, nil); - } -| LCOMM uexpr - { - $$ = Nod(ORECV, $2, nil); - } - -/* - * call-like statements that - * can be preceded by 'defer' and 'go' - */ -pseudocall: - pexpr '(' ')' - { - $$ = Nod(OCALL, $1, nil); - } -| pexpr '(' expr_or_type_list ocomma ')' - { - $$ = Nod(OCALL, $1, nil); - $$.List = $3; - } -| pexpr '(' expr_or_type_list LDDD ocomma ')' - { - $$ = Nod(OCALL, $1, nil); - $$.List = $3; - $$.Isddd = true; - } - -pexpr_no_paren: - LLITERAL - { - $$ = nodlit($1); - } -| name -| pexpr '.' sym - { - if $1.Op == OPACK { - var s *Sym - s = restrictlookup($3.Name, $1.Pkg); - $1.Used = true; - $$ = oldname(s); - break; - } - $$ = Nod(OXDOT, $1, newname($3)); - } -| pexpr '.' '(' expr_or_type ')' - { - $$ = Nod(ODOTTYPE, $1, $4); - } -| pexpr '.' '(' LTYPE ')' - { - $$ = Nod(OTYPESW, nil, $1); - } -| pexpr '[' expr ']' - { - $$ = Nod(OINDEX, $1, $3); - } -| pexpr '[' oexpr ':' oexpr ']' - { - $$ = Nod(OSLICE, $1, Nod(OKEY, $3, $5)); - } -| pexpr '[' oexpr ':' oexpr ':' oexpr ']' - { - if $5 == nil { - Yyerror("middle index required in 3-index slice"); - } - if $7 == nil { - Yyerror("final index required in 3-index slice"); - } - $$ = Nod(OSLICE3, $1, Nod(OKEY, $3, Nod(OKEY, $5, $7))); - } -| pseudocall -| convtype '(' expr ocomma ')' - { - // conversion - $$ = Nod(OCALL, $1, nil); - $$.List = list1($3); - } -| comptype lbrace start_complit braced_keyval_list '}' - { - $$ = $3; - $$.Right = $1; - $$.List = $4; - fixlbrace($2); - } -| pexpr_no_paren '{' start_complit braced_keyval_list '}' - { - $$ = $3; - $$.Right = $1; - $$.List = $4; - } -| '(' expr_or_type ')' '{' start_complit braced_keyval_list '}' - { - Yyerror("cannot parenthesize type in composite literal"); - $$ = $5; - $$.Right = $2; - $$.List = $6; - } -| fnliteral - -start_complit: - { - // composite expression. - // make node early so we get the right line number. - $$ = Nod(OCOMPLIT, nil, nil); - } - -keyval: - expr ':' complitexpr - { - $$ = Nod(OKEY, $1, $3); - } - -bare_complitexpr: - expr - { - // These nodes do not carry line numbers. - // Since a composite literal commonly spans several lines, - // the line number on errors may be misleading. - // Introduce a wrapper node to give the correct line. - $$ = $1; - switch($$.Op) { - case ONAME, ONONAME, OTYPE, OPACK, OLITERAL: - $$ = Nod(OPAREN, $$, nil); - $$.Implicit = true; - } - } -| '{' start_complit braced_keyval_list '}' - { - $$ = $2; - $$.List = $3; - } - -complitexpr: - expr -| '{' start_complit braced_keyval_list '}' - { - $$ = $2; - $$.List = $3; - } - -pexpr: - pexpr_no_paren -| '(' expr_or_type ')' - { - $$ = $2; - - // Need to know on lhs of := whether there are ( ). - // Don't bother with the OPAREN in other cases: - // it's just a waste of memory and time. - switch($$.Op) { - case ONAME, ONONAME, OPACK, OTYPE, OLITERAL, OTYPESW: - $$ = Nod(OPAREN, $$, nil); - } - } - -expr_or_type: - expr -| non_expr_type %prec PreferToRightParen - -name_or_type: - ntype - -lbrace: - LBODY - { - $$ = LBODY; - } -| '{' - { - $$ = '{'; - } - -/* - * names and types - * newname is used before declared - * oldname is used after declared - */ -new_name: - sym - { - if $1 == nil { - $$ = nil; - } else { - $$ = newname($1); - } - } - -dcl_name: - sym - { - $$ = dclname($1); - } - -onew_name: - { - $$ = nil; - } -| new_name - -sym: - LNAME - { - $$ = $1; - // during imports, unqualified non-exported identifiers are from builtinpkg - if importpkg != nil && !exportname($1.Name) { - $$ = Pkglookup($1.Name, builtinpkg); - } - } -| hidden_importsym -| '?' - { - $$ = nil; - } - -hidden_importsym: - '@' LLITERAL '.' LNAME - { - var p *Pkg - - if $2.U.(string) == "" { - p = importpkg; - } else { - if isbadimport($2.U.(string)) { - errorexit(); - } - p = mkpkg($2.U.(string)); - } - $$ = Pkglookup($4.Name, p); - } -| '@' LLITERAL '.' '?' - { - var p *Pkg - - if $2.U.(string) == "" { - p = importpkg; - } else { - if isbadimport($2.U.(string)) { - errorexit(); - } - p = mkpkg($2.U.(string)); - } - $$ = Pkglookup("?", p); - } - -name: - sym %prec NotParen - { - $$ = oldname($1); - if $$.Pack != nil { - $$.Pack.Used = true; - } - } - -labelname: - new_name - -/* - * to avoid parsing conflicts, type is split into - * channel types - * function types - * parenthesized types - * any other type - * the type system makes additional restrictions, - * but those are not implemented in the grammar. - */ -dotdotdot: - LDDD - { - Yyerror("final argument in variadic function missing type"); - $$ = Nod(ODDD, typenod(typ(TINTER)), nil); - } -| LDDD ntype - { - $$ = Nod(ODDD, $2, nil); - } - -ntype: - recvchantype -| fntype -| othertype -| ptrtype -| dotname -| '(' ntype ')' - { - $$ = $2; - } - -non_expr_type: - recvchantype -| fntype -| othertype -| '*' non_expr_type - { - $$ = Nod(OIND, $2, nil); - } - -non_recvchantype: - fntype -| othertype -| ptrtype -| dotname -| '(' ntype ')' - { - $$ = $2; - } - -convtype: - fntype -| othertype - -comptype: - othertype - -fnret_type: - recvchantype -| fntype -| othertype -| ptrtype -| dotname - -dotname: - name -| name '.' sym - { - if $1.Op == OPACK { - var s *Sym - s = restrictlookup($3.Name, $1.Pkg); - $1.Used = true; - $$ = oldname(s); - break; - } - $$ = Nod(OXDOT, $1, newname($3)); - } - -othertype: - '[' oexpr ']' ntype - { - $$ = Nod(OTARRAY, $2, $4); - } -| '[' LDDD ']' ntype - { - // array literal of nelem - $$ = Nod(OTARRAY, Nod(ODDD, nil, nil), $4); - } -| LCHAN non_recvchantype - { - $$ = Nod(OTCHAN, $2, nil); - $$.Etype = Cboth; - } -| LCHAN LCOMM ntype - { - $$ = Nod(OTCHAN, $3, nil); - $$.Etype = Csend; - } -| LMAP '[' ntype ']' ntype - { - $$ = Nod(OTMAP, $3, $5); - } -| structtype -| interfacetype - -ptrtype: - '*' ntype - { - $$ = Nod(OIND, $2, nil); - } - -recvchantype: - LCOMM LCHAN ntype - { - $$ = Nod(OTCHAN, $3, nil); - $$.Etype = Crecv; - } - -structtype: - LSTRUCT lbrace structdcl_list osemi '}' - { - $$ = Nod(OTSTRUCT, nil, nil); - $$.List = $3; - fixlbrace($2); - } -| LSTRUCT lbrace '}' - { - $$ = Nod(OTSTRUCT, nil, nil); - fixlbrace($2); - } - -interfacetype: - LINTERFACE lbrace interfacedcl_list osemi '}' - { - $$ = Nod(OTINTER, nil, nil); - $$.List = $3; - fixlbrace($2); - } -| LINTERFACE lbrace '}' - { - $$ = Nod(OTINTER, nil, nil); - fixlbrace($2); - } - -/* - * function stuff - * all in one place to show how crappy it all is - */ -xfndcl: - LFUNC fndcl fnbody - { - $$ = $2; - if $$ == nil { - break; - } - if noescape && $3 != nil { - Yyerror("can only use //go:noescape with external func implementations"); - } - $$.Nbody = $3; - $$.Func.Endlineno = lineno; - $$.Noescape = noescape; - $$.Func.Nosplit = nosplit; - $$.Func.Nowritebarrier = nowritebarrier; - funcbody($$); - } - -fndcl: - sym '(' oarg_type_list_ocomma ')' fnres - { - var t *Node - - $$ = nil; - $3 = checkarglist($3, 1); - - if $1.Name == "init" { - $1 = renameinit(); - if $3 != nil || $5 != nil { - Yyerror("func init must have no arguments and no return values"); - } - } - if localpkg.Name == "main" && $1.Name == "main" { - if $3 != nil || $5 != nil { - Yyerror("func main must have no arguments and no return values"); - } - } - - t = Nod(OTFUNC, nil, nil); - t.List = $3; - t.Rlist = $5; - - $$ = Nod(ODCLFUNC, nil, nil); - $$.Nname = newfuncname($1); - $$.Nname.Defn = $$; - $$.Nname.Ntype = t; // TODO: check if nname already has an ntype - declare($$.Nname, PFUNC); - - funchdr($$); - } -| '(' oarg_type_list_ocomma ')' sym '(' oarg_type_list_ocomma ')' fnres - { - var rcvr, t *Node - - $$ = nil; - $2 = checkarglist($2, 0); - $6 = checkarglist($6, 1); - - if $2 == nil { - Yyerror("method has no receiver"); - break; - } - if $2.Next != nil { - Yyerror("method has multiple receivers"); - break; - } - rcvr = $2.N; - if rcvr.Op != ODCLFIELD { - Yyerror("bad receiver in method"); - break; - } - - t = Nod(OTFUNC, rcvr, nil); - t.List = $6; - t.Rlist = $8; - - $$ = Nod(ODCLFUNC, nil, nil); - $$.Func.Shortname = newfuncname($4); - $$.Nname = methodname1($$.Func.Shortname, rcvr.Right); - $$.Nname.Defn = $$; - $$.Nname.Ntype = t; - $$.Nname.Nointerface = nointerface; - declare($$.Nname, PFUNC); - - funchdr($$); - } - -hidden_fndcl: - hidden_pkg_importsym '(' ohidden_funarg_list ')' ohidden_funres - { - var s *Sym - var t *Type - - $$ = nil; - - s = $1; - t = functype(nil, $3, $5); - - importsym(s, ONAME); - if s.Def != nil && s.Def.Op == ONAME { - if Eqtype(t, s.Def.Type) { - dclcontext = PDISCARD; // since we skip funchdr below - break; - } - Yyerror("inconsistent definition for func %v during import\n\t%v\n\t%v", s, s.Def.Type, t); - } - - $$ = newfuncname(s); - $$.Type = t; - declare($$, PFUNC); - - funchdr($$); - } -| '(' hidden_funarg_list ')' sym '(' ohidden_funarg_list ')' ohidden_funres - { - $$ = methodname1(newname($4), $2.N.Right); - $$.Type = functype($2.N, $6, $8); - - checkwidth($$.Type); - addmethod($4, $$.Type, false, nointerface); - nointerface = false - funchdr($$); - - // inl.C's inlnode in on a dotmeth node expects to find the inlineable body as - // (dotmeth's type).Nname.Inl, and dotmeth's type has been pulled - // out by typecheck's lookdot as this $$.ttype. So by providing - // this back link here we avoid special casing there. - $$.Type.Nname = $$; - } - -fntype: - LFUNC '(' oarg_type_list_ocomma ')' fnres - { - $3 = checkarglist($3, 1); - $$ = Nod(OTFUNC, nil, nil); - $$.List = $3; - $$.Rlist = $5; - } - -fnbody: - { - $$ = nil; - } -| '{' stmt_list '}' - { - $$ = $2; - if $$ == nil { - $$ = list1(Nod(OEMPTY, nil, nil)); - } - } - -fnres: - %prec NotParen - { - $$ = nil; - } -| fnret_type - { - $$ = list1(Nod(ODCLFIELD, nil, $1)); - } -| '(' oarg_type_list_ocomma ')' - { - $2 = checkarglist($2, 0); - $$ = $2; - } - -fnlitdcl: - fntype - { - closurehdr($1); - } - -fnliteral: - fnlitdcl lbrace stmt_list '}' - { - $$ = closurebody($3); - fixlbrace($2); - } -| fnlitdcl error - { - $$ = closurebody(nil); - } - -/* - * lists of things - * note that they are left recursive - * to conserve yacc stack. they need to - * be reversed to interpret correctly - */ -xdcl_list: - { - $$ = nil; - } -| xdcl_list xdcl ';' - { - $$ = concat($1, $2); - if nsyntaxerrors == 0 { - testdclstack(); - } - nointerface = false - noescape = false - nosplit = false - nowritebarrier = false - } - -vardcl_list: - vardcl -| vardcl_list ';' vardcl - { - $$ = concat($1, $3); - } - -constdcl_list: - constdcl1 -| constdcl_list ';' constdcl1 - { - $$ = concat($1, $3); - } - -typedcl_list: - typedcl - { - $$ = list1($1); - } -| typedcl_list ';' typedcl - { - $$ = list($1, $3); - } - -structdcl_list: - structdcl -| structdcl_list ';' structdcl - { - $$ = concat($1, $3); - } - -interfacedcl_list: - interfacedcl - { - $$ = list1($1); - } -| interfacedcl_list ';' interfacedcl - { - $$ = list($1, $3); - } - -structdcl: - new_name_list ntype oliteral - { - var l *NodeList - - var n *Node - l = $1; - if l == nil { - // ? symbol, during import (list1(nil) == nil) - n = $2; - if n.Op == OIND { - n = n.Left; - } - n = embedded(n.Sym, importpkg); - n.Right = $2; - n.Val = $3; - $$ = list1(n); - break; - } - - for l=$1; l != nil; l=l.Next { - l.N = Nod(ODCLFIELD, l.N, $2); - l.N.Val = $3; - } - } -| embed oliteral - { - $1.Val = $2; - $$ = list1($1); - } -| '(' embed ')' oliteral - { - $2.Val = $4; - $$ = list1($2); - Yyerror("cannot parenthesize embedded type"); - } -| '*' embed oliteral - { - $2.Right = Nod(OIND, $2.Right, nil); - $2.Val = $3; - $$ = list1($2); - } -| '(' '*' embed ')' oliteral - { - $3.Right = Nod(OIND, $3.Right, nil); - $3.Val = $5; - $$ = list1($3); - Yyerror("cannot parenthesize embedded type"); - } -| '*' '(' embed ')' oliteral - { - $3.Right = Nod(OIND, $3.Right, nil); - $3.Val = $5; - $$ = list1($3); - Yyerror("cannot parenthesize embedded type"); - } - -packname: - LNAME - { - var n *Node - - $$ = $1; - n = oldname($1); - if n.Pack != nil { - n.Pack.Used = true; - } - } -| LNAME '.' sym - { - var pkg *Pkg - - if $1.Def == nil || $1.Def.Op != OPACK { - Yyerror("%v is not a package", $1); - pkg = localpkg; - } else { - $1.Def.Used = true; - pkg = $1.Def.Pkg; - } - $$ = restrictlookup($3.Name, pkg); - } - -embed: - packname - { - $$ = embedded($1, localpkg); - } - -interfacedcl: - new_name indcl - { - $$ = Nod(ODCLFIELD, $1, $2); - ifacedcl($$); - } -| packname - { - $$ = Nod(ODCLFIELD, nil, oldname($1)); - } -| '(' packname ')' - { - $$ = Nod(ODCLFIELD, nil, oldname($2)); - Yyerror("cannot parenthesize embedded type"); - } - -indcl: - '(' oarg_type_list_ocomma ')' fnres - { - // without func keyword - $2 = checkarglist($2, 1); - $$ = Nod(OTFUNC, fakethis(), nil); - $$.List = $2; - $$.Rlist = $4; - } - -/* - * function arguments. - */ -arg_type: - name_or_type -| sym name_or_type - { - $$ = Nod(ONONAME, nil, nil); - $$.Sym = $1; - $$ = Nod(OKEY, $$, $2); - } -| sym dotdotdot - { - $$ = Nod(ONONAME, nil, nil); - $$.Sym = $1; - $$ = Nod(OKEY, $$, $2); - } -| dotdotdot - -arg_type_list: - arg_type - { - $$ = list1($1); - } -| arg_type_list ',' arg_type - { - $$ = list($1, $3); - } - -oarg_type_list_ocomma: - { - $$ = nil; - } -| arg_type_list ocomma - { - $$ = $1; - } - -/* - * statement - */ -stmt: - { - $$ = nil; - } -| compound_stmt -| common_dcl - { - $$ = liststmt($1); - } -| non_dcl_stmt -| error - { - $$ = nil; - } - -non_dcl_stmt: - simple_stmt -| for_stmt -| switch_stmt -| select_stmt -| if_stmt -| labelname ':' - { - $1 = Nod(OLABEL, $1, nil); - $1.Sym = dclstack; // context, for goto restrictions - } - stmt - { - var l *NodeList - - $1.Defn = $4; - l = list1($1); - if $4 != nil { - l = list(l, $4); - } - $$ = liststmt(l); - } -| LFALL - { - // will be converted to OFALL - $$ = Nod(OXFALL, nil, nil); - $$.Xoffset = int64(block); - } -| LBREAK onew_name - { - $$ = Nod(OBREAK, $2, nil); - } -| LCONTINUE onew_name - { - $$ = Nod(OCONTINUE, $2, nil); - } -| LGO pseudocall - { - $$ = Nod(OPROC, $2, nil); - } -| LDEFER pseudocall - { - $$ = Nod(ODEFER, $2, nil); - } -| LGOTO new_name - { - $$ = Nod(OGOTO, $2, nil); - $$.Sym = dclstack; // context, for goto restrictions - } -| LRETURN oexpr_list - { - $$ = Nod(ORETURN, nil, nil); - $$.List = $2; - if $$.List == nil && Curfn != nil { - var l *NodeList - - for l=Curfn.Func.Dcl; l != nil; l=l.Next { - if l.N.Class == PPARAM { - continue; - } - if l.N.Class != PPARAMOUT { - break; - } - if l.N.Sym.Def != l.N { - Yyerror("%s is shadowed during return", l.N.Sym.Name); - } - } - } - } - -stmt_list: - stmt - { - $$ = nil; - if $1 != nil { - $$ = list1($1); - } - } -| stmt_list ';' stmt - { - $$ = $1; - if $3 != nil { - $$ = list($$, $3); - } - } - -new_name_list: - new_name - { - $$ = list1($1); - } -| new_name_list ',' new_name - { - $$ = list($1, $3); - } - -dcl_name_list: - dcl_name - { - $$ = list1($1); - } -| dcl_name_list ',' dcl_name - { - $$ = list($1, $3); - } - -expr_list: - expr - { - $$ = list1($1); - } -| expr_list ',' expr - { - $$ = list($1, $3); - } - -expr_or_type_list: - expr_or_type - { - $$ = list1($1); - } -| expr_or_type_list ',' expr_or_type - { - $$ = list($1, $3); - } - -/* - * list of combo of keyval and val - */ -keyval_list: - keyval - { - $$ = list1($1); - } -| bare_complitexpr - { - $$ = list1($1); - } -| keyval_list ',' keyval - { - $$ = list($1, $3); - } -| keyval_list ',' bare_complitexpr - { - $$ = list($1, $3); - } - -braced_keyval_list: - { - $$ = nil; - } -| keyval_list ocomma - { - $$ = $1; - } - -/* - * optional things - */ -osemi: -| ';' - -ocomma: -| ',' - -oexpr: - { - $$ = nil; - } -| expr - -oexpr_list: - { - $$ = nil; - } -| expr_list - -osimple_stmt: - { - $$ = nil; - } -| simple_stmt - -ohidden_funarg_list: - { - $$ = nil; - } -| hidden_funarg_list - -ohidden_structdcl_list: - { - $$ = nil; - } -| hidden_structdcl_list - -ohidden_interfacedcl_list: - { - $$ = nil; - } -| hidden_interfacedcl_list - -oliteral: - { - $$.Ctype = CTxxx; - } -| LLITERAL - -/* - * import syntax from package header - */ -hidden_import: - LIMPORT LNAME LLITERAL ';' - { - importimport($2, $3.U.(string)); - } -| LVAR hidden_pkg_importsym hidden_type ';' - { - importvar($2, $3); - } -| LCONST hidden_pkg_importsym '=' hidden_constant ';' - { - importconst($2, Types[TIDEAL], $4); - } -| LCONST hidden_pkg_importsym hidden_type '=' hidden_constant ';' - { - importconst($2, $3, $5); - } -| LTYPE hidden_pkgtype hidden_type ';' - { - importtype($2, $3); - } -| LFUNC hidden_fndcl fnbody ';' - { - if $2 == nil { - dclcontext = PEXTERN; // since we skip the funcbody below - break; - } - - $2.Func.Inl = $3; - - funcbody($2); - importlist = list(importlist, $2); - - if Debug['E'] > 0 { - fmt.Printf("import [%q] func %v \n", importpkg.Path, $2) - if Debug['m'] > 2 && $2.Func.Inl != nil { - fmt.Printf("inl body:%v\n", $2.Func.Inl) - } - } - } - -hidden_pkg_importsym: - hidden_importsym - { - $$ = $1; - structpkg = $$.Pkg; - } - -hidden_pkgtype: - hidden_pkg_importsym - { - $$ = pkgtype($1); - importsym($1, OTYPE); - } - -/* - * importing types - */ - -hidden_type: - hidden_type_misc -| hidden_type_recv_chan -| hidden_type_func - -hidden_type_non_recv_chan: - hidden_type_misc -| hidden_type_func - -hidden_type_misc: - hidden_importsym - { - $$ = pkgtype($1); - } -| LNAME - { - // predefined name like uint8 - $1 = Pkglookup($1.Name, builtinpkg); - if $1.Def == nil || $1.Def.Op != OTYPE { - Yyerror("%s is not a type", $1.Name); - $$ = nil; - } else { - $$ = $1.Def.Type; - } - } -| '[' ']' hidden_type - { - $$ = aindex(nil, $3); - } -| '[' LLITERAL ']' hidden_type - { - $$ = aindex(nodlit($2), $4); - } -| LMAP '[' hidden_type ']' hidden_type - { - $$ = maptype($3, $5); - } -| LSTRUCT '{' ohidden_structdcl_list '}' - { - $$ = tostruct($3); - } -| LINTERFACE '{' ohidden_interfacedcl_list '}' - { - $$ = tointerface($3); - } -| '*' hidden_type - { - $$ = Ptrto($2); - } -| LCHAN hidden_type_non_recv_chan - { - $$ = typ(TCHAN); - $$.Type = $2; - $$.Chan = Cboth; - } -| LCHAN '(' hidden_type_recv_chan ')' - { - $$ = typ(TCHAN); - $$.Type = $3; - $$.Chan = Cboth; - } -| LCHAN LCOMM hidden_type - { - $$ = typ(TCHAN); - $$.Type = $3; - $$.Chan = Csend; - } - -hidden_type_recv_chan: - LCOMM LCHAN hidden_type - { - $$ = typ(TCHAN); - $$.Type = $3; - $$.Chan = Crecv; - } - -hidden_type_func: - LFUNC '(' ohidden_funarg_list ')' ohidden_funres - { - $$ = functype(nil, $3, $5); - } - -hidden_funarg: - sym hidden_type oliteral - { - $$ = Nod(ODCLFIELD, nil, typenod($2)); - if $1 != nil { - $$.Left = newname($1); - } - $$.Val = $3; - } -| sym LDDD hidden_type oliteral - { - var t *Type - - t = typ(TARRAY); - t.Bound = -1; - t.Type = $3; - - $$ = Nod(ODCLFIELD, nil, typenod(t)); - if $1 != nil { - $$.Left = newname($1); - } - $$.Isddd = true; - $$.Val = $4; - } - -hidden_structdcl: - sym hidden_type oliteral - { - var s *Sym - var p *Pkg - - if $1 != nil && $1.Name != "?" { - $$ = Nod(ODCLFIELD, newname($1), typenod($2)); - $$.Val = $3; - } else { - s = $2.Sym; - if s == nil && Isptr[$2.Etype] { - s = $2.Type.Sym; - } - p = importpkg; - if $1 != nil { - p = $1.Pkg; - } - $$ = embedded(s, p); - $$.Right = typenod($2); - $$.Val = $3; - } - } - -hidden_interfacedcl: - sym '(' ohidden_funarg_list ')' ohidden_funres - { - $$ = Nod(ODCLFIELD, newname($1), typenod(functype(fakethis(), $3, $5))); - } -| hidden_type - { - $$ = Nod(ODCLFIELD, nil, typenod($1)); - } - -ohidden_funres: - { - $$ = nil; - } -| hidden_funres - -hidden_funres: - '(' ohidden_funarg_list ')' - { - $$ = $2; - } -| hidden_type - { - $$ = list1(Nod(ODCLFIELD, nil, typenod($1))); - } - -/* - * importing constants - */ - -hidden_literal: - LLITERAL - { - $$ = nodlit($1); - } -| '-' LLITERAL - { - $$ = nodlit($2); - switch($$.Val.Ctype){ - case CTINT, CTRUNE: - mpnegfix($$.Val.U.(*Mpint)); - break; - case CTFLT: - mpnegflt($$.Val.U.(*Mpflt)); - break; - case CTCPLX: - mpnegflt(&$$.Val.U.(*Mpcplx).Real); - mpnegflt(&$$.Val.U.(*Mpcplx).Imag); - break; - default: - Yyerror("bad negated constant"); - } - } -| sym - { - $$ = oldname(Pkglookup($1.Name, builtinpkg)); - if $$.Op != OLITERAL { - Yyerror("bad constant %v", $$.Sym); - } - } - -hidden_constant: - hidden_literal -| '(' hidden_literal '+' hidden_literal ')' - { - if $2.Val.Ctype == CTRUNE && $4.Val.Ctype == CTINT { - $$ = $2; - mpaddfixfix($2.Val.U.(*Mpint), $4.Val.U.(*Mpint), 0); - break; - } - $4.Val.U.(*Mpcplx).Real = $4.Val.U.(*Mpcplx).Imag; - Mpmovecflt(&$4.Val.U.(*Mpcplx).Imag, 0.0); - $$ = nodcplxlit($2.Val, $4.Val); - } - -hidden_import_list: -| hidden_import_list hidden_import - -hidden_funarg_list: - hidden_funarg - { - $$ = list1($1); - } -| hidden_funarg_list ',' hidden_funarg - { - $$ = list($1, $3); - } - -hidden_structdcl_list: - hidden_structdcl - { - $$ = list1($1); - } -| hidden_structdcl_list ';' hidden_structdcl - { - $$ = list($1, $3); - } - -hidden_interfacedcl_list: - hidden_interfacedcl - { - $$ = list1($1); - } -| hidden_interfacedcl_list ';' hidden_interfacedcl - { - $$ = list($1, $3); - } - -%% -func fixlbrace(lbr int) { - // If the opening brace was an LBODY, - // set up for another one now that we're done. - // See comment in lex.C about loophack. - if lbr == LBODY { - loophack = 1 - } -} diff --git a/src/cmd/internal/gc/gsubr.go b/src/cmd/internal/gc/gsubr.go deleted file mode 100644 index 5ec4587e74..0000000000 --- a/src/cmd/internal/gc/gsubr.go +++ /dev/null @@ -1,827 +0,0 @@ -// Derived from Inferno utils/6c/txt.c -// http://code.google.com/p/inferno-os/source/browse/utils/6c/txt.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package gc - -import ( - "cmd/internal/obj" - "fmt" - "runtime" - "strings" -) - -var ddumped int - -var dfirst *obj.Prog - -var dpc *obj.Prog - -/* - * Is this node a memory operand? - */ -func Ismem(n *Node) bool { - switch n.Op { - case OITAB, - OSPTR, - OLEN, - OCAP, - OINDREG, - ONAME, - OPARAM, - OCLOSUREVAR: - return true - - case OADDR: - return Thearch.Thechar == '6' || Thearch.Thechar == '9' // because 6g uses PC-relative addressing; TODO(rsc): not sure why 9g too - } - - return false -} - -func Samereg(a *Node, b *Node) bool { - if a == nil || b == nil { - return false - } - if a.Op != OREGISTER { - return false - } - if b.Op != OREGISTER { - return false - } - if a.Reg != b.Reg { - return false - } - return true -} - -func Gbranch(as int, t *Type, likely int) *obj.Prog { - p := Prog(as) - p.To.Type = obj.TYPE_BRANCH - p.To.Val = nil - if as != obj.AJMP && likely != 0 && Thearch.Thechar != '9' && Thearch.Thechar != '7' { - p.From.Type = obj.TYPE_CONST - p.From.Offset = int64(obj.Bool2int(likely > 0)) - } - - if Debug['g'] != 0 { - fmt.Printf("%v\n", p) - } - - return p -} - -func Prog(as int) *obj.Prog { - var p *obj.Prog - - if as == obj.ADATA || as == obj.AGLOBL { - if ddumped != 0 { - Fatal("already dumped data") - } - if dpc == nil { - dpc = Ctxt.NewProg() - dfirst = dpc - } - - p = dpc - dpc = Ctxt.NewProg() - p.Link = dpc - } else { - p = Pc - Pc = Ctxt.NewProg() - Clearp(Pc) - p.Link = Pc - } - - if lineno == 0 { - if Debug['K'] != 0 { - Warn("prog: line 0") - } - } - - p.As = int16(as) - p.Lineno = lineno - return p -} - -func Nodreg(n *Node, t *Type, r int) { - if t == nil { - Fatal("nodreg: t nil") - } - - *n = Node{} - n.Op = OREGISTER - n.Addable = true - ullmancalc(n) - n.Reg = int16(r) - n.Type = t -} - -func Nodindreg(n *Node, t *Type, r int) { - Nodreg(n, t, r) - n.Op = OINDREG -} - -func Afunclit(a *obj.Addr, n *Node) { - if a.Type == obj.TYPE_ADDR && a.Name == obj.NAME_EXTERN { - a.Type = obj.TYPE_MEM - a.Sym = Linksym(n.Sym) - } -} - -func Clearp(p *obj.Prog) { - obj.Nopout(p) - p.As = obj.AEND - p.Pc = int64(pcloc) - pcloc++ -} - -func dumpdata() { - ddumped = 1 - if dfirst == nil { - return - } - newplist() - *Pc = *dfirst - Pc = dpc - Clearp(Pc) -} - -// Fixup instructions after allocauto (formerly compactframe) has moved all autos around. -func fixautoused(p *obj.Prog) { - for lp := &p; ; { - p = *lp - if p == nil { - break - } - if p.As == obj.ATYPE && p.From.Node != nil && p.From.Name == obj.NAME_AUTO && !((p.From.Node).(*Node)).Used { - *lp = p.Link - continue - } - - if (p.As == obj.AVARDEF || p.As == obj.AVARKILL) && p.To.Node != nil && !((p.To.Node).(*Node)).Used { - // Cannot remove VARDEF instruction, because - unlike TYPE handled above - - // VARDEFs are interspersed with other code, and a jump might be using the - // VARDEF as a target. Replace with a no-op instead. A later pass will remove - // the no-ops. - obj.Nopout(p) - - continue - } - - if p.From.Name == obj.NAME_AUTO && p.From.Node != nil { - p.From.Offset += ((p.From.Node).(*Node)).Stkdelta - } - - if p.To.Name == obj.NAME_AUTO && p.To.Node != nil { - p.To.Offset += ((p.To.Node).(*Node)).Stkdelta - } - - lp = &p.Link - } -} - -func ggloblnod(nam *Node) { - p := Thearch.Gins(obj.AGLOBL, nam, nil) - p.Lineno = nam.Lineno - p.From.Sym.Gotype = Linksym(ngotype(nam)) - p.To.Sym = nil - p.To.Type = obj.TYPE_CONST - p.To.Offset = nam.Type.Width - if nam.Name.Readonly { - p.From3.Offset = obj.RODATA - } - if nam.Type != nil && !haspointers(nam.Type) { - p.From3.Offset |= obj.NOPTR - } -} - -func ggloblsym(s *Sym, width int32, flags int16) { - p := Thearch.Gins(obj.AGLOBL, nil, nil) - p.From.Type = obj.TYPE_MEM - p.From.Name = obj.NAME_EXTERN - p.From.Sym = Linksym(s) - if flags&obj.LOCAL != 0 { - p.From.Sym.Local = true - flags &= ^obj.LOCAL - } - p.To.Type = obj.TYPE_CONST - p.To.Offset = int64(width) - p.From3.Offset = int64(flags) -} - -func gjmp(to *obj.Prog) *obj.Prog { - p := Gbranch(obj.AJMP, nil, 0) - if to != nil { - Patch(p, to) - } - return p -} - -func gtrack(s *Sym) { - p := Thearch.Gins(obj.AUSEFIELD, nil, nil) - p.From.Type = obj.TYPE_MEM - p.From.Name = obj.NAME_EXTERN - p.From.Sym = Linksym(s) -} - -func gused(n *Node) { - Thearch.Gins(obj.ANOP, n, nil) // used -} - -func Isfat(t *Type) bool { - if t != nil { - switch t.Etype { - case TSTRUCT, TARRAY, TSTRING, - TINTER: // maybe remove later - return true - } - } - - return false -} - -// Sweep the prog list to mark any used nodes. -func markautoused(p *obj.Prog) { - for ; p != nil; p = p.Link { - if p.As == obj.ATYPE || p.As == obj.AVARDEF || p.As == obj.AVARKILL { - continue - } - - if p.From.Node != nil { - ((p.From.Node).(*Node)).Used = true - } - - if p.To.Node != nil { - ((p.To.Node).(*Node)).Used = true - } - } -} - -// Naddr rewrites a to refer to n. -// It assumes that a is zeroed on entry. -func Naddr(a *obj.Addr, n *Node) { - if n == nil { - return - } - - if n.Type != nil && n.Type.Etype != TIDEAL { - // TODO(rsc): This is undone by the selective clearing of width below, - // to match architectures that were not as aggressive in setting width - // during naddr. Those widths must be cleared to avoid triggering - // failures in gins when it detects real but heretofore latent (and one - // hopes innocuous) type mismatches. - // The type mismatches should be fixed and the clearing below removed. - dowidth(n.Type) - - a.Width = n.Type.Width - } - - switch n.Op { - default: - a := a // copy to let escape into Ctxt.Dconv - Debug['h'] = 1 - Dump("naddr", n) - Fatal("naddr: bad %v %v", Oconv(int(n.Op), 0), Ctxt.Dconv(a)) - - case OREGISTER: - a.Type = obj.TYPE_REG - a.Reg = n.Reg - a.Sym = nil - if Thearch.Thechar == '8' { // TODO(rsc): Never clear a->width. - a.Width = 0 - } - - case OINDREG: - a.Type = obj.TYPE_MEM - a.Reg = n.Reg - a.Sym = Linksym(n.Sym) - a.Offset = n.Xoffset - if a.Offset != int64(int32(a.Offset)) { - Yyerror("offset %d too large for OINDREG", a.Offset) - } - if Thearch.Thechar == '8' { // TODO(rsc): Never clear a->width. - a.Width = 0 - } - - // n->left is PHEAP ONAME for stack parameter. - // compute address of actual parameter on stack. - case OPARAM: - a.Etype = Simtype[n.Left.Type.Etype] - - a.Width = n.Left.Type.Width - a.Offset = n.Xoffset - a.Sym = Linksym(n.Left.Sym) - a.Type = obj.TYPE_MEM - a.Name = obj.NAME_PARAM - a.Node = n.Left.Orig - - case OCLOSUREVAR: - if !Curfn.Func.Needctxt { - Fatal("closurevar without needctxt") - } - a.Type = obj.TYPE_MEM - a.Reg = int16(Thearch.REGCTXT) - a.Sym = nil - a.Offset = n.Xoffset - - case OCFUNC: - Naddr(a, n.Left) - a.Sym = Linksym(n.Left.Sym) - - case ONAME: - a.Etype = 0 - if n.Type != nil { - a.Etype = Simtype[n.Type.Etype] - } - a.Offset = n.Xoffset - s := n.Sym - a.Node = n.Orig - - //if(a->node >= (Node*)&n) - // fatal("stack node"); - if s == nil { - s = Lookup(".noname") - } - if n.Name.Method { - if n.Type != nil { - if n.Type.Sym != nil { - if n.Type.Sym.Pkg != nil { - s = Pkglookup(s.Name, n.Type.Sym.Pkg) - } - } - } - } - - a.Type = obj.TYPE_MEM - switch n.Class { - default: - Fatal("naddr: ONAME class %v %d\n", n.Sym, n.Class) - - case PEXTERN: - a.Name = obj.NAME_EXTERN - - case PAUTO: - a.Name = obj.NAME_AUTO - - case PPARAM, PPARAMOUT: - a.Name = obj.NAME_PARAM - - case PFUNC: - a.Name = obj.NAME_EXTERN - a.Type = obj.TYPE_ADDR - a.Width = int64(Widthptr) - s = funcsym(s) - } - - a.Sym = Linksym(s) - - case OLITERAL: - if Thearch.Thechar == '8' { - a.Width = 0 - } - switch n.Val.Ctype { - default: - Fatal("naddr: const %v", Tconv(n.Type, obj.FmtLong)) - - case CTFLT: - a.Type = obj.TYPE_FCONST - a.Val = mpgetflt(n.Val.U.(*Mpflt)) - - case CTINT, CTRUNE: - a.Sym = nil - a.Type = obj.TYPE_CONST - a.Offset = Mpgetfix(n.Val.U.(*Mpint)) - - case CTSTR: - datagostring(n.Val.U.(string), a) - - case CTBOOL: - a.Sym = nil - a.Type = obj.TYPE_CONST - a.Offset = int64(obj.Bool2int(n.Val.U.(bool))) - - case CTNIL: - a.Sym = nil - a.Type = obj.TYPE_CONST - a.Offset = 0 - } - - case OADDR: - Naddr(a, n.Left) - a.Etype = uint8(Tptr) - if Thearch.Thechar != '5' && Thearch.Thechar != '7' && Thearch.Thechar != '9' { // TODO(rsc): Do this even for arm, ppc64. - a.Width = int64(Widthptr) - } - if a.Type != obj.TYPE_MEM { - a := a // copy to let escape into Ctxt.Dconv - Fatal("naddr: OADDR %v (from %v)", Ctxt.Dconv(a), Oconv(int(n.Left.Op), 0)) - } - a.Type = obj.TYPE_ADDR - - // itable of interface value - case OITAB: - Naddr(a, n.Left) - - if a.Type == obj.TYPE_CONST && a.Offset == 0 { - break // itab(nil) - } - a.Etype = uint8(Tptr) - a.Width = int64(Widthptr) - - // pointer in a string or slice - case OSPTR: - Naddr(a, n.Left) - - if a.Type == obj.TYPE_CONST && a.Offset == 0 { - break // ptr(nil) - } - a.Etype = Simtype[Tptr] - a.Offset += int64(Array_array) - a.Width = int64(Widthptr) - - // len of string or slice - case OLEN: - Naddr(a, n.Left) - - if a.Type == obj.TYPE_CONST && a.Offset == 0 { - break // len(nil) - } - a.Etype = Simtype[TUINT] - a.Offset += int64(Array_nel) - if Thearch.Thechar != '5' { // TODO(rsc): Do this even on arm. - a.Width = int64(Widthint) - } - - // cap of string or slice - case OCAP: - Naddr(a, n.Left) - - if a.Type == obj.TYPE_CONST && a.Offset == 0 { - break // cap(nil) - } - a.Etype = Simtype[TUINT] - a.Offset += int64(Array_cap) - if Thearch.Thechar != '5' { // TODO(rsc): Do this even on arm. - a.Width = int64(Widthint) - } - } - return -} - -func newplist() *obj.Plist { - pl := obj.Linknewplist(Ctxt) - - Pc = Ctxt.NewProg() - Clearp(Pc) - pl.Firstpc = Pc - - return pl -} - -func nodarg(t *Type, fp int) *Node { - var n *Node - - // entire argument struct, not just one arg - if t.Etype == TSTRUCT && t.Funarg != 0 { - n = Nod(ONAME, nil, nil) - n.Sym = Lookup(".args") - n.Type = t - var savet Iter - first := Structfirst(&savet, &t) - if first == nil { - Fatal("nodarg: bad struct") - } - if first.Width == BADWIDTH { - Fatal("nodarg: offset not computed for %v", t) - } - n.Xoffset = first.Width - n.Addable = true - goto fp - } - - if t.Etype != TFIELD { - Fatal("nodarg: not field %v", t) - } - - if fp == 1 { - var n *Node - for l := Curfn.Func.Dcl; l != nil; l = l.Next { - n = l.N - if (n.Class == PPARAM || n.Class == PPARAMOUT) && !isblanksym(t.Sym) && n.Sym == t.Sym { - return n - } - } - } - - n = Nod(ONAME, nil, nil) - n.Type = t.Type - n.Sym = t.Sym - - if t.Width == BADWIDTH { - Fatal("nodarg: offset not computed for %v", t) - } - n.Xoffset = t.Width - n.Addable = true - n.Orig = t.Nname - - // Rewrite argument named _ to __, - // or else the assignment to _ will be - // discarded during code generation. -fp: - if isblank(n) { - n.Sym = Lookup("__") - } - - switch fp { - case 0: // output arg - n.Op = OINDREG - - n.Reg = int16(Thearch.REGSP) - if HasLinkRegister() { - n.Xoffset += int64(Ctxt.Arch.Ptrsize) - } - - case 1: // input arg - n.Class = PPARAM - - case 2: // offset output arg - Fatal("shouldn't be used") - } - - n.Typecheck = 1 - return n -} - -func Patch(p *obj.Prog, to *obj.Prog) { - if p.To.Type != obj.TYPE_BRANCH { - Fatal("patch: not a branch") - } - p.To.Val = to - p.To.Offset = to.Pc -} - -func unpatch(p *obj.Prog) *obj.Prog { - if p.To.Type != obj.TYPE_BRANCH { - Fatal("unpatch: not a branch") - } - q, _ := p.To.Val.(*obj.Prog) - p.To.Val = nil - p.To.Offset = 0 - return q -} - -var reg [100]int // count of references to reg -var regstk [100][]byte // allocation sites, when -v is given - -func ginit() { - for r := range reg { - reg[r] = 1 - } - - for r := Thearch.REGMIN; r <= Thearch.REGMAX; r++ { - reg[r-Thearch.REGMIN] = 0 - } - for r := Thearch.FREGMIN; r <= Thearch.FREGMAX; r++ { - reg[r-Thearch.REGMIN] = 0 - } - - for _, r := range Thearch.ReservedRegs { - reg[r-Thearch.REGMIN] = 1 - } -} - -func gclean() { - for _, r := range Thearch.ReservedRegs { - reg[r-Thearch.REGMIN]-- - } - - for r := Thearch.REGMIN; r <= Thearch.REGMAX; r++ { - n := reg[r-Thearch.REGMIN] - if n != 0 { - 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 { - if Debug['v'] != 0 { - Regdump() - } - Yyerror("reg %v left allocated", obj.Rconv(r)) - } - } -} - -func Anyregalloc() bool { - n := 0 - for r := Thearch.REGMIN; r <= Thearch.REGMAX; r++ { - if reg[r-Thearch.REGMIN] == 0 { - n++ - } - } - return n > len(Thearch.ReservedRegs) -} - -/* - * allocate register of type t, leave in n. - * if o != N, o may be reusable register. - * caller must Regfree(n). - */ -func Regalloc(n *Node, t *Type, o *Node) { - if t == nil { - Fatal("regalloc: t nil") - } - et := int(Simtype[t.Etype]) - if Ctxt.Arch.Regsize == 4 && (et == TINT64 || et == TUINT64) { - Fatal("regalloc 64bit") - } - - var i int -Switch: - switch et { - default: - Fatal("regalloc: unknown type %v", t) - - case TINT8, TUINT8, TINT16, TUINT16, TINT32, TUINT32, TINT64, TUINT64, TPTR32, TPTR64, TBOOL: - if o != nil && o.Op == OREGISTER { - i = int(o.Reg) - if Thearch.REGMIN <= i && i <= Thearch.REGMAX { - break Switch - } - } - for i = Thearch.REGMIN; i <= Thearch.REGMAX; i++ { - if reg[i-Thearch.REGMIN] == 0 { - break Switch - } - } - Flusherrors() - Regdump() - Fatal("out of fixed registers") - - case TFLOAT32, TFLOAT64: - if Thearch.Use387 { - i = Thearch.FREGMIN // x86.REG_F0 - break Switch - } - if o != nil && o.Op == OREGISTER { - i = int(o.Reg) - if Thearch.FREGMIN <= i && i <= Thearch.FREGMAX { - break Switch - } - } - for i = Thearch.FREGMIN; i <= Thearch.FREGMAX; i++ { - if reg[i-Thearch.REGMIN] == 0 { // note: REGMIN, not FREGMIN - break Switch - } - } - Flusherrors() - Regdump() - Fatal("out of floating registers") - - case TCOMPLEX64, TCOMPLEX128: - Tempname(n, t) - return - } - - ix := i - Thearch.REGMIN - if reg[ix] == 0 && Debug['v'] > 0 { - if regstk[ix] == nil { - regstk[ix] = make([]byte, 4096) - } - stk := regstk[ix] - n := runtime.Stack(stk[:cap(stk)], false) - regstk[ix] = stk[:n] - } - reg[ix]++ - Nodreg(n, t, i) -} - -func Regfree(n *Node) { - if n.Op == ONAME { - return - } - if n.Op != OREGISTER && n.Op != OINDREG { - Fatal("regfree: not a register") - } - i := int(n.Reg) - if i == Thearch.REGSP { - return - } - switch { - case Thearch.REGMIN <= i && i <= Thearch.REGMAX, - Thearch.FREGMIN <= i && i <= Thearch.FREGMAX: - // ok - default: - Fatal("regfree: reg out of range") - } - - i -= Thearch.REGMIN - if reg[i] <= 0 { - Fatal("regfree: reg not allocated") - } - reg[i]-- - if reg[i] == 0 { - regstk[i] = regstk[i][:0] - } -} - -// Reginuse reports whether r is in use. -func Reginuse(r int) bool { - switch { - case Thearch.REGMIN <= r && r <= Thearch.REGMAX, - Thearch.FREGMIN <= r && r <= Thearch.FREGMAX: - // ok - default: - Fatal("reginuse: reg out of range") - } - - return reg[r-Thearch.REGMIN] > 0 -} - -// Regrealloc(n) undoes the effect of Regfree(n), -// so that a register can be given up but then reclaimed. -func Regrealloc(n *Node) { - if n.Op != OREGISTER && n.Op != OINDREG { - Fatal("regrealloc: not a register") - } - i := int(n.Reg) - if i == Thearch.REGSP { - return - } - switch { - case Thearch.REGMIN <= i && i <= Thearch.REGMAX, - Thearch.FREGMIN <= i && i <= Thearch.FREGMAX: - // ok - default: - Fatal("regrealloc: reg out of range") - } - - i -= Thearch.REGMIN - if reg[i] == 0 && Debug['v'] > 0 { - if regstk[i] == nil { - regstk[i] = make([]byte, 4096) - } - stk := regstk[i] - n := runtime.Stack(stk[:cap(stk)], false) - regstk[i] = stk[:n] - } - reg[i]++ -} - -func Regdump() { - if Debug['v'] == 0 { - fmt.Printf("run compiler with -v for register allocation sites\n") - return - } - - dump := func(r int) { - stk := regstk[r-Thearch.REGMIN] - if len(stk) == 0 { - return - } - fmt.Printf("reg %v allocated at:\n", obj.Rconv(r)) - fmt.Printf("\t%s\n", strings.Replace(strings.TrimSpace(string(stk)), "\n", "\n\t", -1)) - } - - for r := Thearch.REGMIN; r <= Thearch.REGMAX; r++ { - if reg[r-Thearch.REGMIN] != 0 { - dump(r) - } - } - for r := Thearch.FREGMIN; r <= Thearch.FREGMAX; r++ { - if reg[r-Thearch.REGMIN] == 0 { - dump(r) - } - } -} diff --git a/src/cmd/internal/gc/init.go b/src/cmd/internal/gc/init.go deleted file mode 100644 index b5d1e505a5..0000000000 --- a/src/cmd/internal/gc/init.go +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright 2009 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 gc - -// case OADD: -// if(n->right->op == OLITERAL) { -// v = n->right->vconst; -// naddr(n->left, a, canemitcode); -// } else -// if(n->left->op == OLITERAL) { -// v = n->left->vconst; -// naddr(n->right, a, canemitcode); -// } else -// goto bad; -// a->offset += v; -// break; - -/* - * a function named init is a special case. - * it is called by the initialization before - * main is run. to make it unique within a - * package and also uncallable, the name, - * normally "pkg.init", is altered to "pkg.init.1". - */ - -var renameinit_initgen int - -func renameinit() *Sym { - renameinit_initgen++ - return Lookupf("init.%d", renameinit_initgen) -} - -/* - * hand-craft the following initialization code - * var initdone· uint8 (1) - * func init() (2) - * if initdone· != 0 { (3) - * if initdone· == 2 (4) - * return - * throw(); (5) - * } - * initdone· = 1; (6) - * // over all matching imported symbols - * .init() (7) - * { } (8) - * init.() // if any (9) - * initdone· = 2; (10) - * return (11) - * } - */ -func anyinit(n *NodeList) bool { - // are there any interesting init statements - for l := n; l != nil; l = l.Next { - switch l.N.Op { - case ODCLFUNC, ODCLCONST, ODCLTYPE, OEMPTY: - break - - case OAS, OASWB: - if isblank(l.N.Left) && candiscard(l.N.Right) { - break - } - fallthrough - - // fall through - default: - return true - } - } - - // is this main - if localpkg.Name == "main" { - return true - } - - // is there an explicit init function - s := Lookup("init.1") - - if s.Def != nil { - return true - } - - // are there any imported init functions - for _, s := range initSyms { - if s.Def != nil { - return true - } - } - - // then none - return false -} - -func fninit(n *NodeList) { - if Debug['A'] != 0 { - // sys.go or unsafe.go during compiler build - return - } - - n = initfix(n) - if !anyinit(n) { - return - } - - var r *NodeList - - // (1) - gatevar := newname(Lookup("initdone·")) - addvar(gatevar, Types[TUINT8], PEXTERN) - - // (2) - Maxarg = 0 - - fn := Nod(ODCLFUNC, nil, nil) - initsym := Lookup("init") - fn.Nname = newname(initsym) - fn.Nname.Defn = fn - fn.Nname.Ntype = Nod(OTFUNC, nil, nil) - declare(fn.Nname, PFUNC) - funchdr(fn) - - // (3) - a := Nod(OIF, nil, nil) - - a.Ntest = Nod(ONE, gatevar, Nodintconst(0)) - r = list(r, a) - - // (4) - b := Nod(OIF, nil, nil) - - b.Ntest = Nod(OEQ, gatevar, Nodintconst(2)) - b.Nbody = list1(Nod(ORETURN, nil, nil)) - a.Nbody = list1(b) - - // (5) - b = syslook("throwinit", 0) - - b = Nod(OCALL, b, nil) - a.Nbody = list(a.Nbody, b) - - // (6) - a = Nod(OAS, gatevar, Nodintconst(1)) - - r = list(r, a) - - // (7) - for _, s := range initSyms { - if s.Def != nil && s != initsym { - // could check that it is fn of no args/returns - a = Nod(OCALL, s.Def, nil) - r = list(r, a) - } - } - - // (8) - r = concat(r, n) - - // (9) - // could check that it is fn of no args/returns - for i := 1; ; i++ { - s := Lookupf("init.%d", i) - if s.Def == nil { - break - } - a = Nod(OCALL, s.Def, nil) - r = list(r, a) - } - - // (10) - a = Nod(OAS, gatevar, Nodintconst(2)) - - r = list(r, a) - - // (11) - a = Nod(ORETURN, nil, nil) - - r = list(r, a) - exportsym(fn.Nname) - - fn.Nbody = r - funcbody(fn) - - Curfn = fn - typecheck(&fn, Etop) - typechecklist(r, Etop) - Curfn = nil - funccompile(fn) -} diff --git a/src/cmd/internal/gc/inl.go b/src/cmd/internal/gc/inl.go deleted file mode 100644 index 22a5d3d9fe..0000000000 --- a/src/cmd/internal/gc/inl.go +++ /dev/null @@ -1,1008 +0,0 @@ -// Copyright 2011 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. -// -// The inlining facility makes 2 passes: first caninl determines which -// functions are suitable for inlining, and for those that are it -// saves a copy of the body. Then inlcalls walks each function body to -// expand calls to inlinable functions. -// -// The debug['l'] flag controls the agressiveness. Note that main() swaps level 0 and 1, -// making 1 the default and -l disable. -ll and more is useful to flush out bugs. -// These additional levels (beyond -l) may be buggy and are not supported. -// 0: disabled -// 1: 40-nodes leaf functions, oneliners, lazy typechecking (default) -// 2: early typechecking of all imported bodies -// 3: allow variadic functions -// 4: allow non-leaf functions , (breaks runtime.Caller) -// -// At some point this may get another default and become switch-offable with -N. -// -// The debug['m'] flag enables diagnostic output. a single -m is useful for verifying -// which calls get inlined or not, more is for debugging, and may go away at any point. -// -// TODO: -// - inline functions with ... args -// - handle T.meth(f()) with func f() (t T, arg, arg, ) - -package gc - -import ( - "cmd/internal/obj" - "fmt" -) - -// Used by caninl. - -// Used by inlcalls - -// Used during inlsubst[list] -var inlfn *Node // function currently being inlined - -var inlretlabel *Node // target of the goto substituted in place of a return - -var inlretvars *NodeList // temp out variables - -// Get the function's package. For ordinary functions it's on the ->sym, but for imported methods -// the ->sym can be re-used in the local package, so peel it off the receiver's type. -func fnpkg(fn *Node) *Pkg { - if fn.Type.Thistuple != 0 { - // method - rcvr := getthisx(fn.Type).Type.Type - - if Isptr[rcvr.Etype] { - rcvr = rcvr.Type - } - if rcvr.Sym == nil { - Fatal("receiver with no sym: [%v] %v (%v)", fn.Sym, Nconv(fn, obj.FmtLong), rcvr) - } - return rcvr.Sym.Pkg - } - - // non-method - return fn.Sym.Pkg -} - -// Lazy typechecking of imported bodies. For local functions, caninl will set ->typecheck -// because they're a copy of an already checked body. -func typecheckinl(fn *Node) { - lno := int(setlineno(fn)) - - // typecheckinl is only for imported functions; - // their bodies may refer to unsafe as long as the package - // was marked safe during import (which was checked then). - // the ->inl of a local function has been typechecked before caninl copied it. - pkg := fnpkg(fn) - - if pkg == localpkg || pkg == nil { - return // typecheckinl on local function - } - - if Debug['m'] > 2 { - fmt.Printf("typecheck import [%v] %v { %v }\n", fn.Sym, Nconv(fn, obj.FmtLong), Hconv(fn.Func.Inl, obj.FmtSharp)) - } - - save_safemode := safemode - safemode = 0 - - savefn := Curfn - Curfn = fn - typechecklist(fn.Func.Inl, Etop) - Curfn = savefn - - safemode = save_safemode - - lineno = int32(lno) -} - -// Caninl determines whether fn is inlineable. -// If so, caninl saves fn->nbody in fn->inl and substitutes it with a copy. -// fn and ->nbody will already have been typechecked. -func caninl(fn *Node) { - if fn.Op != ODCLFUNC { - Fatal("caninl %v", fn) - } - if fn.Nname == nil { - Fatal("caninl no nname %v", Nconv(fn, obj.FmtSign)) - } - - // If fn has no body (is defined outside of Go), cannot inline it. - if fn.Nbody == nil { - return - } - - if fn.Typecheck == 0 { - Fatal("caninl on non-typechecked function %v", fn) - } - - // can't handle ... args yet - if Debug['l'] < 3 { - for t := fn.Type.Type.Down.Down.Type; t != nil; t = t.Down { - if t.Isddd { - return - } - } - } - - // Runtime package must not be race instrumented. - // Racewalk skips runtime package. However, some runtime code can be - // inlined into other packages and instrumented there. To avoid this, - // we disable inlining of runtime functions in race mode. - // The example that we observed is inlining of LockOSThread, - // which lead to false race reports on m contents. - if flag_race != 0 && myimportpath == "runtime" { - return - } - - const maxBudget = 80 - budget := maxBudget // allowed hairyness - if ishairylist(fn.Nbody, &budget) || budget < 0 { - return - } - - savefn := Curfn - Curfn = fn - - fn.Nname.Func.Inl = fn.Nbody - fn.Nbody = inlcopylist(fn.Nname.Func.Inl) - fn.Nname.Func.Inldcl = inlcopylist(fn.Nname.Defn.Func.Dcl) - fn.Nname.Func.InlCost = int32(maxBudget - budget) - - // hack, TODO, check for better way to link method nodes back to the thing with the ->inl - // this is so export can find the body of a method - fn.Type.Nname = fn.Nname - - if Debug['m'] > 1 { - fmt.Printf("%v: can inline %v as: %v { %v }\n", fn.Line(), Nconv(fn.Nname, obj.FmtSharp), Tconv(fn.Type, obj.FmtSharp), Hconv(fn.Nname.Func.Inl, obj.FmtSharp)) - } else if Debug['m'] != 0 { - fmt.Printf("%v: can inline %v\n", fn.Line(), fn.Nname) - } - - Curfn = savefn -} - -// Look for anything we want to punt on. -func ishairylist(ll *NodeList, budget *int) bool { - for ; ll != nil; ll = ll.Next { - if ishairy(ll.N, budget) { - return true - } - } - return false -} - -func ishairy(n *Node, budget *int) bool { - if n == nil { - return false - } - - switch n.Op { - // Call is okay if inlinable and we have the budget for the body. - case OCALLFUNC: - if n.Left.Func != nil && n.Left.Func.Inl != nil { - *budget -= int(n.Left.Func.InlCost) - break - } - if n.Left.Op == ONAME && n.Left.Left != nil && n.Left.Left.Op == OTYPE && n.Left.Right != nil && n.Left.Right.Op == ONAME { // methods called as functions - if n.Left.Sym.Def != nil && n.Left.Sym.Def.Func.Inl != nil { - *budget -= int(n.Left.Sym.Def.Func.InlCost) - break - } - } - if Debug['l'] < 4 { - return true - } - - // Call is okay if inlinable and we have the budget for the body. - case OCALLMETH: - if n.Left.Type == nil { - Fatal("no function type for [%p] %v\n", n.Left, Nconv(n.Left, obj.FmtSign)) - } - if n.Left.Type.Nname == nil { - Fatal("no function definition for [%p] %v\n", n.Left.Type, Tconv(n.Left.Type, obj.FmtSign)) - } - if n.Left.Type.Nname.Func.Inl != nil { - *budget -= int(n.Left.Type.Nname.Func.InlCost) - break - } - if Debug['l'] < 4 { - return true - } - - // Things that are too hairy, irrespective of the budget - case OCALL, OCALLINTER, OPANIC, ORECOVER: - if Debug['l'] < 4 { - return true - } - - case OCLOSURE, - OCALLPART, - ORANGE, - OFOR, - OSELECT, - OSWITCH, - OPROC, - ODEFER, - ODCLTYPE, // can't print yet - ODCLCONST, // can't print yet - ORETJMP: - return true - } - - (*budget)-- - - return *budget < 0 || ishairy(n.Left, budget) || ishairy(n.Right, budget) || ishairylist(n.List, budget) || ishairylist(n.Rlist, budget) || ishairylist(n.Ninit, budget) || ishairy(n.Ntest, budget) || ishairy(n.Nincr, budget) || ishairylist(n.Nbody, budget) || ishairylist(n.Nelse, budget) -} - -// Inlcopy and inlcopylist recursively copy the body of a function. -// Any name-like node of non-local class is marked for re-export by adding it to -// the exportlist. -func inlcopylist(ll *NodeList) *NodeList { - var l *NodeList - for ; ll != nil; ll = ll.Next { - l = list(l, inlcopy(ll.N)) - } - return l -} - -func inlcopy(n *Node) *Node { - if n == nil { - return nil - } - - switch n.Op { - case ONAME, OTYPE, OLITERAL: - return n - } - - m := Nod(OXXX, nil, nil) - *m = *n - if m.Func != nil { - m.Func.Inl = nil - } - m.Left = inlcopy(n.Left) - m.Right = inlcopy(n.Right) - m.List = inlcopylist(n.List) - m.Rlist = inlcopylist(n.Rlist) - m.Ninit = inlcopylist(n.Ninit) - m.Ntest = inlcopy(n.Ntest) - m.Nincr = inlcopy(n.Nincr) - m.Nbody = inlcopylist(n.Nbody) - m.Nelse = inlcopylist(n.Nelse) - - return m -} - -// Inlcalls/nodelist/node walks fn's statements and expressions and substitutes any -// calls made to inlineable functions. This is the external entry point. -func inlcalls(fn *Node) { - savefn := Curfn - Curfn = fn - inlnode(&fn) - if fn != Curfn { - Fatal("inlnode replaced curfn") - } - Curfn = savefn -} - -// Turn an OINLCALL into a statement. -func inlconv2stmt(n *Node) { - n.Op = OBLOCK - - // n->ninit stays - n.List = n.Nbody - - n.Nbody = nil - n.Rlist = nil -} - -// Turn an OINLCALL into a single valued expression. -func inlconv2expr(np **Node) { - n := *np - r := n.Rlist.N - addinit(&r, concat(n.Ninit, n.Nbody)) - *np = r -} - -// Turn the rlist (with the return values) of the OINLCALL in -// n into an expression list lumping the ninit and body -// containing the inlined statements on the first list element so -// order will be preserved Used in return, oas2func and call -// statements. -func inlconv2list(n *Node) *NodeList { - if n.Op != OINLCALL || n.Rlist == nil { - Fatal("inlconv2list %v\n", Nconv(n, obj.FmtSign)) - } - - l := n.Rlist - addinit(&l.N, concat(n.Ninit, n.Nbody)) - return l -} - -func inlnodelist(l *NodeList) { - for ; l != nil; l = l.Next { - inlnode(&l.N) - } -} - -// inlnode recurses over the tree to find inlineable calls, which will -// be turned into OINLCALLs by mkinlcall. When the recursion comes -// back up will examine left, right, list, rlist, ninit, ntest, nincr, -// nbody and nelse and use one of the 4 inlconv/glue functions above -// to turn the OINLCALL into an expression, a statement, or patch it -// in to this nodes list or rlist as appropriate. -// NOTE it makes no sense to pass the glue functions down the -// recursion to the level where the OINLCALL gets created because they -// have to edit /this/ n, so you'd have to push that one down as well, -// but then you may as well do it here. so this is cleaner and -// shorter and less complicated. -func inlnode(np **Node) { - if *np == nil { - return - } - - n := *np - - switch n.Op { - // inhibit inlining of their argument - case ODEFER, OPROC: - switch n.Left.Op { - case OCALLFUNC, OCALLMETH: - n.Left.Etype = n.Op - } - fallthrough - - // TODO do them here (or earlier), - // so escape analysis can avoid more heapmoves. - case OCLOSURE: - return - } - - lno := int(setlineno(n)) - - inlnodelist(n.Ninit) - for l := n.Ninit; l != nil; l = l.Next { - if l.N.Op == OINLCALL { - inlconv2stmt(l.N) - } - } - - inlnode(&n.Left) - if n.Left != nil && n.Left.Op == OINLCALL { - inlconv2expr(&n.Left) - } - - inlnode(&n.Right) - if n.Right != nil && n.Right.Op == OINLCALL { - inlconv2expr(&n.Right) - } - - inlnodelist(n.List) - switch n.Op { - case OBLOCK: - for l := n.List; l != nil; l = l.Next { - if l.N.Op == OINLCALL { - inlconv2stmt(l.N) - } - } - - // if we just replaced arg in f(arg()) or return arg with an inlined call - // and arg returns multiple values, glue as list - case ORETURN, - OCALLFUNC, - OCALLMETH, - OCALLINTER, - OAPPEND, - OCOMPLEX: - if count(n.List) == 1 && n.List.N.Op == OINLCALL && count(n.List.N.Rlist) > 1 { - n.List = inlconv2list(n.List.N) - break - } - fallthrough - - default: - for l := n.List; l != nil; l = l.Next { - if l.N.Op == OINLCALL { - inlconv2expr(&l.N) - } - } - } - - inlnodelist(n.Rlist) - switch n.Op { - case OAS2FUNC: - if n.Rlist.N.Op == OINLCALL { - n.Rlist = inlconv2list(n.Rlist.N) - n.Op = OAS2 - n.Typecheck = 0 - typecheck(np, Etop) - break - } - fallthrough - - default: - for l := n.Rlist; l != nil; l = l.Next { - if l.N.Op == OINLCALL { - inlconv2expr(&l.N) - } - } - } - - inlnode(&n.Ntest) - if n.Ntest != nil && n.Ntest.Op == OINLCALL { - inlconv2expr(&n.Ntest) - } - - inlnode(&n.Nincr) - if n.Nincr != nil && n.Nincr.Op == OINLCALL { - inlconv2stmt(n.Nincr) - } - - inlnodelist(n.Nbody) - for l := n.Nbody; l != nil; l = l.Next { - if l.N.Op == OINLCALL { - inlconv2stmt(l.N) - } - } - - inlnodelist(n.Nelse) - for l := n.Nelse; l != nil; l = l.Next { - if l.N.Op == OINLCALL { - inlconv2stmt(l.N) - } - } - - // with all the branches out of the way, it is now time to - // transmogrify this node itself unless inhibited by the - // switch at the top of this function. - switch n.Op { - case OCALLFUNC, OCALLMETH: - if n.Etype == OPROC || n.Etype == ODEFER { - return - } - } - - switch n.Op { - case OCALLFUNC: - if Debug['m'] > 3 { - fmt.Printf("%v:call to func %v\n", n.Line(), Nconv(n.Left, obj.FmtSign)) - } - if n.Left.Func != nil && n.Left.Func.Inl != nil { // normal case - mkinlcall(np, n.Left, n.Isddd) - } else if n.Left.Op == ONAME && n.Left.Left != nil && n.Left.Left.Op == OTYPE && n.Left.Right != nil && n.Left.Right.Op == ONAME { // methods called as functions - if n.Left.Sym.Def != nil { - mkinlcall(np, n.Left.Sym.Def, n.Isddd) - } - } - - case OCALLMETH: - if Debug['m'] > 3 { - fmt.Printf("%v:call to meth %v\n", n.Line(), Nconv(n.Left.Right, obj.FmtLong)) - } - - // typecheck should have resolved ODOTMETH->type, whose nname points to the actual function. - if n.Left.Type == nil { - Fatal("no function type for [%p] %v\n", n.Left, Nconv(n.Left, obj.FmtSign)) - } - - if n.Left.Type.Nname == nil { - Fatal("no function definition for [%p] %v\n", n.Left.Type, Tconv(n.Left.Type, obj.FmtSign)) - } - - mkinlcall(np, n.Left.Type.Nname, n.Isddd) - } - - lineno = int32(lno) -} - -func mkinlcall(np **Node, fn *Node, isddd bool) { - save_safemode := safemode - - // imported functions may refer to unsafe as long as the - // package was marked safe during import (already checked). - pkg := fnpkg(fn) - - if pkg != localpkg && pkg != nil { - safemode = 0 - } - mkinlcall1(np, fn, isddd) - safemode = save_safemode -} - -func tinlvar(t *Type) *Node { - if t.Nname != nil && !isblank(t.Nname) { - if t.Nname.Name.Inlvar == nil { - Fatal("missing inlvar for %v\n", t.Nname) - } - return t.Nname.Name.Inlvar - } - - typecheck(&nblank, Erv|Easgn) - return nblank -} - -var inlgen int - -// if *np is a call, and fn is a function with an inlinable body, substitute *np with an OINLCALL. -// On return ninit has the parameter assignments, the nbody is the -// inlined function body and list, rlist contain the input, output -// parameters. -func mkinlcall1(np **Node, fn *Node, isddd bool) { - // For variadic fn. - if fn.Func.Inl == nil { - return - } - - if fn == Curfn || fn.Defn == Curfn { - return - } - - if Debug['l'] < 2 { - typecheckinl(fn) - } - - n := *np - - // Bingo, we have a function node, and it has an inlineable body - if Debug['m'] > 1 { - fmt.Printf("%v: inlining call to %v %v { %v }\n", n.Line(), fn.Sym, Tconv(fn.Type, obj.FmtSharp), Hconv(fn.Func.Inl, obj.FmtSharp)) - } else if Debug['m'] != 0 { - fmt.Printf("%v: inlining call to %v\n", n.Line(), fn) - } - - if Debug['m'] > 2 { - fmt.Printf("%v: Before inlining: %v\n", n.Line(), Nconv(n, obj.FmtSign)) - } - - saveinlfn := inlfn - inlfn = fn - - ninit := n.Ninit - - //dumplist("ninit pre", ninit); - - var dcl *NodeList - if fn.Defn != nil { // local function - dcl = fn.Func.Inldcl // imported function - } else { - dcl = fn.Func.Dcl - } - - inlretvars = nil - i := 0 - - // Make temp names to use instead of the originals - for ll := dcl; ll != nil; ll = ll.Next { - if ll.N.Class == PPARAMOUT { // return values handled below. - continue - } - if ll.N.Op == ONAME { - ll.N.Name.Inlvar = inlvar(ll.N) - - // Typecheck because inlvar is not necessarily a function parameter. - typecheck(&ll.N.Name.Inlvar, Erv) - - if ll.N.Class&^PHEAP != PAUTO { - ninit = list(ninit, Nod(ODCL, ll.N.Name.Inlvar, nil)) // otherwise gen won't emit the allocations for heapallocs - } - } - } - - // temporaries for return values. - var m *Node - for t := getoutargx(fn.Type).Type; t != nil; t = t.Down { - if t != nil && t.Nname != nil && !isblank(t.Nname) { - m = inlvar(t.Nname) - typecheck(&m, Erv) - t.Nname.Name.Inlvar = m - } else { - // anonymous return values, synthesize names for use in assignment that replaces return - m = retvar(t, i) - i++ - } - - ninit = list(ninit, Nod(ODCL, m, nil)) - inlretvars = list(inlretvars, m) - } - - // assign receiver. - var as *Node - if fn.Type.Thistuple != 0 && n.Left.Op == ODOTMETH { - // method call with a receiver. - t := getthisx(fn.Type).Type - - 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 { - Fatal("method call without receiver: %v", Nconv(n, obj.FmtSign)) - } - if t == nil { - Fatal("method call unknown receiver type: %v", Nconv(n, obj.FmtSign)) - } - as = Nod(OAS, tinlvar(t), n.Left.Left) - if as != nil { - typecheck(&as, Etop) - ninit = list(ninit, as) - } - } - - // check if inlined function is variadic. - variadic := false - - var varargtype *Type - varargcount := 0 - for t := fn.Type.Type.Down.Down.Type; t != nil; t = t.Down { - if t.Isddd { - variadic = true - varargtype = t.Type - } - } - - // but if argument is dotted too forget about variadicity. - if variadic && isddd { - variadic = false - } - - // check if argument is actually a returned tuple from call. - multiret := 0 - - if n.List != nil && n.List.Next == nil { - switch n.List.N.Op { - case OCALL, OCALLFUNC, OCALLINTER, OCALLMETH: - if n.List.N.Left.Type.Outtuple > 1 { - multiret = n.List.N.Left.Type.Outtuple - 1 - } - } - } - - if variadic { - varargcount = count(n.List) + multiret - if n.Left.Op != ODOTMETH { - varargcount -= fn.Type.Thistuple - } - varargcount -= fn.Type.Intuple - 1 - } - - // assign arguments to the parameters' temp names - as = Nod(OAS2, nil, nil) - - as.Rlist = n.List - ll := n.List - - // TODO: if len(nlist) == 1 but multiple args, check that n->list->n is a call? - if fn.Type.Thistuple != 0 && n.Left.Op != ODOTMETH { - // non-method call to method - if n.List == nil { - Fatal("non-method call to method without first arg: %v", Nconv(n, obj.FmtSign)) - } - - // append receiver inlvar to LHS. - t := getthisx(fn.Type).Type - - 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 { - Fatal("method call unknown receiver type: %v", Nconv(n, obj.FmtSign)) - } - as.List = list(as.List, tinlvar(t)) - ll = ll.Next // track argument count. - } - - // append ordinary arguments to LHS. - chkargcount := n.List != nil && n.List.Next != nil - - var vararg *Node // the slice argument to a variadic call - var varargs *NodeList // the list of LHS names to put in vararg. - if !chkargcount { - // 0 or 1 expression on RHS. - var i int - for t := getinargx(fn.Type).Type; t != nil; t = t.Down { - if variadic && t.Isddd { - vararg = tinlvar(t) - for i = 0; i < varargcount && ll != nil; i++ { - m = argvar(varargtype, i) - varargs = list(varargs, m) - as.List = list(as.List, m) - } - - break - } - - as.List = list(as.List, tinlvar(t)) - } - } else { - // match arguments except final variadic (unless the call is dotted itself) - var t *Type - for t = getinargx(fn.Type).Type; t != nil; { - if ll == nil { - break - } - if variadic && t.Isddd { - break - } - as.List = list(as.List, tinlvar(t)) - t = t.Down - ll = ll.Next - } - - // match varargcount arguments with variadic parameters. - if variadic && t != nil && t.Isddd { - vararg = tinlvar(t) - var i int - for i = 0; i < varargcount && ll != nil; i++ { - m = argvar(varargtype, i) - varargs = list(varargs, m) - as.List = list(as.List, m) - ll = ll.Next - } - - if i == varargcount { - t = t.Down - } - } - - if ll != nil || t != nil { - Fatal("arg count mismatch: %v vs %v\n", Tconv(getinargx(fn.Type), obj.FmtSharp), Hconv(n.List, obj.FmtComma)) - } - } - - if as.Rlist != nil { - typecheck(&as, Etop) - ninit = list(ninit, as) - } - - // turn the variadic args into a slice. - if variadic { - as = Nod(OAS, vararg, nil) - if varargcount == 0 { - as.Right = nodnil() - as.Right.Type = varargtype - } else { - vararrtype := typ(TARRAY) - vararrtype.Type = varargtype.Type - vararrtype.Bound = int64(varargcount) - - as.Right = Nod(OCOMPLIT, nil, typenod(varargtype)) - as.Right.List = varargs - as.Right = Nod(OSLICE, as.Right, Nod(OKEY, nil, nil)) - } - - typecheck(&as, Etop) - ninit = list(ninit, as) - } - - // zero the outparams - for ll := inlretvars; ll != nil; ll = ll.Next { - as = Nod(OAS, ll.N, nil) - typecheck(&as, Etop) - ninit = list(ninit, as) - } - - inlretlabel = newlabel_inl() - inlgen++ - body := inlsubstlist(fn.Func.Inl) - - body = list(body, Nod(OGOTO, inlretlabel, nil)) // avoid 'not used' when function doesnt have return - body = list(body, Nod(OLABEL, inlretlabel, nil)) - - typechecklist(body, Etop) - - //dumplist("ninit post", ninit); - - call := Nod(OINLCALL, nil, nil) - - call.Ninit = ninit - call.Nbody = body - call.Rlist = inlretvars - call.Type = n.Type - call.Typecheck = 1 - - setlno(call, int(n.Lineno)) - - //dumplist("call body", body); - - *np = call - - inlfn = saveinlfn - - // transitive inlining - // might be nice to do this before exporting the body, - // but can't emit the body with inlining expanded. - // instead we emit the things that the body needs - // and each use must redo the inlining. - // luckily these are small. - body = fn.Func.Inl - fn.Func.Inl = nil // prevent infinite recursion (shouldn't happen anyway) - inlnodelist(call.Nbody) - for ll := call.Nbody; ll != nil; ll = ll.Next { - if ll.N.Op == OINLCALL { - inlconv2stmt(ll.N) - } - } - fn.Func.Inl = body - - if Debug['m'] > 2 { - fmt.Printf("%v: After inlining %v\n\n", n.Line(), Nconv(*np, obj.FmtSign)) - } -} - -// Every time we expand a function we generate a new set of tmpnames, -// PAUTO's in the calling functions, and link them off of the -// PPARAM's, PAUTOS and PPARAMOUTs of the called function. -func inlvar(var_ *Node) *Node { - if Debug['m'] > 3 { - fmt.Printf("inlvar %v\n", Nconv(var_, obj.FmtSign)) - } - - n := newname(var_.Sym) - n.Type = var_.Type - n.Class = PAUTO - n.Used = true - n.Curfn = Curfn // the calling function, not the called one - n.Addrtaken = var_.Addrtaken - - // Esc pass wont run if we're inlining into a iface wrapper. - // Luckily, we can steal the results from the target func. - // If inlining a function defined in another package after - // escape analysis is done, treat all local vars as escaping. - // See issue 9537. - if var_.Esc == EscHeap || (inl_nonlocal != 0 && var_.Op == ONAME) { - addrescapes(n) - } - - Curfn.Func.Dcl = list(Curfn.Func.Dcl, n) - return n -} - -// Synthesize a variable to store the inlined function's results in. -func retvar(t *Type, i int) *Node { - n := newname(Lookupf("~r%d", i)) - n.Type = t.Type - n.Class = PAUTO - n.Used = true - n.Curfn = Curfn // the calling function, not the called one - Curfn.Func.Dcl = list(Curfn.Func.Dcl, n) - return n -} - -// Synthesize a variable to store the inlined function's arguments -// when they come from a multiple return call. -func argvar(t *Type, i int) *Node { - n := newname(Lookupf("~arg%d", i)) - n.Type = t.Type - n.Class = PAUTO - n.Used = true - n.Curfn = Curfn // the calling function, not the called one - Curfn.Func.Dcl = list(Curfn.Func.Dcl, n) - return n -} - -var newlabel_inl_label int - -func newlabel_inl() *Node { - newlabel_inl_label++ - n := newname(Lookupf(".inlret%.6d", newlabel_inl_label)) - n.Etype = 1 // flag 'safe' for escape analysis (no backjumps) - return n -} - -// inlsubst and inlsubstlist recursively copy the body of the saved -// pristine ->inl body of the function while substituting references -// to input/output parameters with ones to the tmpnames, and -// substituting returns with assignments to the output. -func inlsubstlist(ll *NodeList) *NodeList { - var l *NodeList - for ; ll != nil; ll = ll.Next { - l = list(l, inlsubst(ll.N)) - } - return l -} - -func inlsubst(n *Node) *Node { - if n == nil { - return nil - } - - switch n.Op { - case ONAME: - 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.Name.Inlvar, obj.FmtSign)) - } - return n.Name.Inlvar - } - - if Debug['m'] > 2 { - fmt.Printf("not substituting name %v\n", Nconv(n, obj.FmtSign)) - } - return n - - case OLITERAL, OTYPE: - return n - - // Since we don't handle bodies with closures, this return is guaranteed to belong to the current inlined function. - - // dump("Return before substitution", n); - case ORETURN: - m := Nod(OGOTO, inlretlabel, nil) - - m.Ninit = inlsubstlist(n.Ninit) - - if inlretvars != nil && n.List != nil { - as := Nod(OAS2, nil, nil) - - // shallow copy or OINLCALL->rlist will be the same list, and later walk and typecheck may clobber that. - for ll := inlretvars; ll != nil; ll = ll.Next { - as.List = list(as.List, ll.N) - } - as.Rlist = inlsubstlist(n.List) - typecheck(&as, Etop) - m.Ninit = list(m.Ninit, as) - } - - typechecklist(m.Ninit, Etop) - typecheck(&m, Etop) - - // dump("Return after substitution", m); - return m - - case OGOTO, OLABEL: - m := Nod(OXXX, nil, nil) - *m = *n - m.Ninit = nil - p := fmt.Sprintf("%s·%d", n.Left.Sym.Name, inlgen) - m.Left = newname(Lookup(p)) - - return m - } - - m := Nod(OXXX, nil, nil) - *m = *n - m.Ninit = nil - - if n.Op == OCLOSURE { - Fatal("cannot inline function containing closure: %v", Nconv(n, obj.FmtSign)) - } - - m.Left = inlsubst(n.Left) - m.Right = inlsubst(n.Right) - m.List = inlsubstlist(n.List) - m.Rlist = inlsubstlist(n.Rlist) - m.Ninit = concat(m.Ninit, inlsubstlist(n.Ninit)) - m.Ntest = inlsubst(n.Ntest) - m.Nincr = inlsubst(n.Nincr) - m.Nbody = inlsubstlist(n.Nbody) - m.Nelse = inlsubstlist(n.Nelse) - - return m -} - -// Plaster over linenumbers -func setlnolist(ll *NodeList, lno int) { - for ; ll != nil; ll = ll.Next { - setlno(ll.N, lno) - } -} - -func setlno(n *Node, lno int) { - if n == nil { - return - } - - // don't clobber names, unless they're freshly synthesized - if n.Op != ONAME || n.Lineno == 0 { - n.Lineno = int32(lno) - } - - setlno(n.Left, lno) - setlno(n.Right, lno) - setlnolist(n.List, lno) - setlnolist(n.Rlist, lno) - setlnolist(n.Ninit, lno) - setlno(n.Ntest, lno) - setlno(n.Nincr, lno) - setlnolist(n.Nbody, lno) - setlnolist(n.Nelse, lno) -} diff --git a/src/cmd/internal/gc/lex.go b/src/cmd/internal/gc/lex.go deleted file mode 100644 index f9211407fb..0000000000 --- a/src/cmd/internal/gc/lex.go +++ /dev/null @@ -1,2601 +0,0 @@ -// Copyright 2009 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. - -//go:generate go tool yacc go.y -//go:generate go run mkbuiltin.go runtime unsafe - -package gc - -import ( - "bytes" - "cmd/internal/obj" - "flag" - "fmt" - "io" - "log" - "os" - "path" - "strconv" - "strings" - "unicode" - "unicode/utf8" -) - -var yyprev int - -var yylast int - -var imported_unsafe int - -var goos string - -var goarch string - -var goroot string - -var ( - Debug_wb int - Debug_append int - Debug_slice int -) - -// Debug arguments. -// These can be specified with the -d flag, as in "-d nil" -// to set the debug_checknil variable. In general the list passed -// to -d can be comma-separated. -var debugtab = []struct { - name string - val *int -}{ - {"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 -// of EOF and other out of range arguments. -func yy_isdigit(c int) bool { - return c >= 0 && c <= 0xFF && isdigit(c) -} - -func yy_isspace(c int) bool { - return c == ' ' || c == '\t' || c == '\n' || c == '\r' -} - -func yy_isalpha(c int) bool { - return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' -} - -func yy_isalnum(c int) bool { - return c >= 0 && c <= 0xFF && isalnum(c) -} - -// Disallow use of isdigit etc. - -const ( - EOF = -1 -) - -func usage() { - fmt.Printf("usage: %cg [options] file.go...\n", Thearch.Thechar) - obj.Flagprint(1) - Exit(2) -} - -func hidePanic() { - if nsavederrors+nerrors > 0 { - // If we've already complained about things - // in the program, don't bother complaining - // about a panic too; let the user clean up - // the code and try again. - if err := recover(); err != nil { - errorexit() - } - } -} - -func doversion() { - p := obj.Expstring() - if p == "X:none" { - p = "" - } - sep := "" - if p != "" { - sep = " " - } - fmt.Printf("%cg version %s%s%s\n", Thearch.Thechar, obj.Getgoversion(), sep, p) - os.Exit(0) -} - -func Main() { - defer hidePanic() - - // Allow GOARCH=thearch.thestring or GOARCH=thearch.thestringsuffix, - // but not other values. - p := obj.Getgoarch() - - if !strings.HasPrefix(p, Thearch.Thestring) { - log.Fatalf("cannot use %cg with GOARCH=%s", Thearch.Thechar, p) - } - goarch = p - - Thearch.Linkarchinit() - Ctxt = obj.Linknew(Thearch.Thelinkarch) - Ctxt.Diag = Yyerror - Ctxt.Bso = &bstdout - bstdout = *obj.Binitw(os.Stdout) - - localpkg = mkpkg("") - localpkg.Prefix = "\"\"" - - // pseudo-package, for scoping - builtinpkg = mkpkg("go.builtin") - - builtinpkg.Prefix = "go.builtin" // not go%2ebuiltin - - // pseudo-package, accessed by import "unsafe" - unsafepkg = mkpkg("unsafe") - - unsafepkg.Name = "unsafe" - - // real package, referred to by generated runtime calls - Runtimepkg = mkpkg("runtime") - - Runtimepkg.Name = "runtime" - - // pseudo-packages used in symbol tables - gostringpkg = mkpkg("go.string") - - gostringpkg.Name = "go.string" - gostringpkg.Prefix = "go.string" // not go%2estring - - itabpkg = mkpkg("go.itab") - - itabpkg.Name = "go.itab" - itabpkg.Prefix = "go.itab" // not go%2eitab - - weaktypepkg = mkpkg("go.weak.type") - - weaktypepkg.Name = "go.weak.type" - weaktypepkg.Prefix = "go.weak.type" // not go%2eweak%2etype - - typelinkpkg = mkpkg("go.typelink") - typelinkpkg.Name = "go.typelink" - typelinkpkg.Prefix = "go.typelink" // not go%2etypelink - - trackpkg = mkpkg("go.track") - - trackpkg.Name = "go.track" - trackpkg.Prefix = "go.track" // not go%2etrack - - typepkg = mkpkg("type") - - typepkg.Name = "type" - - goroot = obj.Getgoroot() - goos = obj.Getgoos() - - Nacl = goos == "nacl" - if Nacl { - flag_largemodel = 1 - } - - outfile = "" - obj.Flagcount("+", "compiling runtime", &compiling_runtime) - obj.Flagcount("%", "debug non-static initializers", &Debug['%']) - obj.Flagcount("A", "for bootstrapping, allow 'any' type", &Debug['A']) - obj.Flagcount("B", "disable bounds checking", &Debug['B']) - obj.Flagstr("D", "set relative `path` for local imports", &localimport) - obj.Flagcount("E", "debug symbol export", &Debug['E']) - obj.Flagfn1("I", "add `directory` to import search path", addidir) - obj.Flagcount("K", "debug missing line numbers", &Debug['K']) - obj.Flagcount("L", "use full (long) path in error messages", &Debug['L']) - obj.Flagcount("M", "debug move generation", &Debug['M']) - obj.Flagcount("N", "disable optimizations", &Debug['N']) - obj.Flagcount("P", "debug peephole optimizer", &Debug['P']) - obj.Flagcount("R", "debug register optimizer", &Debug['R']) - obj.Flagcount("S", "print assembly listing", &Debug['S']) - obj.Flagfn0("V", "print compiler version", doversion) - obj.Flagcount("W", "debug parse tree after type checking", &Debug['W']) - obj.Flagstr("asmhdr", "write assembly header to `file`", &asmhdr) - obj.Flagcount("complete", "compiling complete package (no C or assembly)", &pure_go) - obj.Flagstr("d", "print debug information about items in `list`", &debugstr) - obj.Flagcount("e", "no limit on number of errors reported", &Debug['e']) - obj.Flagcount("f", "debug stack frames", &Debug['f']) - obj.Flagcount("g", "debug code generation", &Debug['g']) - obj.Flagcount("h", "halt on error", &Debug['h']) - obj.Flagcount("i", "debug line number stack", &Debug['i']) - obj.Flagstr("installsuffix", "set pkg directory `suffix`", &flag_installsuffix) - obj.Flagcount("j", "debug runtime-initialized variables", &Debug['j']) - obj.Flagcount("l", "disable inlining", &Debug['l']) - obj.Flagcount("live", "debug liveness analysis", &debuglive) - obj.Flagcount("m", "print optimization decisions", &Debug['m']) - obj.Flagcount("nolocalimports", "reject local (relative) imports", &nolocalimports) - obj.Flagstr("o", "write output to `file`", &outfile) - obj.Flagstr("p", "set expected package import `path`", &myimportpath) - obj.Flagcount("pack", "write package file instead of object file", &writearchive) - obj.Flagcount("r", "debug generated wrappers", &Debug['r']) - obj.Flagcount("race", "enable race detector", &flag_race) - obj.Flagcount("s", "warn about composite literals that can be simplified", &Debug['s']) - obj.Flagstr("trimpath", "remove `prefix` from recorded source file paths", &Ctxt.LineHist.TrimPathPrefix) - obj.Flagcount("u", "reject unsafe code", &safemode) - obj.Flagcount("v", "increase debug verbosity", &Debug['v']) - obj.Flagcount("w", "debug type checking", &Debug['w']) - use_writebarrier = 1 - obj.Flagcount("wb", "enable write barrier", &use_writebarrier) - obj.Flagcount("x", "debug lexer", &Debug['x']) - obj.Flagcount("y", "debug declarations in canned imports (with -d)", &Debug['y']) - var flag_shared int - var flag_dynlink bool - if Thearch.Thechar == '6' { - obj.Flagcount("largemodel", "generate code that assumes a large memory model", &flag_largemodel) - obj.Flagcount("shared", "generate code that can be linked into a shared library", &flag_shared) - flag.BoolVar(&flag_dynlink, "dynlink", false, "support references to Go symbols defined in other shared libraries") - } - obj.Flagstr("cpuprofile", "write cpu profile to `file`", &cpuprofile) - obj.Flagstr("memprofile", "write memory profile to `file`", &memprofile) - obj.Flagint64("memprofilerate", "set runtime.MemProfileRate to `rate`", &memprofilerate) - obj.Flagparse(usage) - - if flag_dynlink { - flag_shared = 1 - } - Ctxt.Flag_shared = int32(flag_shared) - Ctxt.Flag_dynlink = flag_dynlink - - Ctxt.Debugasm = int32(Debug['S']) - Ctxt.Debugvlog = int32(Debug['v']) - - if flag.NArg() < 1 { - usage() - } - - startProfile() - - if flag_race != 0 { - racepkg = mkpkg("runtime/race") - racepkg.Name = "race" - } - - // parse -d argument - if debugstr != "" { - Split: - for _, name := range strings.Split(debugstr, ",") { - if name == "" { - continue - } - val := 1 - if i := strings.Index(name, "="); i >= 0 { - var err error - val, err = strconv.Atoi(name[i+1:]) - if err != nil { - log.Fatalf("invalid debug value %v", name) - } - name = name[:i] - } - for _, t := range debugtab { - if t.name == name { - if t.val != nil { - *t.val = val - continue Split - } - } - } - log.Fatalf("unknown debug key -d %s\n", name) - } - } - - // enable inlining. for now: - // default: inlining on. (debug['l'] == 1) - // -l: inlining off (debug['l'] == 0) - // -ll, -lll: inlining on again, with extra debugging (debug['l'] > 1) - if Debug['l'] <= 1 { - Debug['l'] = 1 - Debug['l'] - } - - Thearch.Betypeinit() - if Widthptr == 0 { - Fatal("betypeinit failed") - } - - lexinit() - typeinit() - lexinit1() - // TODO(rsc): Restore yytinit? - - blockgen = 1 - dclcontext = PEXTERN - nerrors = 0 - lexlineno = 1 - - for _, infile = range flag.Args() { - linehistpush(infile) - - curio.infile = infile - var err error - curio.bin, err = obj.Bopenr(infile) - if err != nil { - fmt.Printf("open %s: %v\n", infile, err) - errorexit() - } - - curio.peekc = 0 - curio.peekc1 = 0 - curio.nlsemi = 0 - curio.eofnl = 0 - curio.last = 0 - - // Skip initial BOM if present. - if obj.Bgetrune(curio.bin) != obj.BOM { - obj.Bungetrune(curio.bin) - } - - block = 1 - iota_ = -1000000 - - imported_unsafe = 0 - - yyparse() - if nsyntaxerrors != 0 { - errorexit() - } - - linehistpop() - if curio.bin != nil { - obj.Bterm(curio.bin) - } - } - - testdclstack() - mkpackage(localpkg.Name) // final import not used checks - lexfini() - - typecheckok = 1 - if Debug['f'] != 0 { - frame(1) - } - - // Process top-level declarations in phases. - - // Phase 1: const, type, and names and types of funcs. - // This will gather all the information about types - // and methods but doesn't depend on any of it. - defercheckwidth() - - for l := xtop; l != nil; l = l.Next { - if l.N.Op != ODCL && l.N.Op != OAS { - typecheck(&l.N, Etop) - } - } - - // Phase 2: Variable assignments. - // To check interface assignments, depends on phase 1. - for l := xtop; l != nil; l = l.Next { - if l.N.Op == ODCL || l.N.Op == OAS { - typecheck(&l.N, Etop) - } - } - resumecheckwidth() - - // Phase 3: Type check function bodies. - for l := xtop; l != nil; l = l.Next { - if l.N.Op == ODCLFUNC || l.N.Op == OCLOSURE { - Curfn = l.N - decldepth = 1 - saveerrors() - typechecklist(l.N.Nbody, Etop) - checkreturn(l.N) - if nerrors != 0 { - l.N.Nbody = nil // type errors; do not compile - } - } - } - - // Phase 4: Decide how to capture closed variables. - // 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 { - Curfn = l.N - capturevars(l.N) - } - } - - Curfn = nil - - if nsavederrors+nerrors != 0 { - errorexit() - } - - // Phase 5: Inlining - if Debug['l'] > 1 { - // Typecheck imported function bodies if debug['l'] > 1, - // otherwise lazily when used or re-exported. - for l := importlist; l != nil; l = l.Next { - if l.N.Func.Inl != nil { - saveerrors() - typecheckinl(l.N) - } - } - - if nsavederrors+nerrors != 0 { - errorexit() - } - } - - if Debug['l'] != 0 { - // Find functions that can be inlined and clone them before walk expands them. - visitBottomUp(xtop, func(list *NodeList, recursive bool) { - for l := list; l != nil; l = l.Next { - if l.N.Op == ODCLFUNC { - caninl(l.N) - inlcalls(l.N) - } - } - }) - } - - // Phase 6: Escape analysis. - // Required for moving heap allocations onto stack, - // which in turn is required by the closure implementation, - // 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. - 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. - for l := xtop; l != nil; l = l.Next { - if l.N.Op == ODCLFUNC && l.N.Closure != nil { - Curfn = l.N - transformclosure(l.N) - } - } - - Curfn = nil - - // Phase 8: Compile top level functions. - for l := xtop; l != nil; l = l.Next { - if l.N.Op == ODCLFUNC { - funccompile(l.N) - } - } - - if nsavederrors+nerrors == 0 { - fninit(xtop) - } - - // Phase 9: Check external declarations. - for l := externdcl; l != nil; l = l.Next { - if l.N.Op == ONAME { - typecheck(&l.N, Erv) - } - } - - if nerrors+nsavederrors != 0 { - errorexit() - } - - dumpobj() - - if asmhdr != "" { - dumpasmhdr() - } - - if nerrors+nsavederrors != 0 { - errorexit() - } - - Flusherrors() -} - -func saveerrors() { - nsavederrors += nerrors - nerrors = 0 -} - -func arsize(b *obj.Biobuf, name string) int { - var buf [ArhdrSize]byte - if _, err := io.ReadFull(b, buf[:]); err != nil { - return -1 - } - aname := strings.Trim(string(buf[0:16]), " ") - if !strings.HasPrefix(aname, name) { - return -1 - } - asize := strings.Trim(string(buf[48:58]), " ") - i, _ := strconv.Atoi(asize) - return i -} - -func skiptopkgdef(b *obj.Biobuf) bool { - /* archive header */ - p := obj.Brdline(b, '\n') - if p == "" { - return false - } - if obj.Blinelen(b) != 8 { - return false - } - if p != "!\n" { - return false - } - - /* symbol table may be first; skip it */ - sz := arsize(b, "__.GOSYMDEF") - - if sz >= 0 { - obj.Bseek(b, int64(sz), 1) - } else { - obj.Bseek(b, 8, 0) - } - - /* package export block is next */ - sz = arsize(b, "__.PKGDEF") - - if sz <= 0 { - return false - } - return true -} - -func addidir(dir string) { - if dir == "" { - return - } - - var pp **Idir - for pp = &idirs; *pp != nil; pp = &(*pp).link { - } - *pp = new(Idir) - (*pp).link = nil - (*pp).dir = dir -} - -// is this path a local name? begins with ./ or ../ or / -func islocalname(name string) bool { - return strings.HasPrefix(name, "/") || - Ctxt.Windows != 0 && len(name) >= 3 && yy_isalpha(int(name[0])) && name[1] == ':' && name[2] == '/' || - strings.HasPrefix(name, "./") || name == "." || - strings.HasPrefix(name, "../") || name == ".." -} - -func findpkg(name string) (file string, ok bool) { - if islocalname(name) { - if safemode != 0 || nolocalimports != 0 { - return "", false - } - - // try .a before .6. important for building libraries: - // if there is an array.6 in the array.a library, - // want to find all of array.a, not just array.6. - file = fmt.Sprintf("%s.a", name) - if obj.Access(file, 0) >= 0 { - return file, true - } - file = fmt.Sprintf("%s.%c", name, Thearch.Thechar) - if obj.Access(file, 0) >= 0 { - return file, true - } - return "", false - } - - // local imports should be canonicalized already. - // don't want to see "encoding/../encoding/base64" - // as different from "encoding/base64". - var q string - _ = q - if path.Clean(name) != name { - Yyerror("non-canonical import path %q (should be %q)", name, q) - return "", false - } - - for p := idirs; p != nil; p = p.link { - file = fmt.Sprintf("%s/%s.a", p.dir, name) - if obj.Access(file, 0) >= 0 { - return file, true - } - file = fmt.Sprintf("%s/%s.%c", p.dir, name, Thearch.Thechar) - if obj.Access(file, 0) >= 0 { - return file, true - } - } - - if goroot != "" { - suffix := "" - suffixsep := "" - if flag_installsuffix != "" { - suffixsep = "_" - suffix = flag_installsuffix - } else if flag_race != 0 { - suffixsep = "_" - suffix = "race" - } - - file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.a", goroot, goos, goarch, suffixsep, suffix, name) - 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) - if obj.Access(file, 0) >= 0 { - return file, true - } - } - - return "", false -} - -func fakeimport() { - importpkg = mkpkg("fake") - cannedimports("fake.6", "$$\n") -} - -func importfile(f *Val, line int) { - if f.Ctype != CTSTR { - Yyerror("import statement not a string") - fakeimport() - return - } - - if len(f.U.(string)) == 0 { - Yyerror("import path is empty") - fakeimport() - return - } - - if isbadimport(f.U.(string)) { - fakeimport() - return - } - - // The package name main is no longer reserved, - // 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.(string) == "main" { - Yyerror("cannot import \"main\"") - errorexit() - } - - if myimportpath != "" && f.U.(string) == myimportpath { - Yyerror("import %q while compiling that package (import cycle)", f.U.(string)) - errorexit() - } - - if f.U.(string) == "unsafe" { - if safemode != 0 { - Yyerror("cannot import package unsafe") - errorexit() - } - - importpkg = mkpkg(f.U.(string)) - cannedimports("unsafe.6", unsafeimport) - imported_unsafe = 1 - return - } - - path_ := f.U.(string) - if islocalname(path_) { - if path_[0] == '/' { - Yyerror("import path cannot be absolute path") - fakeimport() - return - } - - prefix := Ctxt.Pathname - if localimport != "" { - prefix = localimport - } - cleanbuf := prefix - cleanbuf += "/" - cleanbuf += path_ - cleanbuf = path.Clean(cleanbuf) - path_ = cleanbuf - - if isbadimport(path_) { - fakeimport() - return - } - } - - file, found := findpkg(path_) - if !found { - Yyerror("can't find import: %q", f.U.(string)) - errorexit() - } - - importpkg = mkpkg(path_) - - // If we already saw that package, feed a dummy statement - // to the lexer to avoid parsing export data twice. - if importpkg.Imported != 0 { - tag := "" - if importpkg.Safe { - tag = "safe" - } - - p := fmt.Sprintf("package %s %s\n$$\n", importpkg.Name, tag) - cannedimports(file, p) - return - } - - importpkg.Imported = 1 - - var err error - var imp *obj.Biobuf - imp, err = obj.Bopenr(file) - if err != nil { - Yyerror("can't open import: %q: %v", f.U.(string), err) - errorexit() - } - - if strings.HasSuffix(file, ".a") { - if !skiptopkgdef(imp) { - Yyerror("import %s: not a package file", file) - errorexit() - } - } - - // check object header - p := obj.Brdstr(imp, '\n', 1) - - if p != "empty archive" { - if !strings.HasPrefix(p, "go object ") { - Yyerror("import %s: not a go object file", file) - errorexit() - } - - q := fmt.Sprintf("%s %s %s %s", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring()) - if p[10:] != q { - Yyerror("import %s: object is [%s] expected [%s]", file, p[10:], q) - errorexit() - } - } - - // assume files move (get installed) - // so don't record the full path. - linehistpragma(file[len(file)-len(path_)-2:]) // acts as #pragma lib - - /* - * position the input right - * after $$ and return - */ - pushedio = curio - - curio.bin = imp - curio.peekc = 0 - curio.peekc1 = 0 - curio.infile = file - curio.nlsemi = 0 - typecheckok = 1 - - var c int32 - for { - c = int32(getc()) - if c == EOF { - break - } - if c != '$' { - continue - } - c = int32(getc()) - if c == EOF { - break - } - if c != '$' { - continue - } - return - } - - Yyerror("no import in %q", f.U.(string)) - unimportfile() -} - -func unimportfile() { - if curio.bin != nil { - obj.Bterm(curio.bin) - curio.bin = nil - } else { - lexlineno-- // re correct sys.6 line number - } - - curio = pushedio - - pushedio.bin = nil - incannedimport = 0 - typecheckok = 0 -} - -func cannedimports(file string, cp string) { - lexlineno++ // if sys.6 is included on line 1, - - pushedio = curio - - curio.bin = nil - curio.peekc = 0 - curio.peekc1 = 0 - curio.infile = file - curio.cp = cp - curio.nlsemi = 0 - curio.importsafe = false - - typecheckok = 1 - incannedimport = 1 -} - -func isfrog(c int) bool { - // complain about possibly invisible control characters - if c < ' ' { - return !yy_isspace(c) // exclude good white space - } - - if 0x7f <= c && c <= 0xa0 { // DEL, unicode block including unbreakable space. - return true - } - return false -} - -type Loophack struct { - v int - next *Loophack -} - -var _yylex_lstk *Loophack - -func _yylex(yylval *yySymType) int32 { - var c1 int - var escflag int - var v int64 - var cp *bytes.Buffer - var rune_ uint - var s *Sym - var h *Loophack - var str string - - prevlineno = lineno - -l0: - c := getc() - if yy_isspace(c) { - if c == '\n' && curio.nlsemi != 0 { - ungetc(c) - if Debug['x'] != 0 { - fmt.Printf("lex: implicit semi\n") - } - return ';' - } - - goto l0 - } - - lineno = lexlineno /* start of token */ - - if c >= utf8.RuneSelf { - /* all multibyte runes are alpha */ - cp = &lexbuf - cp.Reset() - - goto talph - } - - if yy_isalpha(c) { - cp = &lexbuf - cp.Reset() - goto talph - } - - if yy_isdigit(c) { - cp = &lexbuf - cp.Reset() - if c != '0' { - for { - cp.WriteByte(byte(c)) - c = getc() - if yy_isdigit(c) { - continue - } - if c == '.' { - goto casedot - } - if c == 'e' || c == 'E' || c == 'p' || c == 'P' { - goto caseep - } - if c == 'i' { - goto casei - } - goto ncu - } - } - - cp.WriteByte(byte(c)) - c = getc() - if c == 'x' || c == 'X' { - for { - cp.WriteByte(byte(c)) - c = getc() - if yy_isdigit(c) { - continue - } - if c >= 'a' && c <= 'f' { - continue - } - if c >= 'A' && c <= 'F' { - continue - } - if lexbuf.Len() == 2 { - Yyerror("malformed hex constant") - } - if c == 'p' { - goto caseep - } - goto ncu - } - } - - if c == 'p' { // 0p begins floating point zero - goto caseep - } - - c1 = 0 - for { - if !yy_isdigit(c) { - break - } - if c < '0' || c > '7' { - c1 = 1 // not octal - } - cp.WriteByte(byte(c)) - c = getc() - } - - if c == '.' { - goto casedot - } - if c == 'e' || c == 'E' { - goto caseep - } - if c == 'i' { - goto casei - } - if c1 != 0 { - Yyerror("malformed octal constant") - } - goto ncu - } - - switch c { - case EOF: - lineno = prevlineno - ungetc(EOF) - return -1 - - case '_': - cp = &lexbuf - cp.Reset() - goto talph - - case '.': - c1 = getc() - if yy_isdigit(c1) { - cp = &lexbuf - cp.Reset() - cp.WriteByte(byte(c)) - c = c1 - goto casedot - } - - if c1 == '.' { - c1 = getc() - if c1 == '.' { - c = LDDD - goto lx - } - - ungetc(c1) - c1 = '.' - } - - /* "..." */ - case '"': - lexbuf.Reset() - lexbuf.WriteString(`""`) - - cp = &strbuf - cp.Reset() - - for { - if escchar('"', &escflag, &v) { - break - } - if v < utf8.RuneSelf || escflag != 0 { - cp.WriteByte(byte(v)) - } else { - rune_ = uint(v) - cp.WriteRune(rune(rune_)) - } - } - - goto strlit - - /* `...` */ - case '`': - lexbuf.Reset() - lexbuf.WriteString("``") - - cp = &strbuf - cp.Reset() - - for { - c = int(getr()) - if c == '\r' { - continue - } - if c == EOF { - Yyerror("eof in string") - break - } - - if c == '`' { - break - } - cp.WriteRune(rune(c)) - } - - goto strlit - - /* '.' */ - case '\'': - if escchar('\'', &escflag, &v) { - Yyerror("empty character literal or unescaped ' in character literal") - v = '\'' - } - - if !escchar('\'', &escflag, &v) { - Yyerror("missing '") - ungetc(int(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") - } - litbuf = "string literal" - return LLITERAL - - case '/': - c1 = getc() - if c1 == '*' { - nl := 0 - for { - c = int(getr()) - if c == '\n' { - nl = 1 - } - for c == '*' { - c = int(getr()) - if c == '/' { - if nl != 0 { - ungetc('\n') - } - goto l0 - } - - if c == '\n' { - nl = 1 - } - } - - if c == EOF { - Yyerror("eof in comment") - errorexit() - } - } - } - - if c1 == '/' { - c = getlinepragma() - for { - if c == '\n' || c == EOF { - ungetc(c) - goto l0 - } - - c = int(getr()) - } - } - - if c1 == '=' { - c = ODIV - goto asop - } - - case ':': - c1 = getc() - if c1 == '=' { - c = LCOLAS - yylval.i = int(lexlineno) - goto lx - } - - case '*': - c1 = getc() - if c1 == '=' { - c = OMUL - goto asop - } - - case '%': - c1 = getc() - if c1 == '=' { - c = OMOD - goto asop - } - - case '+': - c1 = getc() - if c1 == '+' { - c = LINC - goto lx - } - - if c1 == '=' { - c = OADD - goto asop - } - - case '-': - c1 = getc() - if c1 == '-' { - c = LDEC - goto lx - } - - if c1 == '=' { - c = OSUB - goto asop - } - - case '>': - c1 = getc() - if c1 == '>' { - c = LRSH - c1 = getc() - if c1 == '=' { - c = ORSH - goto asop - } - - break - } - - if c1 == '=' { - c = LGE - goto lx - } - - c = LGT - - case '<': - c1 = getc() - if c1 == '<' { - c = LLSH - c1 = getc() - if c1 == '=' { - c = OLSH - goto asop - } - - break - } - - if c1 == '=' { - c = LLE - goto lx - } - - if c1 == '-' { - c = LCOMM - goto lx - } - - c = LLT - - case '=': - c1 = getc() - if c1 == '=' { - c = LEQ - goto lx - } - - case '!': - c1 = getc() - if c1 == '=' { - c = LNE - goto lx - } - - case '&': - c1 = getc() - if c1 == '&' { - c = LANDAND - goto lx - } - - if c1 == '^' { - c = LANDNOT - c1 = getc() - if c1 == '=' { - c = OANDNOT - goto asop - } - - break - } - - if c1 == '=' { - c = OAND - goto asop - } - - case '|': - c1 = getc() - if c1 == '|' { - c = LOROR - goto lx - } - - if c1 == '=' { - c = OOR - goto asop - } - - case '^': - c1 = getc() - if c1 == '=' { - c = OXOR - goto asop - } - - /* - * clumsy dance: - * to implement rule that disallows - * if T{1}[0] { ... } - * but allows - * if (T{1}[0]) { ... } - * the block bodies for if/for/switch/select - * begin with an LBODY token, not '{'. - * - * when we see the keyword, the next - * non-parenthesized '{' becomes an LBODY. - * loophack is normally 0. - * a keyword makes it go up to 1. - * parens push loophack onto a stack and go back to 0. - * a '{' with loophack == 1 becomes LBODY and disables loophack. - * - * i said it was clumsy. - */ - case '(', '[': - if loophack != 0 || _yylex_lstk != nil { - h = new(Loophack) - if h == nil { - Flusherrors() - Yyerror("out of memory") - errorexit() - } - - h.v = loophack - h.next = _yylex_lstk - _yylex_lstk = h - loophack = 0 - } - - goto lx - - case ')', ']': - if _yylex_lstk != nil { - h = _yylex_lstk - loophack = h.v - _yylex_lstk = h.next - } - - goto lx - - case '{': - if loophack == 1 { - if Debug['x'] != 0 { - fmt.Printf("%v lex: LBODY\n", Ctxt.Line(int(lexlineno))) - } - loophack = 0 - return LBODY - } - - goto lx - - default: - goto lx - } - - ungetc(c1) - -lx: - if c > 0xff { - if Debug['x'] != 0 { - fmt.Printf("%v lex: TOKEN %s\n", Ctxt.Line(int(lexlineno)), lexname(c)) - } - } else { - if Debug['x'] != 0 { - fmt.Printf("%v lex: TOKEN '%c'\n", Ctxt.Line(int(lexlineno)), c) - } - } - if isfrog(c) { - Yyerror("illegal character 0x%x", uint(c)) - goto l0 - } - - if importpkg == nil && (c == '#' || c == '$' || c == '?' || c == '@' || c == '\\') { - Yyerror("%s: unexpected %c", "syntax error", c) - goto l0 - } - - return int32(c) - -asop: - yylval.i = c // rathole to hold which asop - if Debug['x'] != 0 { - fmt.Printf("lex: TOKEN ASOP %c\n", c) - } - return LASOP - - /* - * cp is set to lexbuf and some - * prefix has been stored - */ -talph: - for { - if c >= utf8.RuneSelf { - ungetc(c) - rune_ = uint(getr()) - - // 0xb7 · is used for internal names - if !unicode.IsLetter(rune(rune_)) && !unicode.IsDigit(rune(rune_)) && (importpkg == nil || rune_ != 0xb7) { - Yyerror("invalid identifier character U+%04x", rune_) - } - cp.WriteRune(rune(rune_)) - } else if !yy_isalnum(c) && c != '_' { - break - } else { - cp.WriteByte(byte(c)) - } - c = getc() - } - - cp = nil - ungetc(c) - - s = LookupBytes(lexbuf.Bytes()) - switch s.Lexical { - case LIGNORE: - goto l0 - - case LFOR, LIF, LSWITCH, LSELECT: - loophack = 1 // see comment about loophack above - } - - if Debug['x'] != 0 { - fmt.Printf("lex: %s %s\n", s, lexname(int(s.Lexical))) - } - yylval.sym = s - return int32(s.Lexical) - -ncu: - cp = nil - ungetc(c) - - str = lexbuf.String() - 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.(*Mpint), 0) - } - - yylval.val.Ctype = CTINT - if Debug['x'] != 0 { - fmt.Printf("lex: integer literal\n") - } - litbuf = "literal " + str - return LLITERAL - -casedot: - for { - cp.WriteByte(byte(c)) - c = getc() - if !yy_isdigit(c) { - break - } - } - - if c == 'i' { - goto casei - } - if c != 'e' && c != 'E' { - goto caseout - } - -caseep: - cp.WriteByte(byte(c)) - c = getc() - if c == '+' || c == '-' { - cp.WriteByte(byte(c)) - c = getc() - } - - if !yy_isdigit(c) { - Yyerror("malformed fp constant exponent") - } - for yy_isdigit(c) { - cp.WriteByte(byte(c)) - c = getc() - } - - if c == 'i' { - goto casei - } - goto caseout - - // imaginary constant -casei: - cp = nil - - str = lexbuf.String() - 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.(*Mpcplx).Real, 0.0) - } - - yylval.val.Ctype = CTCPLX - if Debug['x'] != 0 { - fmt.Printf("lex: imaginary literal\n") - } - litbuf = "literal " + str - return LLITERAL - -caseout: - cp = nil - ungetc(c) - - str = lexbuf.String() - 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.(*Mpflt), 0.0) - } - - yylval.val.Ctype = CTFLT - if Debug['x'] != 0 { - fmt.Printf("lex: floating literal\n") - } - litbuf = "literal " + str - return LLITERAL - -strlit: - yylval.val.U = internString(cp.Bytes()) - yylval.val.Ctype = CTSTR - if Debug['x'] != 0 { - fmt.Printf("lex: string literal\n") - } - litbuf = "string literal" - return LLITERAL -} - -var internedStrings = map[string]string{} - -func internString(b []byte) string { - s, ok := internedStrings[string(b)] // string(b) here doesn't allocate - if ok { - return s - } - s = string(b) - internedStrings[s] = s - return s -} - -func more(pp *string) bool { - p := *pp - for p != "" && yy_isspace(int(p[0])) { - p = p[1:] - } - *pp = p - return p != "" -} - -/* - * read and interpret syntax that looks like - * //line parse.y:15 - * as a discontinuity in sequential line numbers. - * the next line of input comes from parse.y:15 - */ -func getlinepragma() int { - var cmd, verb, name string - - c := int(getr()) - if c == 'g' { - cp := &lexbuf - cp.Reset() - cp.WriteByte('g') // already read - for { - c = int(getr()) - if c == EOF || c >= utf8.RuneSelf { - return c - } - if c == '\n' { - break - } - cp.WriteByte(byte(c)) - } - cp = nil - - text := lexbuf.String() - - if strings.HasPrefix(text, "go:cgo_") { - pragcgo(text) - } - - cmd = text - verb = cmd - if i := strings.Index(verb, " "); i >= 0 { - verb = verb[:i] - } - - if verb == "go:linkname" { - if imported_unsafe == 0 { - Yyerror("//go:linkname only allowed in Go files that import \"unsafe\"") - } - f := strings.Fields(cmd) - if len(f) != 3 { - Yyerror("usage: //go:linkname localname linkname") - return c - } - - Lookup(f[1]).Linkname = f[2] - return c - } - - if verb == "go:nointerface" && obj.Fieldtrack_enabled != 0 { - nointerface = true - return c - } - - if verb == "go:noescape" { - noescape = true - return c - } - - if verb == "go:nosplit" { - nosplit = true - return c - } - - if verb == "go:nowritebarrier" { - if compiling_runtime == 0 { - Yyerror("//go:nowritebarrier only allowed in runtime") - } - nowritebarrier = true - return c - } - return c - } - if c != 'l' { - return c - } - for i := 1; i < 5; i++ { - c = int(getr()) - if c != int("line "[i]) { - return c - } - } - - cp := &lexbuf - cp.Reset() - linep := 0 - for { - c = int(getr()) - if c == EOF { - return c - } - if c == '\n' { - break - } - if c == ' ' { - continue - } - if c == ':' { - linep = cp.Len() + 1 - } - cp.WriteByte(byte(c)) - } - - cp = nil - - if linep == 0 { - return c - } - text := lexbuf.String() - n := 0 - for _, c := range text[linep:] { - if c < '0' || c > '9' { - goto out - } - n = n*10 + int(c) - '0' - if n > 1e8 { - Yyerror("line number out of range") - errorexit() - } - } - - if n <= 0 { - return c - } - - name = text[:linep-1] - linehistupdate(name, n) - return c - -out: - return c -} - -func getimpsym(pp *string) string { - more(pp) // skip spaces - p := *pp - if p == "" || p[0] == '"' { - return "" - } - i := 0 - for i < len(p) && !yy_isspace(int(p[i])) && p[i] != '"' { - i++ - } - sym := p[:i] - *pp = p[i:] - return sym -} - -func getquoted(pp *string) (string, bool) { - more(pp) // skip spaces - p := *pp - if p == "" || p[0] != '"' { - return "", false - } - p = p[1:] - i := strings.Index(p, `"`) - if i < 0 { - return "", false - } - *pp = p[i+1:] - return p[:i], true -} - -// Copied nearly verbatim from the C compiler's #pragma parser. -// TODO: Rewrite more cleanly once the compiler is written in Go. -func pragcgo(text string) { - var q string - - if i := strings.Index(text, " "); i >= 0 { - text, q = text[:i], text[i:] - } - - verb := text[3:] // skip "go:" - - if verb == "cgo_dynamic_linker" || verb == "dynlinker" { - var ok bool - var p string - p, ok = getquoted(&q) - if !ok { - Yyerror("usage: //go:cgo_dynamic_linker \"path\"") - return - } - pragcgobuf += fmt.Sprintf("cgo_dynamic_linker %v\n", plan9quote(p)) - return - - } - - if verb == "dynexport" { - verb = "cgo_export_dynamic" - } - if verb == "cgo_export_static" || verb == "cgo_export_dynamic" { - local := getimpsym(&q) - var remote string - if local == "" { - goto err2 - } - if !more(&q) { - pragcgobuf += fmt.Sprintf("%s %v\n", verb, plan9quote(local)) - return - } - - remote = getimpsym(&q) - if remote == "" { - goto err2 - } - pragcgobuf += fmt.Sprintf("%s %v %v\n", verb, plan9quote(local), plan9quote(remote)) - return - - err2: - Yyerror("usage: //go:%s local [remote]", verb) - return - } - - if verb == "cgo_import_dynamic" || verb == "dynimport" { - var ok bool - local := getimpsym(&q) - var p string - var remote string - if local == "" { - goto err3 - } - if !more(&q) { - pragcgobuf += fmt.Sprintf("cgo_import_dynamic %v\n", plan9quote(local)) - return - } - - remote = getimpsym(&q) - if remote == "" { - goto err3 - } - if !more(&q) { - pragcgobuf += fmt.Sprintf("cgo_import_dynamic %v %v\n", plan9quote(local), plan9quote(remote)) - return - } - - p, ok = getquoted(&q) - if !ok { - goto err3 - } - pragcgobuf += fmt.Sprintf("cgo_import_dynamic %v %v %v\n", plan9quote(local), plan9quote(remote), plan9quote(p)) - return - - err3: - Yyerror("usage: //go:cgo_import_dynamic local [remote [\"library\"]]") - return - } - - if verb == "cgo_import_static" { - local := getimpsym(&q) - if local == "" || more(&q) { - Yyerror("usage: //go:cgo_import_static local") - return - } - pragcgobuf += fmt.Sprintf("cgo_import_static %v\n", plan9quote(local)) - return - - } - - if verb == "cgo_ldflag" { - var ok bool - var p string - p, ok = getquoted(&q) - if !ok { - Yyerror("usage: //go:cgo_ldflag \"arg\"") - return - } - pragcgobuf += fmt.Sprintf("cgo_ldflag %v\n", plan9quote(p)) - return - - } -} - -type yy struct{} - -func (yy) Lex(v *yySymType) int { - return int(yylex(v)) -} - -func (yy) Error(msg string) { - Yyerror("%s", msg) -} - -var theparser yyParser -var parsing bool - -func yyparse() { - theparser = yyNewParser() - parsing = true - theparser.Parse(yy{}) - parsing = false -} - -func yylex(yylval *yySymType) int32 { - lx := int(_yylex(yylval)) - - if curio.nlsemi != 0 && lx == EOF { - // Treat EOF as "end of line" for the purposes - // of inserting a semicolon. - lx = ';' - } - - switch lx { - case LNAME, - LLITERAL, - LBREAK, - LCONTINUE, - LFALL, - LRETURN, - LINC, - LDEC, - ')', - '}', - ']': - curio.nlsemi = 1 - - default: - curio.nlsemi = 0 - } - - // Track last two tokens returned by yylex. - yyprev = yylast - - yylast = lx - return int32(lx) -} - -func getc() int { - c := curio.peekc - if c != 0 { - curio.peekc = curio.peekc1 - curio.peekc1 = 0 - goto check - } - - if curio.bin == nil { - if len(curio.cp) == 0 { - c = 0 - } else { - c = int(curio.cp[0]) - curio.cp = curio.cp[1:] - } - } else { - loop: - c = obj.Bgetc(curio.bin) - if c == 0xef { - buf, err := curio.bin.Peek(2) - if err != nil { - log.Fatalf("getc: peeking: %v", err) - } - if buf[0] == 0xbb && buf[1] == 0xbf { - yyerrorl(int(lexlineno), "Unicode (UTF-8) BOM in middle of file") - - // consume BOM bytes - obj.Bgetc(curio.bin) - obj.Bgetc(curio.bin) - goto loop - } - } - } - -check: - switch c { - case 0: - if curio.bin != nil { - Yyerror("illegal NUL byte") - break - } - fallthrough - - // insert \n at EOF - case EOF: - if curio.eofnl != 0 || curio.last == '\n' { - return EOF - } - curio.eofnl = 1 - c = '\n' - fallthrough - - case '\n': - if pushedio.bin == nil { - lexlineno++ - } - } - - curio.last = c - return c -} - -func ungetc(c int) { - curio.peekc1 = curio.peekc - curio.peekc = c - if c == '\n' && pushedio.bin == nil { - lexlineno-- - } -} - -func getr() int32 { - var buf [utf8.UTFMax]byte - - for i := 0; ; i++ { - c := getc() - if i == 0 && c < utf8.RuneSelf { - return int32(c) - } - buf[i] = byte(c) - if i+1 == len(buf) || utf8.FullRune(buf[:i+1]) { - r, w := utf8.DecodeRune(buf[:i+1]) - if r == utf8.RuneError && w == 1 { - lineno = lexlineno - // The string conversion here makes a copy for passing - // to fmt.Printf, so that buf itself does not escape and can - // be allocated on the stack. - Yyerror("illegal UTF-8 sequence % x", string(buf[:i+1])) - } - return int32(r) - } - } -} - -func escchar(e int, escflg *int, val *int64) bool { - *escflg = 0 - - c := int(getr()) - switch c { - case EOF: - Yyerror("eof in string") - return true - - case '\n': - Yyerror("newline in string") - return true - - case '\\': - break - - default: - if c == e { - return true - } - *val = int64(c) - return false - } - - u := 0 - c = int(getr()) - var i int - switch c { - case 'x': - *escflg = 1 // it's a byte - i = 2 - goto hex - - case 'u': - i = 4 - u = 1 - goto hex - - case 'U': - i = 8 - u = 1 - goto hex - - case '0', - '1', - '2', - '3', - '4', - '5', - '6', - '7': - *escflg = 1 // it's a byte - l := int64(c) - '0' - for i := 2; i > 0; i-- { - c = getc() - if c >= '0' && c <= '7' { - l = l*8 + int64(c) - '0' - continue - } - - Yyerror("non-octal character in escape sequence: %c", c) - ungetc(c) - } - - if l > 255 { - Yyerror("octal escape value > 255: %d", l) - } - - *val = l - return false - - case 'a': - c = '\a' - case 'b': - c = '\b' - case 'f': - c = '\f' - case 'n': - c = '\n' - case 'r': - c = '\r' - case 't': - c = '\t' - case 'v': - c = '\v' - case '\\': - c = '\\' - - default: - if c != e { - Yyerror("unknown escape sequence: %c", c) - } - } - - *val = int64(c) - return false - -hex: - l := int64(0) - for ; i > 0; i-- { - c = getc() - if c >= '0' && c <= '9' { - l = l*16 + int64(c) - '0' - continue - } - - if c >= 'a' && c <= 'f' { - l = l*16 + int64(c) - 'a' + 10 - continue - } - - if c >= 'A' && c <= 'F' { - l = l*16 + int64(c) - 'A' + 10 - continue - } - - Yyerror("non-hex character in escape sequence: %c", c) - ungetc(c) - break - } - - if u != 0 && (l > utf8.MaxRune || (0xd800 <= l && l < 0xe000)) { - Yyerror("invalid Unicode code point in escape sequence: %#x", l) - l = utf8.RuneError - } - - *val = l - return false -} - -var syms = []struct { - name string - lexical int - etype int - op int -}{ - /* basic types */ - {"int8", LNAME, TINT8, OXXX}, - {"int16", LNAME, TINT16, OXXX}, - {"int32", LNAME, TINT32, OXXX}, - {"int64", LNAME, TINT64, OXXX}, - {"uint8", LNAME, TUINT8, OXXX}, - {"uint16", LNAME, TUINT16, OXXX}, - {"uint32", LNAME, TUINT32, OXXX}, - {"uint64", LNAME, TUINT64, OXXX}, - {"float32", LNAME, TFLOAT32, OXXX}, - {"float64", LNAME, TFLOAT64, OXXX}, - {"complex64", LNAME, TCOMPLEX64, OXXX}, - {"complex128", LNAME, TCOMPLEX128, OXXX}, - {"bool", LNAME, TBOOL, OXXX}, - {"string", LNAME, TSTRING, OXXX}, - {"any", LNAME, TANY, OXXX}, - {"break", LBREAK, Txxx, OXXX}, - {"case", LCASE, Txxx, OXXX}, - {"chan", LCHAN, Txxx, OXXX}, - {"const", LCONST, Txxx, OXXX}, - {"continue", LCONTINUE, Txxx, OXXX}, - {"default", LDEFAULT, Txxx, OXXX}, - {"else", LELSE, Txxx, OXXX}, - {"defer", LDEFER, Txxx, OXXX}, - {"fallthrough", LFALL, Txxx, OXXX}, - {"for", LFOR, Txxx, OXXX}, - {"func", LFUNC, Txxx, OXXX}, - {"go", LGO, Txxx, OXXX}, - {"goto", LGOTO, Txxx, OXXX}, - {"if", LIF, Txxx, OXXX}, - {"import", LIMPORT, Txxx, OXXX}, - {"interface", LINTERFACE, Txxx, OXXX}, - {"map", LMAP, Txxx, OXXX}, - {"package", LPACKAGE, Txxx, OXXX}, - {"range", LRANGE, Txxx, OXXX}, - {"return", LRETURN, Txxx, OXXX}, - {"select", LSELECT, Txxx, OXXX}, - {"struct", LSTRUCT, Txxx, OXXX}, - {"switch", LSWITCH, Txxx, OXXX}, - {"type", LTYPE, Txxx, OXXX}, - {"var", LVAR, Txxx, OXXX}, - {"append", LNAME, Txxx, OAPPEND}, - {"cap", LNAME, Txxx, OCAP}, - {"close", LNAME, Txxx, OCLOSE}, - {"complex", LNAME, Txxx, OCOMPLEX}, - {"copy", LNAME, Txxx, OCOPY}, - {"delete", LNAME, Txxx, ODELETE}, - {"imag", LNAME, Txxx, OIMAG}, - {"len", LNAME, Txxx, OLEN}, - {"make", LNAME, Txxx, OMAKE}, - {"new", LNAME, Txxx, ONEW}, - {"panic", LNAME, Txxx, OPANIC}, - {"print", LNAME, Txxx, OPRINT}, - {"println", LNAME, Txxx, OPRINTN}, - {"real", LNAME, Txxx, OREAL}, - {"recover", LNAME, Txxx, ORECOVER}, - {"notwithstanding", LIGNORE, Txxx, OXXX}, - {"thetruthofthematter", LIGNORE, Txxx, OXXX}, - {"despiteallobjections", LIGNORE, Txxx, OXXX}, - {"whereas", LIGNORE, Txxx, OXXX}, - {"insofaras", LIGNORE, Txxx, OXXX}, -} - -func lexinit() { - var lex int - var s *Sym - var s1 *Sym - var t *Type - var etype int - - /* - * initialize basic types array - * initialize known symbols - */ - for i := 0; i < len(syms); i++ { - lex = syms[i].lexical - s = Lookup(syms[i].name) - s.Lexical = uint16(lex) - - etype = syms[i].etype - if etype != Txxx { - if etype < 0 || etype >= len(Types) { - Fatal("lexinit: %s bad etype", s.Name) - } - s1 = Pkglookup(syms[i].name, builtinpkg) - t = Types[etype] - if t == nil { - t = typ(etype) - t.Sym = s1 - - if etype != TANY && etype != TSTRING { - dowidth(t) - } - Types[etype] = t - } - - s1.Lexical = LNAME - s1.Def = typenod(t) - continue - } - - etype = syms[i].op - if etype != OXXX { - s1 = Pkglookup(syms[i].name, builtinpkg) - s1.Lexical = LNAME - s1.Def = Nod(ONAME, nil, nil) - s1.Def.Sym = s1 - s1.Def.Etype = uint8(etype) - } - } - - // logically, the type of a string literal. - // types[TSTRING] is the named type string - // (the type of x in var x string or var x = "hello"). - // this is the ideal form - // (the type of x in const x = "hello"). - idealstring = typ(TSTRING) - - idealbool = typ(TBOOL) - - s = Pkglookup("true", builtinpkg) - s.Def = Nodbool(true) - s.Def.Sym = Lookup("true") - s.Def.Type = idealbool - - s = Pkglookup("false", builtinpkg) - s.Def = Nodbool(false) - s.Def.Sym = Lookup("false") - s.Def.Type = idealbool - - s = Lookup("_") - s.Block = -100 - s.Def = Nod(ONAME, nil, nil) - s.Def.Sym = s - Types[TBLANK] = typ(TBLANK) - s.Def.Type = Types[TBLANK] - nblank = s.Def - - s = Pkglookup("_", builtinpkg) - s.Block = -100 - s.Def = Nod(ONAME, nil, nil) - s.Def.Sym = s - Types[TBLANK] = typ(TBLANK) - s.Def.Type = Types[TBLANK] - - Types[TNIL] = typ(TNIL) - s = Pkglookup("nil", builtinpkg) - var v Val - v.Ctype = CTNIL - s.Def = nodlit(v) - s.Def.Sym = s -} - -func lexinit1() { - // t = interface { Error() string } - rcvr := typ(TSTRUCT) - - rcvr.Type = typ(TFIELD) - rcvr.Type.Type = Ptrto(typ(TSTRUCT)) - rcvr.Funarg = 1 - in := typ(TSTRUCT) - in.Funarg = 1 - out := typ(TSTRUCT) - out.Type = typ(TFIELD) - out.Type.Type = Types[TSTRING] - out.Funarg = 1 - f := typ(TFUNC) - *getthis(f) = rcvr - *Getoutarg(f) = out - *getinarg(f) = in - f.Thistuple = 1 - f.Intuple = 0 - f.Outnamed = 0 - f.Outtuple = 1 - t := typ(TINTER) - t.Type = typ(TFIELD) - t.Type.Sym = Lookup("Error") - t.Type.Type = f - - // error type - s := Lookup("error") - - s.Lexical = LNAME - s1 := Pkglookup("error", builtinpkg) - errortype = t - errortype.Sym = s1 - s1.Lexical = LNAME - s1.Def = typenod(errortype) - - // byte alias - s = Lookup("byte") - - s.Lexical = LNAME - s1 = Pkglookup("byte", builtinpkg) - bytetype = typ(TUINT8) - bytetype.Sym = s1 - s1.Lexical = LNAME - s1.Def = typenod(bytetype) - - // rune alias - s = Lookup("rune") - - s.Lexical = LNAME - s1 = Pkglookup("rune", builtinpkg) - runetype = typ(TINT32) - runetype.Sym = s1 - s1.Lexical = LNAME - s1.Def = typenod(runetype) -} - -func lexfini() { - var s *Sym - var lex int - var etype int - var i int - - for i = 0; i < len(syms); i++ { - lex = syms[i].lexical - if lex != LNAME { - continue - } - s = Lookup(syms[i].name) - s.Lexical = uint16(lex) - - etype = syms[i].etype - if etype != Txxx && (etype != TANY || Debug['A'] != 0) && s.Def == nil { - s.Def = typenod(Types[etype]) - s.Origpkg = builtinpkg - } - - etype = syms[i].op - if etype != OXXX && s.Def == nil { - s.Def = Nod(ONAME, nil, nil) - s.Def.Sym = s - s.Def.Etype = uint8(etype) - s.Origpkg = builtinpkg - } - } - - // backend-specific builtin types (e.g. int). - for i = range Thearch.Typedefs { - s = Lookup(Thearch.Typedefs[i].Name) - if s.Def == nil { - s.Def = typenod(Types[Thearch.Typedefs[i].Etype]) - s.Origpkg = builtinpkg - } - } - - // there's only so much table-driven we can handle. - // these are special cases. - s = Lookup("byte") - - if s.Def == nil { - s.Def = typenod(bytetype) - s.Origpkg = builtinpkg - } - - s = Lookup("error") - if s.Def == nil { - s.Def = typenod(errortype) - s.Origpkg = builtinpkg - } - - s = Lookup("rune") - if s.Def == nil { - s.Def = typenod(runetype) - s.Origpkg = builtinpkg - } - - s = Lookup("nil") - if s.Def == nil { - var v Val - v.Ctype = CTNIL - s.Def = nodlit(v) - s.Def.Sym = s - s.Origpkg = builtinpkg - } - - s = Lookup("iota") - if s.Def == nil { - s.Def = Nod(OIOTA, nil, nil) - s.Def.Sym = s - s.Origpkg = builtinpkg - } - - s = Lookup("true") - if s.Def == nil { - s.Def = Nodbool(true) - s.Def.Sym = s - s.Origpkg = builtinpkg - } - - s = Lookup("false") - if s.Def == nil { - s.Def = Nodbool(false) - s.Def.Sym = s - s.Origpkg = builtinpkg - } - - nodfp = Nod(ONAME, nil, nil) - nodfp.Type = Types[TINT32] - nodfp.Xoffset = 0 - nodfp.Class = PPARAM - nodfp.Sym = Lookup(".fp") -} - -var lexn = []struct { - lex int - name string -}{ - {LANDAND, "ANDAND"}, - {LANDNOT, "ANDNOT"}, - {LASOP, "ASOP"}, - {LBREAK, "BREAK"}, - {LCASE, "CASE"}, - {LCHAN, "CHAN"}, - {LCOLAS, "COLAS"}, - {LCOMM, "<-"}, - {LCONST, "CONST"}, - {LCONTINUE, "CONTINUE"}, - {LDDD, "..."}, - {LDEC, "DEC"}, - {LDEFAULT, "DEFAULT"}, - {LDEFER, "DEFER"}, - {LELSE, "ELSE"}, - {LEQ, "EQ"}, - {LFALL, "FALL"}, - {LFOR, "FOR"}, - {LFUNC, "FUNC"}, - {LGE, "GE"}, - {LGO, "GO"}, - {LGOTO, "GOTO"}, - {LGT, "GT"}, - {LIF, "IF"}, - {LIMPORT, "IMPORT"}, - {LINC, "INC"}, - {LINTERFACE, "INTERFACE"}, - {LLE, "LE"}, - {LLITERAL, "LITERAL"}, - {LLSH, "LSH"}, - {LLT, "LT"}, - {LMAP, "MAP"}, - {LNAME, "NAME"}, - {LNE, "NE"}, - {LOROR, "OROR"}, - {LPACKAGE, "PACKAGE"}, - {LRANGE, "RANGE"}, - {LRETURN, "RETURN"}, - {LRSH, "RSH"}, - {LSELECT, "SELECT"}, - {LSTRUCT, "STRUCT"}, - {LSWITCH, "SWITCH"}, - {LTYPE, "TYPE"}, - {LVAR, "VAR"}, -} - -func lexname(lex int) string { - for i := 0; i < len(lexn); i++ { - if lexn[i].lex == lex { - return lexn[i].name - } - } - return fmt.Sprintf("LEX-%d", lex) -} - -var yytfix = []struct { - have string - want string -}{ - {"$end", "EOF"}, - {"LASOP", "op="}, - {"LBREAK", "break"}, - {"LCASE", "case"}, - {"LCHAN", "chan"}, - {"LCOLAS", ":="}, - {"LCONST", "const"}, - {"LCONTINUE", "continue"}, - {"LDDD", "..."}, - {"LDEFAULT", "default"}, - {"LDEFER", "defer"}, - {"LELSE", "else"}, - {"LFALL", "fallthrough"}, - {"LFOR", "for"}, - {"LFUNC", "func"}, - {"LGO", "go"}, - {"LGOTO", "goto"}, - {"LIF", "if"}, - {"LIMPORT", "import"}, - {"LINTERFACE", "interface"}, - {"LMAP", "map"}, - {"LNAME", "name"}, - {"LPACKAGE", "package"}, - {"LRANGE", "range"}, - {"LRETURN", "return"}, - {"LSELECT", "select"}, - {"LSTRUCT", "struct"}, - {"LSWITCH", "switch"}, - {"LTYPE", "type"}, - {"LVAR", "var"}, - {"LANDAND", "&&"}, - {"LANDNOT", "&^"}, - {"LBODY", "{"}, - {"LCOMM", "<-"}, - {"LDEC", "--"}, - {"LINC", "++"}, - {"LEQ", "=="}, - {"LGE", ">="}, - {"LGT", ">"}, - {"LLE", "<="}, - {"LLT", "<"}, - {"LLSH", "<<"}, - {"LRSH", ">>"}, - {"LOROR", "||"}, - {"LNE", "!="}, - // spell out to avoid confusion with punctuation in error messages - {"';'", "semicolon or newline"}, - {"','", "comma"}, -} - -func init() { - yyErrorVerbose = true - -Outer: - for i, s := range yyToknames { - // Apply yytfix if possible. - for _, fix := range yytfix { - if s == fix.have { - yyToknames[i] = fix.want - continue Outer - } - } - - // Turn 'x' into x. - if len(s) == 3 && s[0] == '\'' && s[2] == '\'' { - yyToknames[i] = s[1:2] - continue - } - } -} - -func pkgnotused(lineno int, path string, name string) { - // If the package was imported with a name other than the final - // import path element, show it explicitly in the error message. - // Note that this handles both renamed imports and imports of - // packages containing unconventional package declarations. - // Note that this uses / always, even on Windows, because Go import - // paths always use forward slashes. - elem := path - if i := strings.LastIndex(elem, "/"); i >= 0 { - elem = elem[i+1:] - } - if name == "" || elem == name { - yyerrorl(int(lineno), "imported and not used: %q", path) - } else { - yyerrorl(int(lineno), "imported and not used: %q as %s", path, name) - } -} - -func mkpackage(pkgname string) { - if localpkg.Name == "" { - if pkgname == "_" { - Yyerror("invalid package name _") - } - localpkg.Name = pkgname - } else { - if pkgname != localpkg.Name { - Yyerror("package %s; expected %s", pkgname, localpkg.Name) - } - for _, s := range localpkg.Syms { - if s.Def == nil { - continue - } - if s.Def.Op == OPACK { - // throw away top-level package name leftover - // from previous file. - // leave s->block set to cause redeclaration - // errors if a conflicting top-level name is - // introduced by a different file. - if !s.Def.Used && nsyntaxerrors == 0 { - pkgnotused(int(s.Def.Lineno), s.Def.Pkg.Path, s.Name) - } - s.Def = nil - continue - } - - if s.Def.Sym != s { - // throw away top-level name left over - // from previous import . "x" - if s.Def.Pack != nil && !s.Def.Pack.Used && nsyntaxerrors == 0 { - pkgnotused(int(s.Def.Pack.Lineno), s.Def.Pack.Pkg.Path, "") - s.Def.Pack.Used = true - } - - s.Def = nil - continue - } - } - } - - if outfile == "" { - p := infile - if i := strings.LastIndex(p, "/"); i >= 0 { - p = p[i+1:] - } - if Ctxt.Windows != 0 { - if i := strings.LastIndex(p, `\`); i >= 0 { - p = p[i+1:] - } - } - if i := strings.LastIndex(p, "."); i >= 0 { - p = p[:i] - } - outfile = fmt.Sprintf("%s.%c", p, Thearch.Thechar) - } -} diff --git a/src/cmd/internal/gc/mkbuiltin.go b/src/cmd/internal/gc/mkbuiltin.go deleted file mode 100644 index b2362a6f01..0000000000 --- a/src/cmd/internal/gc/mkbuiltin.go +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2009 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 - -// Generate builtin.go from $* (runtime.go and unsafe.go). -// Run this after changing runtime.go and unsafe.go -// or after changing the export metadata format in the compiler. -// Either way, you need to have a working compiler binary first. -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) - } - defer f.Close() - w := bufio.NewWriter(f) - - fmt.Fprintln(w, "// AUTO-GENERATED by mkbuiltin.go; DO NOT EDIT") - fmt.Fprintln(w, "") - fmt.Fprintln(w, "package gc") - - for _, name := range os.Args[1:] { - mkbuiltin(w, gochar, name) - } - - if err := w.Flush(); err != nil { - log.Fatal(err) - } -} - -// 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 { - log.Fatal(err) - } - obj := fmt.Sprintf("%s.%s", name, gochar) - defer os.Remove(obj) - - r, err := os.Open(obj) - if err != nil { - log.Fatal(err) - } - defer r.Close() - scanner := bufio.NewScanner(r) - - // Look for $$ that introduces imports. - for scanner.Scan() { - if strings.Contains(scanner.Text(), "$$") { - goto Begin - } - } - log.Fatal("did not find beginning of imports") - -Begin: - initfunc := fmt.Sprintf("init_%s_function", name) - - 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. - replacer := strings.NewReplacer("PACKAGE", name) - - // Process imports, stopping at $$ that closes them. - for scanner.Scan() { - p := scanner.Text() - if strings.Contains(p, "$$") { - goto End - } - - // Chop leading white space. - p = strings.TrimLeft(p, " \t") - - // Cut out decl of init_$1_function - it doesn't exist. - if strings.Contains(p, initfunc) { - continue - } - - fmt.Fprintf(w, "\t%q +\n", replacer.Replace(p)+"\n") - } - log.Fatal("did not find end of imports") - -End: - fmt.Fprintf(w, "\t\"$$\\n\"\n") -} diff --git a/src/cmd/internal/gc/mparith2.go b/src/cmd/internal/gc/mparith2.go deleted file mode 100644 index 2456dbf60a..0000000000 --- a/src/cmd/internal/gc/mparith2.go +++ /dev/null @@ -1,300 +0,0 @@ -// Copyright 2009 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 gc - -import ( - "cmd/internal/gc/big" - "cmd/internal/obj" - "fmt" -) - -/// implements fix arithmetic - -func mpsetovf(a *Mpint) { - a.Val.SetUint64(1) // avoid spurious div-zero errors - a.Ovf = true -} - -func mptestovf(a *Mpint, extra int) bool { - // We don't need to be precise here, any reasonable upper limit would do. - // For now, use existing limit so we pass all the tests unchanged. - if a.Val.BitLen()+extra > Mpprec { - mpsetovf(a) - } - return a.Ovf -} - -func mpmovefixfix(a, b *Mpint) { - a.Val.Set(&b.Val) -} - -func mpmovefltfix(a *Mpint, b *Mpflt) int { - if _, acc := b.Val.Int(&a.Val); acc == big.Exact { - return 0 - } - - const delta = 16 // a reasonably small number of bits > 0 - var t big.Float - t.SetPrec(Mpprec - delta) - - // try rounding down a little - t.SetMode(big.ToZero) - t.Set(&b.Val) - if _, acc := t.Int(&a.Val); acc == big.Exact { - return 0 - } - - // try rounding up a little - t.SetMode(big.AwayFromZero) - t.Set(&b.Val) - if _, acc := t.Int(&a.Val); acc == big.Exact { - return 0 - } - - return -1 -} - -func mpaddfixfix(a, b *Mpint, quiet int) { - if a.Ovf || b.Ovf { - if nsavederrors+nerrors == 0 { - Yyerror("ovf in mpaddfixfix") - } - mpsetovf(a) - return - } - - a.Val.Add(&a.Val, &b.Val) - - if mptestovf(a, 0) && quiet == 0 { - Yyerror("constant addition overflow") - } -} - -func mpsubfixfix(a, b *Mpint) { - if a.Ovf || b.Ovf { - if nsavederrors+nerrors == 0 { - Yyerror("ovf in mpsubfixfix") - } - mpsetovf(a) - return - } - - a.Val.Sub(&a.Val, &b.Val) - - if mptestovf(a, 0) { - Yyerror("constant subtraction overflow") - } -} - -func mpmulfixfix(a, b *Mpint) { - if a.Ovf || b.Ovf { - if nsavederrors+nerrors == 0 { - Yyerror("ovf in mpmulfixfix") - } - mpsetovf(a) - return - } - - a.Val.Mul(&a.Val, &b.Val) - - if mptestovf(a, 0) { - Yyerror("constant multiplication overflow") - } -} - -func mpdivfixfix(a, b *Mpint) { - if a.Ovf || b.Ovf { - if nsavederrors+nerrors == 0 { - Yyerror("ovf in mpdivfixfix") - } - mpsetovf(a) - return - } - - a.Val.Quo(&a.Val, &b.Val) - - if mptestovf(a, 0) { - // can only happen for div-0 which should be checked elsewhere - Yyerror("constant division overflow") - } -} - -func mpmodfixfix(a, b *Mpint) { - if a.Ovf || b.Ovf { - if nsavederrors+nerrors == 0 { - Yyerror("ovf in mpmodfixfix") - } - mpsetovf(a) - return - } - - a.Val.Rem(&a.Val, &b.Val) - - if mptestovf(a, 0) { - // should never happen - Yyerror("constant modulo overflow") - } -} - -func mporfixfix(a, b *Mpint) { - if a.Ovf || b.Ovf { - if nsavederrors+nerrors == 0 { - Yyerror("ovf in mporfixfix") - } - mpsetovf(a) - return - } - - a.Val.Or(&a.Val, &b.Val) -} - -func mpandfixfix(a, b *Mpint) { - if a.Ovf || b.Ovf { - if nsavederrors+nerrors == 0 { - Yyerror("ovf in mpandfixfix") - } - mpsetovf(a) - return - } - - a.Val.And(&a.Val, &b.Val) -} - -func mpandnotfixfix(a, b *Mpint) { - if a.Ovf || b.Ovf { - if nsavederrors+nerrors == 0 { - Yyerror("ovf in mpandnotfixfix") - } - mpsetovf(a) - return - } - - a.Val.AndNot(&a.Val, &b.Val) -} - -func mpxorfixfix(a, b *Mpint) { - if a.Ovf || b.Ovf { - if nsavederrors+nerrors == 0 { - Yyerror("ovf in mpxorfixfix") - } - mpsetovf(a) - return - } - - a.Val.Xor(&a.Val, &b.Val) -} - -// shift left by s (or right by -s) -func Mpshiftfix(a *Mpint, s int) { - switch { - case s > 0: - if mptestovf(a, s) { - Yyerror("constant shift overflow") - return - } - a.Val.Lsh(&a.Val, uint(s)) - case s < 0: - a.Val.Rsh(&a.Val, uint(-s)) - } -} - -func mplshfixfix(a, b *Mpint) { - if a.Ovf || b.Ovf { - if nsavederrors+nerrors == 0 { - Yyerror("ovf in mplshfixfix") - } - mpsetovf(a) - return - } - - s := Mpgetfix(b) - if s < 0 || s >= Mpprec { - Yyerror("stupid shift: %d", s) - Mpmovecfix(a, 0) - return - } - - Mpshiftfix(a, int(s)) -} - -func mprshfixfix(a, b *Mpint) { - if a.Ovf || b.Ovf { - if nsavederrors+nerrors == 0 { - Yyerror("ovf in mprshfixfix") - } - mpsetovf(a) - return - } - - s := Mpgetfix(b) - if s < 0 || s >= Mpprec { - Yyerror("stupid shift: %d", s) - if a.Val.Sign() < 0 { - Mpmovecfix(a, -1) - } else { - Mpmovecfix(a, 0) - } - return - } - - Mpshiftfix(a, int(-s)) -} - -func Mpcmpfixfix(a, b *Mpint) int { - return a.Val.Cmp(&b.Val) -} - -func mpcmpfixc(b *Mpint, c int64) int { - return b.Val.Cmp(big.NewInt(c)) -} - -func mpnegfix(a *Mpint) { - a.Val.Neg(&a.Val) -} - -func Mpgetfix(a *Mpint) int64 { - if a.Ovf { - if nsavederrors+nerrors == 0 { - Yyerror("constant overflow") - } - return 0 - } - - return a.Val.Int64() -} - -func Mpmovecfix(a *Mpint, c int64) { - a.Val.SetInt64(c) -} - -func mpatofix(a *Mpint, as string) { - _, ok := a.Val.SetString(as, 0) - if !ok { - // required syntax is [+-][0[x]]d* - // At the moment we lose precise error cause; - // the old code distinguished between: - // - malformed hex constant - // - malformed octal constant - // - malformed decimal constant - // TODO(gri) use different conversion function - Yyerror("malformed integer constant: %s", as) - a.Val.SetUint64(0) - return - } - if mptestovf(a, 0) { - Yyerror("constant too large: %s", as) - } -} - -func (x *Mpint) String() string { - return Bconv(x, 0) -} - -func Bconv(xval *Mpint, flag int) string { - if flag&obj.FmtSharp != 0 { - return fmt.Sprintf("%#x", &xval.Val) - } - return xval.Val.String() -} diff --git a/src/cmd/internal/gc/mparith3.go b/src/cmd/internal/gc/mparith3.go deleted file mode 100644 index 2700b64a89..0000000000 --- a/src/cmd/internal/gc/mparith3.go +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright 2009 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 gc - -import ( - "cmd/internal/gc/big" - "cmd/internal/obj" - "fmt" - "math" -) - -/// implements float arihmetic - -func newMpflt() *Mpflt { - var a Mpflt - a.Val.SetPrec(Mpprec) - return &a -} - -func Mpmovefixflt(a *Mpflt, b *Mpint) { - if b.Ovf { - // sign doesn't really matter but copy anyway - a.Val.SetInf(b.Val.Sign() < 0) - return - } - a.Val.SetInt(&b.Val) -} - -func mpmovefltflt(a *Mpflt, b *Mpflt) { - a.Val.Set(&b.Val) -} - -func mpaddfltflt(a *Mpflt, b *Mpflt) { - if Mpdebug { - fmt.Printf("\n%v + %v", a, b) - } - - a.Val.Add(&a.Val, &b.Val) - - if Mpdebug { - fmt.Printf(" = %v\n\n", a) - } -} - -func mpaddcflt(a *Mpflt, c float64) { - var b Mpflt - - Mpmovecflt(&b, c) - mpaddfltflt(a, &b) -} - -func mpsubfltflt(a *Mpflt, b *Mpflt) { - if Mpdebug { - fmt.Printf("\n%v - %v", a, b) - } - - a.Val.Sub(&a.Val, &b.Val) - - if Mpdebug { - fmt.Printf(" = %v\n\n", a) - } -} - -func mpmulfltflt(a *Mpflt, b *Mpflt) { - if Mpdebug { - fmt.Printf("%v\n * %v\n", a, b) - } - - a.Val.Mul(&a.Val, &b.Val) - - if Mpdebug { - fmt.Printf(" = %v\n\n", a) - } -} - -func mpmulcflt(a *Mpflt, c float64) { - var b Mpflt - - Mpmovecflt(&b, c) - mpmulfltflt(a, &b) -} - -func mpdivfltflt(a *Mpflt, b *Mpflt) { - if Mpdebug { - fmt.Printf("%v\n / %v\n", a, b) - } - - a.Val.Quo(&a.Val, &b.Val) - - if Mpdebug { - fmt.Printf(" = %v\n\n", a) - } -} - -func mpcmpfltflt(a *Mpflt, b *Mpflt) int { - return a.Val.Cmp(&b.Val) -} - -func mpcmpfltc(b *Mpflt, c float64) int { - var a Mpflt - - Mpmovecflt(&a, c) - 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: - // 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() - default: - panic("unreachable") - } - - // check for overflow - if math.IsInf(x, 0) && nsavederrors+nerrors == 0 { - Yyerror("mpgetflt ovf") - } - - return x -} - -func mpgetflt(a *Mpflt) float64 { - return mpgetfltN(a, 53, -1023) -} - -func mpgetflt32(a *Mpflt) float64 { - return mpgetfltN(a, 24, -127) -} - -func Mpmovecflt(a *Mpflt, c float64) { - if Mpdebug { - fmt.Printf("\nconst %g", c) - } - - a.Val.SetFloat64(c) - - if Mpdebug { - fmt.Printf(" = %v\n", a) - } -} - -func mpnegflt(a *Mpflt) { - a.Val.Neg(&a.Val) -} - -// -// floating point input -// required syntax is [+-]d*[.]d*[e[+-]d*] or [+-]0xH*[e[+-]d*] -// -func mpatoflt(a *Mpflt, as string) { - for len(as) > 0 && (as[0] == ' ' || as[0] == '\t') { - as = as[1:] - } - - f, ok := a.Val.SetString(as) - if !ok { - // At the moment we lose precise error cause; - // the old code additionally distinguished between: - // - malformed hex constant - // - decimal point in hex constant - // - constant exponent out of range - // - decimal point and binary point in constant - // TODO(gri) use different conversion function or check separately - Yyerror("malformed constant: %s", as) - a.Val.SetUint64(0) - } - - if f.IsInf() { - Yyerror("constant too large: %s", as) - a.Val.SetUint64(0) - } -} - -func (f *Mpflt) String() string { - return Fconv(f, 0) -} - -func Fconv(fvp *Mpflt, flag int) string { - if flag&obj.FmtSharp == 0 { - return fvp.Val.Format('b', 0) - } - - // use decimal format for error messages - - // determine sign - f := &fvp.Val - var sign string - if fvp.Val.Signbit() { - sign = "-" - f = new(big.Float).Abs(f) - } else if flag&obj.FmtSign != 0 { - sign = "+" - } - - // Use fmt formatting if in float64 range (common case). - if x, _ := f.Float64(); !math.IsInf(x, 0) { - return fmt.Sprintf("%s%.6g", sign, x) - } - - // Out of float64 range. Do approximate manual to decimal - // conversion to avoid precise but possibly slow Float - // formatting. The exponent is > 0 since a negative out- - // of-range exponent would have underflowed and led to 0. - // f = mant * 2**exp - var mant big.Float - exp := float64(f.MantExp(&mant)) // 0.5 <= mant < 1.0, exp > 0 - - // approximate float64 mantissa m and decimal exponent d - // f ~ m * 10**d - m, _ := mant.Float64() // 0.5 <= m < 1.0 - d := exp * (math.Ln2 / math.Ln10) // log_10(2) - - // adjust m for truncated (integer) decimal exponent e - e := int64(d) - m *= math.Pow(10, d-float64(e)) - for m >= 10 { - m /= 10 - e++ - } - - return fmt.Sprintf("%s%.5fe+%d", sign, m, e) -} diff --git a/src/cmd/internal/gc/obj.go b/src/cmd/internal/gc/obj.go deleted file mode 100644 index 2afd786dc1..0000000000 --- a/src/cmd/internal/gc/obj.go +++ /dev/null @@ -1,434 +0,0 @@ -// Copyright 2009 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 gc - -import ( - "cmd/internal/obj" - "fmt" - "strconv" -) - -/* - * architecture-independent object file output - */ -const ( - ArhdrSize = 60 -) - -func formathdr(arhdr []byte, name string, size int64) { - copy(arhdr[:], fmt.Sprintf("%-16s%-12d%-6d%-6d%-8o%-10d`\n", name, 0, 0, 0, 0644, size)) -} - -func dumpobj() { - var err error - bout, err = obj.Bopenw(outfile) - if err != nil { - Flusherrors() - fmt.Printf("can't create %s: %v\n", outfile, err) - errorexit() - } - - startobj := int64(0) - var arhdr [ArhdrSize]byte - if writearchive != 0 { - obj.Bwritestring(bout, "!\n") - arhdr = [ArhdrSize]byte{} - bout.Write(arhdr[:]) - startobj = obj.Boffset(bout) - } - - fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring()) - dumpexport() - - if writearchive != 0 { - bout.Flush() - size := obj.Boffset(bout) - startobj - if size&1 != 0 { - obj.Bputc(bout, 0) - } - obj.Bseek(bout, startobj-ArhdrSize, 0) - formathdr(arhdr[:], "__.PKGDEF", size) - bout.Write(arhdr[:]) - bout.Flush() - - obj.Bseek(bout, startobj+size+(size&1), 0) - arhdr = [ArhdrSize]byte{} - bout.Write(arhdr[:]) - startobj = obj.Boffset(bout) - fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring()) - } - - if pragcgobuf != "" { - if writearchive != 0 { - // write empty export section; must be before cgo section - fmt.Fprintf(bout, "\n$$\n\n$$\n\n") - } - - fmt.Fprintf(bout, "\n$$ // cgo\n") - fmt.Fprintf(bout, "%s\n$$\n\n", pragcgobuf) - } - - fmt.Fprintf(bout, "\n!\n") - - var externs *NodeList - if externdcl != nil { - externs = externdcl.End - } - - dumpglobls() - dumptypestructs() - - // Dump extra globals. - tmp := externdcl - - if externs != nil { - externdcl = externs.Next - } - dumpglobls() - externdcl = tmp - - zero := Pkglookup("zerovalue", Runtimepkg) - ggloblsym(zero, int32(zerosize), obj.DUPOK|obj.RODATA) - - dumpdata() - obj.Writeobjdirect(Ctxt, bout) - - if writearchive != 0 { - bout.Flush() - size := obj.Boffset(bout) - startobj - if size&1 != 0 { - obj.Bputc(bout, 0) - } - obj.Bseek(bout, startobj-ArhdrSize, 0) - name := fmt.Sprintf("_go_.%c", Thearch.Thechar) - formathdr(arhdr[:], name, size) - bout.Write(arhdr[:]) - } - - obj.Bterm(bout) -} - -func dumpglobls() { - var n *Node - - // add globals - for l := externdcl; l != nil; l = l.Next { - n = l.N - if n.Op != ONAME { - continue - } - - if n.Type == nil { - Fatal("external %v nil type\n", n) - } - if n.Class == PFUNC { - continue - } - if n.Sym.Pkg != localpkg { - continue - } - dowidth(n.Type) - - ggloblnod(n) - } - - for l := funcsyms; l != nil; l = l.Next { - n = l.N - dsymptr(n.Sym, 0, n.Sym.Def.Func.Shortname.Sym, 0) - ggloblsym(n.Sym, int32(Widthptr), obj.DUPOK|obj.RODATA) - } - - // Do not reprocess funcsyms on next dumpglobls call. - funcsyms = nil -} - -func Bputname(b *obj.Biobuf, s *obj.LSym) { - obj.Bwritestring(b, s.Name) - obj.Bputc(b, 0) -} - -func Linksym(s *Sym) *obj.LSym { - if s == nil { - return nil - } - if s.Lsym != nil { - return s.Lsym - } - var name string - if isblanksym(s) { - name = "_" - } else if s.Linkname != "" { - name = s.Linkname - } else { - name = s.Pkg.Prefix + "." + s.Name - } - - ls := obj.Linklookup(Ctxt, name, 0) - s.Lsym = ls - return ls -} - -func duintxx(s *Sym, off int, v uint64, wid int) int { - // Update symbol data directly instead of generating a - // DATA instruction that liblink will have to interpret later. - // This reduces compilation time and memory usage. - off = int(Rnd(int64(off), int64(wid))) - - return int(obj.Setuintxx(Ctxt, Linksym(s), int64(off), v, int64(wid))) -} - -func duint8(s *Sym, off int, v uint8) int { - return duintxx(s, off, uint64(v), 1) -} - -func duint16(s *Sym, off int, v uint16) int { - return duintxx(s, off, uint64(v), 2) -} - -func duint32(s *Sym, off int, v uint32) int { - return duintxx(s, off, uint64(v), 4) -} - -func duint64(s *Sym, off int, v uint64) int { - return duintxx(s, off, v, 8) -} - -func duintptr(s *Sym, off int, v uint64) int { - return duintxx(s, off, v, Widthptr) -} - -var stringsym_gen int - -func stringsym(s string) *Sym { - var symname string - var pkg *Pkg - if len(s) > 100 { - // huge strings are made static to avoid long names - stringsym_gen++ - symname = fmt.Sprintf(".gostring.%d", stringsym_gen) - - pkg = localpkg - } else { - // small strings get named by their contents, - // so that multiple modules using the same string - // can share it. - symname = strconv.Quote(s) - pkg = gostringpkg - } - - sym := Pkglookup(symname, pkg) - - // SymUniq flag indicates that data is generated already - if sym.Flags&SymUniq != 0 { - return sym - } - sym.Flags |= SymUniq - sym.Def = newname(sym) - - off := 0 - - // string header - off = dsymptr(sym, off, sym, Widthptr+Widthint) - off = duintxx(sym, off, uint64(len(s)), Widthint) - - // string data - var m int - for n := 0; n < len(s); n += m { - m = 8 - if m > len(s)-n { - m = len(s) - n - } - off = dsname(sym, off, s[n:n+m]) - } - - off = duint8(sym, off, 0) // terminating NUL for runtime - off = (off + Widthptr - 1) &^ (Widthptr - 1) // round to pointer alignment - ggloblsym(sym, int32(off), obj.DUPOK|obj.RODATA|obj.LOCAL) - - return sym -} - -var slicebytes_gen int - -func slicebytes(nam *Node, s string, len int) { - var m int - - slicebytes_gen++ - symname := fmt.Sprintf(".gobytes.%d", slicebytes_gen) - sym := Pkglookup(symname, localpkg) - sym.Def = newname(sym) - - off := 0 - for n := 0; n < len; n += m { - m = 8 - if m > len-n { - m = len - n - } - off = dsname(sym, off, s[n:n+m]) - } - - ggloblsym(sym, int32(off), obj.NOPTR|obj.LOCAL) - - if nam.Op != ONAME { - Fatal("slicebytes %v", nam) - } - off = int(nam.Xoffset) - off = dsymptr(nam.Sym, off, sym, 0) - off = duintxx(nam.Sym, off, uint64(len), Widthint) - duintxx(nam.Sym, off, uint64(len), Widthint) -} - -func dstringptr(s *Sym, off int, str string) int { - off = int(Rnd(int64(off), int64(Widthptr))) - p := Thearch.Gins(obj.ADATA, nil, nil) - p.From.Type = obj.TYPE_MEM - p.From.Name = obj.NAME_EXTERN - p.From.Sym = Linksym(s) - p.From.Offset = int64(off) - p.From3.Type = obj.TYPE_CONST - p.From3.Offset = int64(Widthptr) - - Datastring(str+"\x00", &p.To) // TODO(rsc): Remove NUL - p.To.Type = obj.TYPE_ADDR - p.To.Etype = Simtype[TINT] - off += Widthptr - - return off -} - -func Datastring(s string, a *obj.Addr) { - sym := stringsym(s) - a.Type = obj.TYPE_MEM - a.Name = obj.NAME_EXTERN - a.Sym = Linksym(sym) - a.Node = sym.Def - a.Offset = int64(Widthptr) + int64(Widthint) // skip header - a.Etype = Simtype[TINT] -} - -func datagostring(sval string, a *obj.Addr) { - sym := stringsym(sval) - a.Type = obj.TYPE_MEM - a.Name = obj.NAME_EXTERN - a.Sym = Linksym(sym) - a.Node = sym.Def - a.Offset = 0 // header - a.Etype = TSTRING -} - -func dgostringptr(s *Sym, off int, str string) int { - if str == "" { - return duintptr(s, off, 0) - } - return dgostrlitptr(s, off, &str) -} - -func dgostrlitptr(s *Sym, off int, lit *string) int { - if lit == nil { - return duintptr(s, off, 0) - } - off = int(Rnd(int64(off), int64(Widthptr))) - p := Thearch.Gins(obj.ADATA, nil, nil) - p.From.Type = obj.TYPE_MEM - p.From.Name = obj.NAME_EXTERN - p.From.Sym = Linksym(s) - p.From.Offset = int64(off) - p.From3.Type = obj.TYPE_CONST - p.From3.Offset = int64(Widthptr) - datagostring(*lit, &p.To) - p.To.Type = obj.TYPE_ADDR - p.To.Etype = Simtype[TINT] - off += Widthptr - - return off -} - -func dsname(s *Sym, off int, t string) int { - p := Thearch.Gins(obj.ADATA, nil, nil) - p.From.Type = obj.TYPE_MEM - p.From.Name = obj.NAME_EXTERN - p.From.Offset = int64(off) - p.From.Sym = Linksym(s) - p.From3.Type = obj.TYPE_CONST - p.From3.Offset = int64(len(t)) - - p.To.Type = obj.TYPE_SCONST - p.To.Val = t - return off + len(t) -} - -func dsymptr(s *Sym, off int, x *Sym, xoff int) int { - off = int(Rnd(int64(off), int64(Widthptr))) - - p := Thearch.Gins(obj.ADATA, nil, nil) - p.From.Type = obj.TYPE_MEM - p.From.Name = obj.NAME_EXTERN - p.From.Sym = Linksym(s) - p.From.Offset = int64(off) - p.From3.Type = obj.TYPE_CONST - p.From3.Offset = int64(Widthptr) - p.To.Type = obj.TYPE_ADDR - p.To.Name = obj.NAME_EXTERN - p.To.Sym = Linksym(x) - p.To.Offset = int64(xoff) - off += Widthptr - - return off -} - -func gdata(nam *Node, nr *Node, wid int) { - if nr.Op == OLITERAL { - switch nr.Val.Ctype { - case CTCPLX: - gdatacomplex(nam, nr.Val.U.(*Mpcplx)) - return - - case CTSTR: - gdatastring(nam, nr.Val.U.(string)) - return - } - } - - p := Thearch.Gins(obj.ADATA, nam, nr) - p.From3.Type = obj.TYPE_CONST - p.From3.Offset = int64(wid) -} - -func gdatacomplex(nam *Node, cval *Mpcplx) { - w := cplxsubtype(int(nam.Type.Etype)) - w = int(Types[w].Width) - - p := Thearch.Gins(obj.ADATA, nam, nil) - p.From3.Type = obj.TYPE_CONST - p.From3.Offset = int64(w) - p.To.Type = obj.TYPE_FCONST - p.To.Val = mpgetflt(&cval.Real) - - p = Thearch.Gins(obj.ADATA, nam, nil) - p.From3.Type = obj.TYPE_CONST - p.From3.Offset = int64(w) - p.From.Offset += int64(w) - p.To.Type = obj.TYPE_FCONST - p.To.Val = mpgetflt(&cval.Imag) -} - -func gdatastring(nam *Node, sval string) { - var nod1 Node - - p := Thearch.Gins(obj.ADATA, nam, nil) - Datastring(sval, &p.To) - p.From3.Type = obj.TYPE_CONST - p.From3.Offset = Types[Tptr].Width - p.To.Type = obj.TYPE_ADDR - - //print("%P\n", p); - - Nodconst(&nod1, Types[TINT], int64(len(sval))) - - p = Thearch.Gins(obj.ADATA, nam, &nod1) - p.From3.Type = obj.TYPE_CONST - p.From3.Offset = int64(Widthint) - p.From.Offset += int64(Widthptr) -} diff --git a/src/cmd/internal/gc/opnames.go b/src/cmd/internal/gc/opnames.go deleted file mode 100644 index fc03ec6666..0000000000 --- a/src/cmd/internal/gc/opnames.go +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2011 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 gc - -// auto generated by go tool dist -var opnames = []string{ - OXXX: "XXX", - ONAME: "NAME", - ONONAME: "NONAME", - OTYPE: "TYPE", - OPACK: "PACK", - OLITERAL: "LITERAL", - OADD: "ADD", - OSUB: "SUB", - OOR: "OR", - OXOR: "XOR", - OADDSTR: "ADDSTR", - OADDR: "ADDR", - OANDAND: "ANDAND", - OAPPEND: "APPEND", - OARRAYBYTESTR: "ARRAYBYTESTR", - OARRAYBYTESTRTMP: "ARRAYBYTESTRTMP", - OARRAYRUNESTR: "ARRAYRUNESTR", - OSTRARRAYBYTE: "STRARRAYBYTE", - OSTRARRAYBYTETMP: "STRARRAYBYTETMP", - OSTRARRAYRUNE: "STRARRAYRUNE", - OAS: "AS", - OAS2: "AS2", - OAS2FUNC: "AS2FUNC", - OAS2RECV: "AS2RECV", - OAS2MAPR: "AS2MAPR", - OAS2DOTTYPE: "AS2DOTTYPE", - OASOP: "ASOP", - OCALL: "CALL", - OCALLFUNC: "CALLFUNC", - OCALLMETH: "CALLMETH", - OCALLINTER: "CALLINTER", - OCALLPART: "CALLPART", - OCAP: "CAP", - OCLOSE: "CLOSE", - OCLOSURE: "CLOSURE", - OCMPIFACE: "CMPIFACE", - OCMPSTR: "CMPSTR", - OCOMPLIT: "COMPLIT", - OMAPLIT: "MAPLIT", - OSTRUCTLIT: "STRUCTLIT", - OARRAYLIT: "ARRAYLIT", - OPTRLIT: "PTRLIT", - OCONV: "CONV", - OCONVIFACE: "CONVIFACE", - OCONVNOP: "CONVNOP", - OCOPY: "COPY", - ODCL: "DCL", - ODCLFUNC: "DCLFUNC", - ODCLFIELD: "DCLFIELD", - ODCLCONST: "DCLCONST", - ODCLTYPE: "DCLTYPE", - ODELETE: "DELETE", - ODOT: "DOT", - ODOTPTR: "DOTPTR", - ODOTMETH: "DOTMETH", - ODOTINTER: "DOTINTER", - OXDOT: "XDOT", - ODOTTYPE: "DOTTYPE", - ODOTTYPE2: "DOTTYPE2", - OEQ: "EQ", - ONE: "NE", - OLT: "LT", - OLE: "LE", - OGE: "GE", - OGT: "GT", - OIND: "IND", - OINDEX: "INDEX", - OINDEXMAP: "INDEXMAP", - OKEY: "KEY", - OPARAM: "PARAM", - OLEN: "LEN", - OMAKE: "MAKE", - OMAKECHAN: "MAKECHAN", - OMAKEMAP: "MAKEMAP", - OMAKESLICE: "MAKESLICE", - OMUL: "MUL", - ODIV: "DIV", - OMOD: "MOD", - OLSH: "LSH", - ORSH: "RSH", - OAND: "AND", - OANDNOT: "ANDNOT", - ONEW: "NEW", - ONOT: "NOT", - OCOM: "COM", - OPLUS: "PLUS", - OMINUS: "MINUS", - OOROR: "OROR", - OPANIC: "PANIC", - OPRINT: "PRINT", - OPRINTN: "PRINTN", - OPAREN: "PAREN", - OSEND: "SEND", - OSLICE: "SLICE", - OSLICEARR: "SLICEARR", - OSLICESTR: "SLICESTR", - OSLICE3: "SLICE3", - OSLICE3ARR: "SLICE3ARR", - ORECOVER: "RECOVER", - ORECV: "RECV", - ORUNESTR: "RUNESTR", - OSELRECV: "SELRECV", - OSELRECV2: "SELRECV2", - OIOTA: "IOTA", - OREAL: "REAL", - OIMAG: "IMAG", - OCOMPLEX: "COMPLEX", - OBLOCK: "BLOCK", - OBREAK: "BREAK", - OCASE: "CASE", - OXCASE: "XCASE", - OCONTINUE: "CONTINUE", - ODEFER: "DEFER", - OEMPTY: "EMPTY", - OFALL: "FALL", - OXFALL: "XFALL", - OFOR: "FOR", - OGOTO: "GOTO", - OIF: "IF", - OLABEL: "LABEL", - OPROC: "PROC", - ORANGE: "RANGE", - ORETURN: "RETURN", - OSELECT: "SELECT", - OSWITCH: "SWITCH", - OTYPESW: "TYPESW", - OTCHAN: "TCHAN", - OTMAP: "TMAP", - OTSTRUCT: "TSTRUCT", - OTINTER: "TINTER", - OTFUNC: "TFUNC", - OTARRAY: "TARRAY", - ODDD: "DDD", - ODDDARG: "DDDARG", - OINLCALL: "INLCALL", - OEFACE: "EFACE", - OITAB: "ITAB", - OSPTR: "SPTR", - OCLOSUREVAR: "CLOSUREVAR", - OCFUNC: "CFUNC", - OCHECKNIL: "CHECKNIL", - OVARKILL: "VARKILL", - OREGISTER: "REGISTER", - OINDREG: "INDREG", - OCMP: "CMP", - ODEC: "DEC", - OINC: "INC", - OEXTEND: "EXTEND", - OHMUL: "HMUL", - OLROT: "LROT", - ORROTC: "RROTC", - ORETJMP: "RETJMP", - OEND: "END", -} diff --git a/src/cmd/internal/gc/order.go b/src/cmd/internal/gc/order.go deleted file mode 100644 index b3fd282a68..0000000000 --- a/src/cmd/internal/gc/order.go +++ /dev/null @@ -1,1170 +0,0 @@ -// Copyright 2012 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 gc - -import ( - "fmt" - "strings" -) - -// Rewrite tree to use separate statements to enforce -// order of evaluation. Makes walk easier, because it -// can (after this runs) reorder at will within an expression. -// -// Rewrite x op= y into x = x op y. -// -// Introduce temporaries as needed by runtime routines. -// For example, the map runtime routines take the map key -// by reference, so make sure all map keys are addressable -// by copying them to temporaries as needed. -// The same is true for channel operations. -// -// Arrange that map index expressions only appear in direct -// assignments x = m[k] or m[k] = x, never in larger expressions. -// -// Arrange that receive expressions only appear in direct assignments -// x = <-c or as standalone statements <-c, never in larger expressions. - -// TODO(rsc): The temporary introduction during multiple assignments -// should be moved into this file, so that the temporaries can be cleaned -// and so that conversions implicit in the OAS2FUNC and OAS2RECV -// nodes can be made explicit and then have their temporaries cleaned. - -// TODO(rsc): Goto and multilevel break/continue can jump over -// inserted VARKILL annotations. Work out a way to handle these. -// The current implementation is safe, in that it will execute correctly. -// But it won't reuse temporaries as aggressively as it might, and -// it can result in unnecessary zeroing of those variables in the function -// prologue. - -// Order holds state during the ordering process. -type Order struct { - out *NodeList // list of generated statements - temp *NodeList // head of stack of temporary variables - free *NodeList // free list of NodeList* structs (for use in temp) -} - -// Order rewrites fn->nbody to apply the ordering constraints -// described in the comment at the top of the file. -func order(fn *Node) { - if Debug['W'] > 1 { - s := fmt.Sprintf("\nbefore order %v", fn.Nname.Sym) - dumplist(s, fn.Nbody) - } - - orderblock(&fn.Nbody) -} - -// Ordertemp allocates a new temporary with the given type, -// pushes it onto the temp stack, and returns it. -// If clear is true, ordertemp emits code to zero the temporary. -func ordertemp(t *Type, order *Order, clear bool) *Node { - var_ := temp(t) - if clear { - a := Nod(OAS, var_, nil) - typecheck(&a, Etop) - order.out = list(order.out, a) - } - - l := order.free - if l == nil { - l = new(NodeList) - } - order.free = l.Next - l.Next = order.temp - l.N = var_ - order.temp = l - return var_ -} - -// Ordercopyexpr behaves like ordertemp but also emits -// code to initialize the temporary to the value n. -// -// The clear argument is provided for use when the evaluation -// of tmp = n turns into a function call that is passed a pointer -// to the temporary as the output space. If the call blocks before -// tmp has been written, the garbage collector will still treat the -// temporary as live, so we must zero it before entering that call. -// Today, this only happens for channel receive operations. -// (The other candidate would be map access, but map access -// returns a pointer to the result data instead of taking a pointer -// to be filled in.) -func ordercopyexpr(n *Node, t *Type, order *Order, clear int) *Node { - var_ := ordertemp(t, order, clear != 0) - a := Nod(OAS, var_, n) - typecheck(&a, Etop) - order.out = list(order.out, a) - return var_ -} - -// Ordercheapexpr returns a cheap version of n. -// The definition of cheap is that n is a variable or constant. -// 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) -} - -// Ordersafeexpr returns a safe version of n. -// The definition of safe is that n can appear multiple times -// without violating the semantics of the original program, -// and that assigning to the safe version has the same effect -// as assigning to the original n. -// -// The intended use is to apply to x when rewriting x += y into x = x + y. -func ordersafeexpr(n *Node, order *Order) *Node { - switch n.Op { - case ONAME, OLITERAL: - return n - - case ODOT, OLEN, OCAP: - l := ordersafeexpr(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 - - case ODOTPTR, OIND: - 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 - - case OINDEX, OINDEXMAP: - var l *Node - if Isfixedarray(n.Left.Type) { - l = ordersafeexpr(n.Left, order) - } else { - l = ordercheapexpr(n.Left, order) - } - r := ordercheapexpr(n.Right, order) - if l == n.Left && r == n.Right { - return n - } - a := Nod(OXXX, nil, nil) - *a = *n - a.Orig = a - a.Left = l - a.Right = r - typecheck(&a, Erv) - return a - } - - Fatal("ordersafeexpr %v", Oconv(int(n.Op), 0)) - return nil // not reached -} - -// Istemp reports whether n is a temporary variable. -func istemp(n *Node) bool { - if n.Op != ONAME { - return false - } - return strings.HasPrefix(n.Sym.Name, "autotmp_") -} - -// Isaddrokay reports whether it is okay to pass n's address to runtime routines. -// Taking the address of a variable makes the liveness and optimization analyses -// lose track of where the variable's lifetime ends. To avoid hurting the analyses -// of ordinary stack variables, those are not 'isaddrokay'. Temporaries are okay, -// because we emit explicit VARKILL instructions marking the end of those -// temporaries' lifetimes. -func isaddrokay(n *Node) bool { - return islvalue(n) && (n.Op != ONAME || n.Class == PEXTERN || istemp(n)) -} - -// Orderaddrtemp ensures that *np is okay to pass by address to runtime routines. -// If the original argument *np is not okay, orderaddrtemp creates a tmp, emits -// tmp = *np, and then sets *np to the tmp variable. -func orderaddrtemp(np **Node, order *Order) { - n := *np - if isaddrokay(n) { - return - } - *np = ordercopyexpr(n, n.Type, order, 0) -} - -// Marktemp returns the top of the temporary variable stack. -func marktemp(order *Order) *NodeList { - return order.temp -} - -// Poptemp pops temporaries off the stack until reaching the mark, -// which must have been returned by marktemp. -func poptemp(mark *NodeList, order *Order) { - var l *NodeList - - for { - l = order.temp - if l == mark { - break - } - order.temp = l.Next - l.Next = order.free - order.free = l - } -} - -// Cleantempnopop emits to *out VARKILL instructions for each temporary -// above the mark on the temporary stack, but it does not pop them -// from the stack. -func cleantempnopop(mark *NodeList, order *Order, out **NodeList) { - var kill *Node - - for l := order.temp; l != mark; l = l.Next { - kill = Nod(OVARKILL, l.N, nil) - typecheck(&kill, Etop) - *out = list(*out, kill) - } -} - -// Cleantemp emits VARKILL instructions for each temporary above the -// mark on the temporary stack and removes them from the stack. -func cleantemp(top *NodeList, order *Order) { - cleantempnopop(top, order, &order.out) - poptemp(top, order) -} - -// Orderstmtlist orders each of the statements in the list. -func orderstmtlist(l *NodeList, order *Order) { - for ; l != nil; l = l.Next { - orderstmt(l.N, order) - } -} - -// Orderblock orders the block of statements *l onto a new list, -// and then replaces *l with that list. -func orderblock(l **NodeList) { - var order Order - mark := marktemp(&order) - orderstmtlist(*l, &order) - cleantemp(mark, &order) - *l = order.out -} - -// Orderexprinplace orders the side effects in *np and -// leaves them as the init list of the final *np. -func orderexprinplace(np **Node, outer *Order) { - n := *np - var order Order - orderexpr(&n, &order, nil) - addinit(&n, order.out) - - // insert new temporaries from order - // at head of outer list. - lp := &order.temp - - for *lp != nil { - lp = &(*lp).Next - } - *lp = outer.temp - outer.temp = order.temp - - *np = n -} - -// Orderstmtinplace orders the side effects of the single statement *np -// and replaces it with the resulting statement list. -func orderstmtinplace(np **Node) { - n := *np - var order Order - mark := marktemp(&order) - orderstmt(n, &order) - cleantemp(mark, &order) - *np = liststmt(order.out) -} - -// Orderinit moves n's init list to order->out. -func orderinit(n *Node, order *Order) { - orderstmtlist(n.Ninit, order) - n.Ninit = nil -} - -// Ismulticall reports whether the list l is f() for a multi-value function. -// Such an f() could appear as the lone argument to a multi-arg function. -func ismulticall(l *NodeList) bool { - // one arg only - if l == nil || l.Next != nil { - return false - } - n := l.N - - // must be call - switch n.Op { - default: - return false - - case OCALLFUNC, OCALLMETH, OCALLINTER: - break - } - - // call must return multiple values - return n.Left.Type.Outtuple > 1 -} - -// Copyret emits t1, t2, ... = n, where n is a function call, -// and then returns the list t1, t2, .... -func copyret(n *Node, order *Order) *NodeList { - if n.Type.Etype != TSTRUCT || n.Type.Funarg == 0 { - Fatal("copyret %v %d", n.Type, n.Left.Type.Outtuple) - } - - var l1 *NodeList - var l2 *NodeList - var tl Iter - var tmp *Node - for t := Structfirst(&tl, &n.Type); t != nil; t = structnext(&tl) { - tmp = temp(t.Type) - l1 = list(l1, tmp) - l2 = list(l2, tmp) - } - - as := Nod(OAS2, nil, nil) - as.List = l1 - as.Rlist = list1(n) - typecheck(&as, Etop) - orderstmt(as, order) - - return l2 -} - -// Ordercallargs orders the list of call arguments *l. -func ordercallargs(l **NodeList, order *Order) { - if ismulticall(*l) { - // return f() where f() is multiple values. - *l = copyret((*l).N, order) - } else { - orderexprlist(*l, 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, nil) - orderexpr(&n.Right, order, nil) // ODDDARG temp - ordercallargs(&n.List, order) -} - -// Ordermapassign appends n to order->out, introducing temporaries -// to make sure that all map assignments have the form m[k] = x, -// where x is adressable. -// (Orderexpr has already been called on n, so we know k is addressable.) -// -// If n is m[k] = x where x is not addressable, the rewrite is: -// tmp = x -// m[k] = tmp -// -// If n is the multiple assignment form ..., m[k], ... = ..., the rewrite is -// t1 = m -// t2 = k -// ...., t3, ... = x -// t1[t2] = t3 -// -// The temporaries t1, t2 are needed in case the ... being assigned -// contain m or k. They are usually unnecessary, but in the unnecessary -// cases they are also typically registerizable, so not much harm done. -// And this only applies to the multiple-assignment form. -// We could do a more precise analysis if needed, like in walk.c. -// -// Ordermapassign also inserts these temporaries if needed for -// calling writebarrierfat with a pointer to n->right. -func ordermapassign(n *Node, order *Order) { - switch n.Op { - default: - Fatal("ordermapassign %v", Oconv(int(n.Op), 0)) - - case OAS: - order.out = list(order.out, n) - - // We call writebarrierfat only for values > 4 pointers long. See walk.c. - if (n.Left.Op == OINDEXMAP || (needwritebarrier(n.Left, n.Right) && n.Left.Type.Width > int64(4*Widthptr))) && !isaddrokay(n.Right) { - m := n.Left - n.Left = ordertemp(m.Type, order, false) - a := Nod(OAS, m, n.Left) - typecheck(&a, Etop) - order.out = list(order.out, a) - } - - case OAS2, OAS2DOTTYPE, OAS2MAPR, OAS2FUNC: - var post *NodeList - var m *Node - var a *Node - for l := n.List; l != nil; l = l.Next { - if l.N.Op == OINDEXMAP { - m = l.N - if !istemp(m.Left) { - m.Left = ordercopyexpr(m.Left, m.Left.Type, order, 0) - } - if !istemp(m.Right) { - m.Right = ordercopyexpr(m.Right, m.Right.Type, order, 0) - } - l.N = ordertemp(m.Type, order, false) - a = Nod(OAS, m, l.N) - typecheck(&a, Etop) - post = list(post, a) - } - } - - order.out = list(order.out, n) - order.out = concat(order.out, post) - } -} - -// Orderstmt orders the statement n, appending to order->out. -// Temporaries created during the statement are cleaned -// up using VARKILL instructions as possible. -func orderstmt(n *Node, order *Order) { - if n == nil { - return - } - - lno := int(setlineno(n)) - - orderinit(n, order) - - switch n.Op { - default: - Fatal("orderstmt %v", Oconv(int(n.Op), 0)) - - case OVARKILL: - order.out = list(order.out, n) - - 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, - OPRINTN, - ORECOVER, - ORECV: - t := marktemp(order) - orderexpr(&n.Left, order, nil) - orderexpr(&n.Right, order, nil) - orderexprlist(n.List, order) - orderexprlist(n.Rlist, order) - switch n.Op { - case OAS2, OAS2DOTTYPE: - ordermapassign(n, order) - default: - order.out = list(order.out, n) - } - cleantemp(t, order) - - 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, nil) - n.Left = ordersafeexpr(n.Left, order) - tmp1 := treecopy(n.Left) - if tmp1.Op == OINDEXMAP { - tmp1.Etype = 0 // now an rvalue not an lvalue - } - 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, nil) - n.Etype = 0 - n.Op = OAS - ordermapassign(n, order) - cleantemp(t, order) - - // Special: make sure key is addressable, - // and make sure OINDEXMAP is not copied out. - case OAS2MAPR: - t := marktemp(order) - - orderexprlist(n.List, order) - r := n.Rlist.N - orderexpr(&r.Left, order, nil) - orderexpr(&r.Right, order, nil) - - // See case OINDEXMAP below. - if r.Right.Op == OARRAYBYTESTR { - r.Right.Op = OARRAYBYTESTRTMP - } - orderaddrtemp(&r.Right, order) - ordermapassign(n, order) - cleantemp(t, order) - - // Special: avoid copy of func call n->rlist->n. - case OAS2FUNC: - t := marktemp(order) - - orderexprlist(n.List, order) - ordercall(n.Rlist.N, order) - ordermapassign(n, order) - cleantemp(t, order) - - // Special: use temporary variables to hold result, - // so that assertI2Tetc can take address of temporary. - // No temporary for blank assignment. - case OAS2DOTTYPE: - t := marktemp(order) - - orderexprlist(n.List, order) - orderexpr(&n.Rlist.N.Left, order, nil) // i in i.(T) - if isblank(n.List.N) { - order.out = list(order.out, n) - } else { - typ := n.Rlist.N.Type - tmp1 := ordertemp(typ, order, haspointers(typ)) - order.out = list(order.out, n) - r := Nod(OAS, n.List.N, tmp1) - typecheck(&r, Etop) - ordermapassign(r, order) - n.List = list(list1(tmp1), n.List.Next.N) - } - - cleantemp(t, order) - - // Special: use temporary variables to hold result, - // so that chanrecv can take address of temporary. - case OAS2RECV: - t := marktemp(order) - - orderexprlist(n.List, order) - 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 - if !isblank(n.List.Next.N) { - tmp2 = ordertemp(n.List.Next.N.Type, order, false) - } else { - tmp2 = ordertemp(Types[TBOOL], order, false) - } - order.out = list(order.out, n) - r := Nod(OAS, n.List.N, tmp1) - typecheck(&r, Etop) - ordermapassign(r, order) - r = Nod(OAS, n.List.Next.N, tmp2) - typecheck(&r, Etop) - ordermapassign(r, order) - n.List = list(list1(tmp1), tmp2) - cleantemp(t, order) - - // Special: does not save n onto out. - case OBLOCK, OEMPTY: - orderstmtlist(n.List, order) - - // Special: n->left is not an expression; save as is. - case OBREAK, - OCONTINUE, - ODCL, - ODCLCONST, - ODCLTYPE, - OFALL, - OXFALL, - OGOTO, - OLABEL, - ORETJMP: - order.out = list(order.out, n) - - // Special: handle call arguments. - case OCALLFUNC, OCALLINTER, OCALLMETH: - t := marktemp(order) - - ordercall(n, order) - order.out = list(order.out, n) - cleantemp(t, order) - - // Special: order arguments to inner call but not call itself. - case ODEFER, OPROC: - t := marktemp(order) - - switch n.Left.Op { - // Delete will take the address of the key. - // Copy key into new temp and do not clean it - // (it persists beyond the statement). - case ODELETE: - orderexprlist(n.Left.List, order) - - t1 := marktemp(order) - np := &n.Left.List.Next.N // map key - *np = ordercopyexpr(*np, (*np).Type, order, 0) - poptemp(t1, order) - - default: - ordercall(n.Left, order) - } - - order.out = list(order.out, n) - cleantemp(t, order) - - case ODELETE: - t := marktemp(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) - - // Clean temporaries from condition evaluation at - // beginning of loop body and after for statement. - case OFOR: - t := marktemp(order) - - orderexprinplace(&n.Ntest, order) - var l *NodeList - cleantempnopop(t, order, &l) - n.Nbody = concat(l, n.Nbody) - orderblock(&n.Nbody) - orderstmtinplace(&n.Nincr) - order.out = list(order.out, n) - cleantemp(t, order) - - // Clean temporaries from condition at - // beginning of both branches. - case OIF: - t := marktemp(order) - - orderexprinplace(&n.Ntest, order) - var l *NodeList - cleantempnopop(t, order, &l) - n.Nbody = concat(l, n.Nbody) - l = nil - cleantempnopop(t, order, &l) - n.Nelse = concat(l, n.Nelse) - poptemp(t, order) - orderblock(&n.Nbody) - orderblock(&n.Nelse) - order.out = list(order.out, n) - - // Special: argument will be converted to interface using convT2E - // so make sure it is an addressable temporary. - case OPANIC: - t := marktemp(order) - - orderexpr(&n.Left, order, nil) - if !Isinter(n.Left.Type) { - orderaddrtemp(&n.Left, order) - } - order.out = list(order.out, n) - cleantemp(t, order) - - // n->right is the expression being ranged over. - // order it, and then make a copy if we need one. - // We almost always do, to ensure that we don't - // see any value changes made during the loop. - // Usually the copy is cheap (e.g., array pointer, chan, slice, string are all tiny). - // The exception is ranging over an array value (not a slice, not a pointer to array), - // which must make a copy to avoid seeing updates made during - // the range body. Ranging over an array value is uncommon though. - case ORANGE: - t := marktemp(order) - - orderexpr(&n.Right, order, nil) - switch n.Type.Etype { - default: - Fatal("orderstmt range %v", n.Type) - - // Mark []byte(str) range expression to reuse string backing storage. - // It is safe because the storage cannot be mutated. - case TARRAY: - if n.Right.Op == OSTRARRAYBYTE { - n.Right.Op = OSTRARRAYBYTETMP - } - if count(n.List) < 2 || isblank(n.List.Next.N) { - // for i := range x will only use x once, to compute len(x). - // No need to copy it. - break - } - fallthrough - - // chan, string, slice, array ranges use value multiple times. - // make copy. - // fall through - case TCHAN, TSTRING: - r := n.Right - - if r.Type.Etype == TSTRING && r.Type != Types[TSTRING] { - r = Nod(OCONV, r, nil) - r.Type = Types[TSTRING] - typecheck(&r, Erv) - } - - n.Right = ordercopyexpr(r, r.Type, order, 0) - - // copy the map value in case it is a map literal. - // TODO(rsc): Make tmp = literal expressions reuse tmp. - // For maps tmp is just one word so it hardly matters. - case TMAP: - r := n.Right - - n.Right = ordercopyexpr(r, r.Type, order, 0) - - // n->alloc is the temp for the iterator. - n.Alloc = ordertemp(Types[TUINT8], order, true) - } - - for l := n.List; l != nil; l = l.Next { - orderexprinplace(&l.N, order) - } - orderblock(&n.Nbody) - order.out = list(order.out, n) - cleantemp(t, order) - - case ORETURN: - ordercallargs(&n.List, order) - order.out = list(order.out, n) - - // Special: clean case temporaries in each block entry. - // Select must enter one of its blocks, so there is no - // need for a cleaning at the end. - // Doubly special: evaluation order for select is stricter - // than ordinary expressions. Even something like p.c - // has to be hoisted into a temporary, so that it cannot be - // reordered after the channel evaluation for a different - // case (if p were nil, then the timing of the fault would - // give this away). - case OSELECT: - t := marktemp(order) - - var tmp1 *Node - var tmp2 *Node - var r *Node - for l := n.List; l != nil; l = l.Next { - if l.N.Op != OXCASE { - Fatal("order select case %v", Oconv(int(l.N.Op), 0)) - } - r = l.N.Left - setlineno(l.N) - - // Append any new body prologue to ninit. - // The next loop will insert ninit into nbody. - if l.N.Ninit != nil { - Fatal("order select ninit") - } - if r != nil { - switch r.Op { - default: - Yyerror("unknown op in select %v", Oconv(int(r.Op), 0)) - Dump("select case", r) - - // If this is case x := <-ch or case x, y := <-ch, the case has - // the ODCL nodes to declare x and y. We want to delay that - // declaration (and possible allocation) until inside the case body. - // Delete the ODCL nodes here and recreate them inside the body below. - case OSELRECV, OSELRECV2: - if r.Colas { - t = r.Ninit - if t != nil && t.N.Op == ODCL && t.N.Left == r.Left { - t = t.Next - } - if t != nil && t.N.Op == ODCL && t.N.Left == r.Ntest { - t = t.Next - } - if t == nil { - r.Ninit = nil - } - } - - if r.Ninit != nil { - Yyerror("ninit on select recv") - dumplist("ninit", r.Ninit) - } - - // case x = <-c - // case x, ok = <-c - // 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, nil) - - if r.Right.Left.Op != ONAME { - r.Right.Left = ordercopyexpr(r.Right.Left, r.Right.Left.Type, order, 0) - } - - // Introduce temporary for receive and move actual copy into case body. - // avoids problems with target being addressed, as usual. - // NOTE: If we wanted to be clever, we could arrange for just one - // temporary per distinct type, sharing the temp among all receives - // with that temp. Similarly one ok bool could be shared among all - // the x,ok receives. Not worth doing until there's a clear need. - if r.Left != nil && isblank(r.Left) { - r.Left = nil - } - if r.Left != nil { - // use channel element type for temporary to avoid conversions, - // such as in case interfacevalue = <-intchan. - // the conversion happens in the OAS instead. - tmp1 = r.Left - - if r.Colas { - tmp2 = Nod(ODCL, tmp1, nil) - typecheck(&tmp2, Etop) - l.N.Ninit = list(l.N.Ninit, tmp2) - } - - r.Left = ordertemp(r.Right.Left.Type.Type, order, haspointers(r.Right.Left.Type.Type)) - tmp2 = Nod(OAS, tmp1, r.Left) - typecheck(&tmp2, Etop) - l.N.Ninit = list(l.N.Ninit, tmp2) - } - - if r.Ntest != nil && isblank(r.Ntest) { - r.Ntest = nil - } - if r.Ntest != nil { - tmp1 = r.Ntest - if r.Colas { - tmp2 = Nod(ODCL, tmp1, nil) - typecheck(&tmp2, Etop) - l.N.Ninit = list(l.N.Ninit, tmp2) - } - - r.Ntest = ordertemp(tmp1.Type, order, false) - tmp2 = Nod(OAS, tmp1, r.Ntest) - typecheck(&tmp2, Etop) - l.N.Ninit = list(l.N.Ninit, tmp2) - } - - orderblock(&l.N.Ninit) - - case OSEND: - if r.Ninit != nil { - Yyerror("ninit on select send") - dumplist("ninit", r.Ninit) - } - - // case c <- x - // r->left is c, r->right is x, both are always evaluated. - orderexpr(&r.Left, order, nil) - - if !istemp(r.Left) { - r.Left = ordercopyexpr(r.Left, r.Left.Type, order, 0) - } - orderexpr(&r.Right, order, nil) - if !istemp(r.Right) { - r.Right = ordercopyexpr(r.Right, r.Right.Type, order, 0) - } - } - } - - orderblock(&l.N.Nbody) - } - - // Now that we have accumulated all the temporaries, clean them. - // Also insert any ninit queued during the previous loop. - // (The temporary cleaning must follow that ninit work.) - for l := n.List; l != nil; l = l.Next { - cleantempnopop(t, order, &l.N.Ninit) - l.N.Nbody = concat(l.N.Ninit, l.N.Nbody) - l.N.Ninit = nil - } - - order.out = list(order.out, n) - poptemp(t, order) - - // Special: value being sent is passed as a pointer; make it addressable. - case OSEND: - t := marktemp(order) - - orderexpr(&n.Left, order, nil) - orderexpr(&n.Right, order, nil) - orderaddrtemp(&n.Right, order) - order.out = list(order.out, n) - cleantemp(t, order) - - // TODO(rsc): Clean temporaries more aggressively. - // Note that because walkswitch will rewrite some of the - // switch into a binary search, this is not as easy as it looks. - // (If we ran that code here we could invoke orderstmt on - // the if-else chain instead.) - // For now just clean all the temporaries at the end. - // In practice that's fine. - case OSWITCH: - t := marktemp(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)) - } - orderexprlistinplace(l.N.List, order) - orderblock(&l.N.Nbody) - } - - order.out = list(order.out, n) - cleantemp(t, order) - } - - lineno = int32(lno) -} - -// Orderexprlist orders the expression list l into order. -func orderexprlist(l *NodeList, order *Order) { - for ; l != nil; l = l.Next { - orderexpr(&l.N, order, nil) - } -} - -// Orderexprlist orders the expression list l but saves -// the side effects on the individual expression ninit lists. -func orderexprlistinplace(l *NodeList, order *Order) { - for ; l != nil; l = l.Next { - orderexprinplace(&l.N, order) - } -} - -// Orderexpr orders a single expression, appending side -// effects to order->out as needed. -// 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 - } - - lno := int(setlineno(n)) - orderinit(n, order) - - switch n.Op { - default: - orderexpr(&n.Left, order, nil) - orderexpr(&n.Right, order, nil) - orderexprlist(n.List, order) - orderexprlist(n.Rlist, order) - - // Addition of strings turns into a function call. - // Allocate a temporary to hold the strings. - // Fewer than 5 strings use direct runtime helpers. - case OADDSTR: - orderexprlist(n.List, order) - - if count(n.List) > 5 { - t := typ(TARRAY) - t.Bound = int64(count(n.List)) - t.Type = Types[TSTRING] - n.Alloc = ordertemp(t, order, false) - } - - // Mark string(byteSlice) arguments to reuse byteSlice backing - // buffer during conversion. String concatenation does not - // memorize the strings for later use, so it is safe. - // However, we can do it only if there is at least one non-empty string literal. - // Otherwise if all other arguments are empty strings, - // concatstrings will return the reference to the temp string - // to the caller. - hasbyte := false - - 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.(string)) != 0 - } - - if haslit && hasbyte { - for l := n.List; l != nil; l = l.Next { - if l.N.Op == OARRAYBYTESTR { - l.N.Op = OARRAYBYTESTRTMP - } - } - } - - case OCMPSTR: - 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 - // memorize the strings for later use, so it is safe. - if n.Left.Op == OARRAYBYTESTR { - n.Left.Op = OARRAYBYTESTRTMP - } - if n.Right.Op == OARRAYBYTESTR { - n.Right.Op = OARRAYBYTESTRTMP - } - - // key must be addressable - case OINDEXMAP: - orderexpr(&n.Left, order, nil) - - 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 - // the []byte backing array. This is a special case that it - // would be nice to handle more generally, but because - // there are no []byte-keyed maps, this specific case comes - // up in important cases in practice. See issue 3512. - // Nothing can change the []byte we are not copying before - // the map index, because the map access is going to - // be forced to happen immediately following this - // conversion (by the ordercopyexpr a few lines below). - if n.Etype == 0 && n.Right.Op == OARRAYBYTESTR { - n.Right.Op = OARRAYBYTESTRTMP - } - - orderaddrtemp(&n.Right, order) - if n.Etype == 0 { - // use of value (not being assigned); - // make copy in temporary. - n = ordercopyexpr(n, n.Type, order, 0) - } - - // concrete type (not interface) argument must be addressable - // temporary to pass to runtime. - case OCONVIFACE: - orderexpr(&n.Left, order, nil) - - if !Isinter(n.Left.Type) { - orderaddrtemp(&n.Left, order) - } - - case OANDAND, OOROR: - mark := marktemp(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 - // context in case the short circuit is taken. - var l *NodeList - - cleantempnopop(mark, order, &l) - n.Right.Ninit = concat(l, n.Right.Ninit) - orderexprinplace(&n.Right, order) - - case OCALLFUNC, - OCALLINTER, - OCALLMETH, - OCAP, - OCOMPLEX, - OCOPY, - OIMAG, - OLEN, - OMAKECHAN, - OMAKEMAP, - OMAKESLICE, - ONEW, - OREAL, - ORECOVER: - ordercall(n, order) - if lhs == nil || lhs.Op != ONAME || flag_race != 0 { - 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 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 - } - - case OARRAYLIT, OCALLPART: - orderexpr(&n.Left, order, nil) - orderexpr(&n.Right, order, nil) - orderexprlist(n.List, order) - orderexprlist(n.Rlist, order) - if n.Noescape { - n.Alloc = ordertemp(Types[TUINT8], order, false) // walk will fill in correct type - } - - case ODDDARG: - if n.Noescape { - // The ddd argument does not live beyond the call it is created for. - // Allocate a temporary that will be cleaned up when this statement - // completes. We could be more aggressive and try to arrange for it - // to be cleaned up when the call completes. - n.Alloc = ordertemp(n.Type.Type, order, false) - } - - case ODOTTYPE, ODOTTYPE2: - 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). - if !isdirectiface(n.Type) || Isfat(n.Type) || flag_race != 0 { - n = ordercopyexpr(n, n.Type, order, 1) - } - - case ORECV: - orderexpr(&n.Left, order, nil) - n = ordercopyexpr(n, n.Type, order, 1) - - case OEQ, ONE: - 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 - // addressable so we can pass them to the runtime. - orderaddrtemp(&n.Left, order) - orderaddrtemp(&n.Right, order) - } - } - - lineno = int32(lno) - - *np = n -} diff --git a/src/cmd/internal/gc/pgen.go b/src/cmd/internal/gc/pgen.go deleted file mode 100644 index 1b67cf2c3e..0000000000 --- a/src/cmd/internal/gc/pgen.go +++ /dev/null @@ -1,555 +0,0 @@ -// Copyright 2011 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 gc - -import ( - "cmd/internal/obj" - "crypto/md5" - "fmt" - "strings" -) - -// "Portable" code generation. - -var makefuncdatasym_nsym int32 - -func makefuncdatasym(namefmt string, funcdatakind int64) *Sym { - var nod Node - - sym := Lookupf(namefmt, makefuncdatasym_nsym) - makefuncdatasym_nsym++ - pnod := newname(sym) - pnod.Class = PEXTERN - Nodconst(&nod, Types[TINT32], funcdatakind) - Thearch.Gins(obj.AFUNCDATA, &nod, pnod) - return sym -} - -// gvardef inserts a VARDEF for n into the instruction stream. -// VARDEF is an annotation for the liveness analysis, marking a place -// where a complete initialization (definition) of a variable begins. -// Since the liveness analysis can see initialization of single-word -// variables quite easy, gvardef is usually only called for multi-word -// or 'fat' variables, those satisfying isfat(n->type). -// However, gvardef is also called when a non-fat variable is initialized -// via a block move; the only time this happens is when you have -// return f() -// for a function with multiple return values exactly matching the return -// types of the current function. -// -// A 'VARDEF x' annotation in the instruction stream tells the liveness -// analysis to behave as though the variable x is being initialized at that -// point in the instruction stream. The VARDEF must appear before the -// actual (multi-instruction) initialization, and it must also appear after -// any uses of the previous value, if any. For example, if compiling: -// -// x = x[1:] -// -// it is important to generate code like: -// -// base, len, cap = pieces of x[1:] -// VARDEF x -// x = {base, len, cap} -// -// If instead the generated code looked like: -// -// VARDEF x -// base, len, cap = pieces of x[1:] -// x = {base, len, cap} -// -// then the liveness analysis would decide the previous value of x was -// unnecessary even though it is about to be used by the x[1:] computation. -// Similarly, if the generated code looked like: -// -// base, len, cap = pieces of x[1:] -// x = {base, len, cap} -// VARDEF x -// -// then the liveness analysis will not preserve the new value of x, because -// the VARDEF appears to have "overwritten" it. -// -// VARDEF is a bit of a kludge to work around the fact that the instruction -// stream is working on single-word values but the liveness analysis -// wants to work on individual variables, which might be multi-word -// aggregates. It might make sense at some point to look into letting -// the liveness analysis work on single-word values as well, although -// there are complications around interface values, slices, and strings, -// all of which cannot be treated as individual words. -// -// VARKILL is the opposite of VARDEF: it marks a value as no longer needed, -// even if its address has been taken. That is, a VARKILL annotation asserts -// that its argument is certainly dead, for use when the liveness analysis -// would not otherwise be able to deduce that fact. - -func gvardefx(n *Node, as int) { - if n == nil { - Fatal("gvardef nil") - } - if n.Op != ONAME { - Yyerror("gvardef %v; %v", Oconv(int(n.Op), obj.FmtSharp), n) - return - } - - switch n.Class { - case PAUTO, PPARAM, PPARAMOUT: - Thearch.Gins(as, nil, n) - } -} - -func Gvardef(n *Node) { - gvardefx(n, obj.AVARDEF) -} - -func gvarkill(n *Node) { - gvardefx(n, obj.AVARKILL) -} - -func removevardef(firstp *obj.Prog) { - for p := firstp; p != nil; p = p.Link { - for p.Link != nil && (p.Link.As == obj.AVARDEF || p.Link.As == obj.AVARKILL) { - p.Link = p.Link.Link - } - if p.To.Type == obj.TYPE_BRANCH { - for p.To.Val.(*obj.Prog) != nil && (p.To.Val.(*obj.Prog).As == obj.AVARDEF || p.To.Val.(*obj.Prog).As == obj.AVARKILL) { - p.To.Val = p.To.Val.(*obj.Prog).Link - } - } - } -} - -func gcsymdup(s *Sym) { - ls := Linksym(s) - if len(ls.R) > 0 { - Fatal("cannot rosymdup %s with relocations", ls.Name) - } - ls.Name = fmt.Sprintf("gclocals·%x", md5.Sum(ls.P)) - ls.Dupok = 1 -} - -func emitptrargsmap() { - sym := Lookup(fmt.Sprintf("%s.args_stackmap", Curfn.Nname.Sym.Name)) - - nptr := int(Curfn.Type.Argwid / int64(Widthptr)) - bv := bvalloc(int32(nptr) * 2) - nbitmap := 1 - if Curfn.Type.Outtuple > 0 { - nbitmap = 2 - } - off := duint32(sym, 0, uint32(nbitmap)) - off = duint32(sym, off, uint32(bv.n)) - var xoffset int64 - if Curfn.Type.Thistuple > 0 { - xoffset = 0 - onebitwalktype1(getthisx(Curfn.Type), &xoffset, bv) - } - - if Curfn.Type.Intuple > 0 { - xoffset = 0 - onebitwalktype1(getinargx(Curfn.Type), &xoffset, bv) - } - - for j := 0; int32(j) < bv.n; j += 32 { - off = duint32(sym, off, bv.b[j/32]) - } - if Curfn.Type.Outtuple > 0 { - xoffset = 0 - onebitwalktype1(getoutargx(Curfn.Type), &xoffset, bv) - for j := 0; int32(j) < bv.n; j += 32 { - off = duint32(sym, off, bv.b[j/32]) - } - } - - ggloblsym(sym, int32(off), obj.RODATA|obj.LOCAL) -} - -// Sort the list of stack variables. Autos after anything else, -// within autos, unused after used, within used, things with -// pointers first, zeroed things first, and then decreasing size. -// Because autos are laid out in decreasing addresses -// on the stack, pointers first, zeroed things first and decreasing size -// really means, in memory, things with pointers needing zeroing at -// the top of the stack and increasing in size. -// Non-autos sort on offset. -func cmpstackvar(a *Node, b *Node) int { - if a.Class != b.Class { - if a.Class == PAUTO { - return +1 - } - return -1 - } - - if a.Class != PAUTO { - if a.Xoffset < b.Xoffset { - return -1 - } - if a.Xoffset > b.Xoffset { - return +1 - } - return 0 - } - - if a.Used != b.Used { - return obj.Bool2int(b.Used) - obj.Bool2int(a.Used) - } - - ap := obj.Bool2int(haspointers(a.Type)) - bp := obj.Bool2int(haspointers(b.Type)) - if ap != bp { - return bp - ap - } - - ap = obj.Bool2int(a.Name.Needzero) - bp = obj.Bool2int(b.Name.Needzero) - if ap != bp { - return bp - ap - } - - if a.Type.Width < b.Type.Width { - return +1 - } - if a.Type.Width > b.Type.Width { - return -1 - } - - return stringsCompare(a.Sym.Name, b.Sym.Name) -} - -// TODO(lvd) find out where the PAUTO/OLITERAL nodes come from. -func allocauto(ptxt *obj.Prog) { - Stksize = 0 - stkptrsize = 0 - - if Curfn.Func.Dcl == nil { - return - } - - // Mark the PAUTO's unused. - for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { - if ll.N.Class == PAUTO { - ll.N.Used = false - } - } - - markautoused(ptxt) - - listsort(&Curfn.Func.Dcl, cmpstackvar) - - // Unused autos are at the end, chop 'em off. - ll := Curfn.Func.Dcl - - n := ll.N - if n.Class == PAUTO && n.Op == ONAME && !n.Used { - // No locals used at all - Curfn.Func.Dcl = nil - - fixautoused(ptxt) - return - } - - for ll := Curfn.Func.Dcl; ll.Next != nil; ll = ll.Next { - n = ll.Next.N - if n.Class == PAUTO && n.Op == ONAME && !n.Used { - ll.Next = nil - Curfn.Func.Dcl.End = ll - break - } - } - - // Reassign stack offsets of the locals that are still there. - var w int64 - for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { - n = ll.N - if n.Class != PAUTO || n.Op != ONAME { - continue - } - - dowidth(n.Type) - w = n.Type.Width - if w >= Thearch.MAXWIDTH || w < 0 { - Fatal("bad width") - } - Stksize += w - Stksize = Rnd(Stksize, int64(n.Type.Align)) - if haspointers(n.Type) { - stkptrsize = Stksize - } - if Thearch.Thechar == '5' || Thearch.Thechar == '7' || Thearch.Thechar == '9' { - Stksize = Rnd(Stksize, int64(Widthptr)) - } - if Stksize >= 1<<31 { - setlineno(Curfn) - Yyerror("stack frame too large (>2GB)") - } - - n.Stkdelta = -Stksize - n.Xoffset - } - - Stksize = Rnd(Stksize, int64(Widthreg)) - stkptrsize = Rnd(stkptrsize, int64(Widthreg)) - - fixautoused(ptxt) - - // The debug information needs accurate offsets on the symbols. - for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { - if ll.N.Class != PAUTO || ll.N.Op != ONAME { - continue - } - ll.N.Xoffset += ll.N.Stkdelta - ll.N.Stkdelta = 0 - } -} - -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 - } - - // Ideally we wouldn't see any integer types here, but we do. - if n.Type == nil || (!Isptr[n.Type.Etype] && !Isint[n.Type.Etype] && n.Type.Etype != TUNSAFEPTR) { - Dump("checknil", n) - Fatal("bad checknil") - } - - if ((Thearch.Thechar == '5' || Thearch.Thechar == '7' || Thearch.Thechar == '9') && n.Op != OREGISTER) || !n.Addable || n.Op == OLITERAL { - var reg Node - Regalloc(®, Types[Tptr], n) - Cgen(n, ®) - Thearch.Gins(obj.ACHECKNIL, ®, nil) - Regfree(®) - return - } - - Thearch.Gins(obj.ACHECKNIL, n, nil) -} - -func compile(fn *Node) { - if Newproc == nil { - Newproc = Sysfunc("newproc") - Deferproc = Sysfunc("deferproc") - Deferreturn = Sysfunc("deferreturn") - Panicindex = Sysfunc("panicindex") - panicslice = Sysfunc("panicslice") - throwreturn = Sysfunc("throwreturn") - } - - lno := setlineno(fn) - - Curfn = fn - dowidth(Curfn.Type) - - var oldstksize int64 - var nod1 Node - var ptxt *obj.Prog - var pl *obj.Plist - var p *obj.Prog - var n *Node - var nam *Node - var gcargs *Sym - var gclocals *Sym - if fn.Nbody == nil { - if pure_go != 0 || strings.HasPrefix(fn.Nname.Sym.Name, "init.") { - Yyerror("missing function body for %q", fn.Nname.Sym.Name) - goto ret - } - - if Debug['A'] != 0 { - goto ret - } - emitptrargsmap() - goto ret - } - - saveerrors() - - // set up domain for labels - clearlabels() - - if Curfn.Type.Outnamed != 0 { - // add clearing of the output parameters - var save Iter - t := Structfirst(&save, Getoutarg(Curfn.Type)) - - for t != nil { - if t.Nname != nil { - n = Nod(OAS, t.Nname, nil) - typecheck(&n, Etop) - Curfn.Nbody = concat(list1(n), Curfn.Nbody) - } - - t = structnext(&save) - } - } - - order(Curfn) - if nerrors != 0 { - goto ret - } - - Hasdefer = 0 - walk(Curfn) - if nerrors != 0 { - goto ret - } - if flag_race != 0 { - racewalk(Curfn) - } - if nerrors != 0 { - goto ret - } - - continpc = nil - breakpc = nil - - pl = newplist() - pl.Name = Linksym(Curfn.Nname.Sym) - - setlineno(Curfn) - - Nodconst(&nod1, Types[TINT32], 0) - nam = Curfn.Nname - if isblank(nam) { - nam = nil - } - ptxt = Thearch.Gins(obj.ATEXT, nam, &nod1) - if fn.Func.Dupok { - ptxt.From3.Offset |= obj.DUPOK - } - if fn.Func.Wrapper { - ptxt.From3.Offset |= obj.WRAPPER - } - if fn.Func.Needctxt { - ptxt.From3.Offset |= obj.NEEDCTXT - } - if fn.Func.Nosplit { - ptxt.From3.Offset |= obj.NOSPLIT - } - - // Clumsy but important. - // See test/recover.go for test cases and src/reflect/value.go - // for the actual functions being considered. - if myimportpath != "" && myimportpath == "reflect" { - if Curfn.Nname.Sym.Name == "callReflect" || Curfn.Nname.Sym.Name == "callMethod" { - ptxt.From3.Offset |= obj.WRAPPER - } - } - - Afunclit(&ptxt.From, Curfn.Nname) - - ginit() - - gcargs = makefuncdatasym("gcargs·%d", obj.FUNCDATA_ArgsPointerMaps) - gclocals = makefuncdatasym("gclocals·%d", obj.FUNCDATA_LocalsPointerMaps) - - for t := Curfn.Paramfld; t != nil; t = t.Down { - gtrack(tracksym(t.Type)) - } - - for l := fn.Func.Dcl; l != nil; l = l.Next { - n = l.N - if n.Op != ONAME { // might be OTYPE or OLITERAL - continue - } - switch n.Class { - case PAUTO, PPARAM, PPARAMOUT: - Nodconst(&nod1, Types[TUINTPTR], l.N.Type.Width) - p = Thearch.Gins(obj.ATYPE, l.N, &nod1) - p.From.Gotype = Linksym(ngotype(l.N)) - } - } - - Genlist(Curfn.Func.Enter) - Genlist(Curfn.Nbody) - gclean() - checklabels() - if nerrors != 0 { - goto ret - } - if Curfn.Func.Endlineno != 0 { - lineno = Curfn.Func.Endlineno - } - - if Curfn.Type.Outtuple != 0 { - Ginscall(throwreturn, 0) - } - - ginit() - - // TODO: Determine when the final cgen_ret can be omitted. Perhaps always? - cgen_ret(nil) - - if Hasdefer != 0 { - // deferreturn pretends to have one uintptr argument. - // Reserve space for it so stack scanner is happy. - if Maxarg < int64(Widthptr) { - Maxarg = int64(Widthptr) - } - } - - gclean() - if nerrors != 0 { - goto ret - } - - Pc.As = obj.ARET // overwrite AEND - Pc.Lineno = lineno - - fixjmp(ptxt) - if Debug['N'] == 0 || Debug['R'] != 0 || Debug['P'] != 0 { - regopt(ptxt) - nilopt(ptxt) - } - - Thearch.Expandchecks(ptxt) - - oldstksize = Stksize - allocauto(ptxt) - - if false { - fmt.Printf("allocauto: %d to %d\n", oldstksize, int64(Stksize)) - } - - setlineno(Curfn) - if int64(Stksize)+Maxarg > 1<<31 { - Yyerror("stack frame too large (>2GB)") - goto ret - } - - // Emit garbage collection symbols. - liveness(Curfn, ptxt, gcargs, gclocals) - - gcsymdup(gcargs) - gcsymdup(gclocals) - - Thearch.Defframe(ptxt) - - if Debug['f'] != 0 { - frame(0) - } - - // Remove leftover instrumentation from the instruction stream. - removevardef(ptxt) - -ret: - lineno = lno -} diff --git a/src/cmd/internal/gc/plive.go b/src/cmd/internal/gc/plive.go deleted file mode 100644 index b4d0699d1f..0000000000 --- a/src/cmd/internal/gc/plive.go +++ /dev/null @@ -1,1830 +0,0 @@ -// Copyright 2013 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. - -// Garbage collector liveness bitmap generation. - -// The command line flag -live causes this code to print debug information. -// The levels are: -// -// -live (aka -live=1): print liveness lists as code warnings at safe points -// -live=2: print an assembly listing with liveness annotations -// -live=3: print information during each computation phase (much chattier) -// -// Each level includes the earlier output as well. - -package gc - -import ( - "cmd/internal/obj" - "fmt" - "sort" -) - -const ( - UNVISITED = 0 - VISITED = 1 -) - -// An ordinary basic block. -// -// Instructions are threaded together in a doubly-linked list. To iterate in -// program order follow the link pointer from the first node and stop after the -// last node has been visited -// -// for(p = bb->first;; p = p->link) { -// ... -// if(p == bb->last) -// break; -// } -// -// To iterate in reverse program order by following the opt pointer from the -// last node -// -// for(p = bb->last; p != nil; p = p->opt) { -// ... -// } -type BasicBlock struct { - pred []*BasicBlock // predecessors; if none, probably start of CFG - succ []*BasicBlock // successors; if none, probably ends in return statement - first *obj.Prog // first instruction in block - last *obj.Prog // last instruction in block - rpo int // reverse post-order number (also index in cfg) - mark int // mark bit for traversals - lastbitmapindex int // for livenessepilogue - - // Summary sets of block effects. - - // Computed during livenessprologue using only the content of - // individual blocks: - // - // uevar: upward exposed variables (used before set in block) - // varkill: killed variables (set in block) - // avarinit: addrtaken variables set or used (proof of initialization) - uevar Bvec - varkill Bvec - avarinit Bvec - - // Computed during livenesssolve using control flow information: - // - // livein: variables live at block entry - // liveout: variables live at block exit - // avarinitany: addrtaken variables possibly initialized at block exit - // (initialized in block or at exit from any predecessor block) - // avarinitall: addrtaken variables certainly initialized at block exit - // (initialized in block or at exit from all predecessor blocks) - livein Bvec - liveout Bvec - avarinitany Bvec - avarinitall Bvec -} - -// A collection of global state used by liveness analysis. -type Liveness struct { - fn *Node - ptxt *obj.Prog - vars []*Node - cfg []*BasicBlock - - // An array with a bit vector for each safe point tracking live pointers - // in the arguments and locals area, indexed by bb.rpo. - argslivepointers []Bvec - livepointers []Bvec -} - -func xmalloc(size uint32) interface{} { - result := (interface{})(make([]byte, size)) - if result == nil { - Fatal("malloc failed") - } - return result -} - -// Constructs a new basic block containing a single instruction. -func newblock(prog *obj.Prog) *BasicBlock { - if prog == nil { - Fatal("newblock: prog cannot be nil") - } - result := new(BasicBlock) - result.rpo = -1 - result.mark = UNVISITED - result.first = prog - result.last = prog - result.pred = make([]*BasicBlock, 0, 2) - result.succ = make([]*BasicBlock, 0, 2) - return result -} - -// Frees a basic block and all of its leaf data structures. -func freeblock(bb *BasicBlock) { - if bb == nil { - Fatal("freeblock: cannot free nil") - } -} - -// Adds an edge between two basic blocks by making from a predecessor of to and -// to a successor of from. -func addedge(from *BasicBlock, to *BasicBlock) { - if from == nil { - Fatal("addedge: from is nil") - } - if to == nil { - Fatal("addedge: to is nil") - } - from.succ = append(from.succ, to) - to.pred = append(to.pred, from) -} - -// Inserts prev before curr in the instruction -// stream. Any control flow, such as branches or fall throughs, that target the -// existing instruction are adjusted to target the new instruction. -func splicebefore(lv *Liveness, bb *BasicBlock, prev *obj.Prog, curr *obj.Prog) { - // There may be other instructions pointing at curr, - // and we want them to now point at prev. Instead of - // trying to find all such instructions, swap the contents - // so that the problem becomes inserting next after curr. - // The "opt" field is the backward link in the linked list. - - // Overwrite curr's data with prev, but keep the list links. - tmp := *curr - - *curr = *prev - curr.Opt = tmp.Opt - curr.Link = tmp.Link - - // Overwrite prev (now next) with curr's old data. - next := prev - - *next = tmp - next.Opt = nil - next.Link = nil - - // Now insert next after curr. - next.Link = curr.Link - - next.Opt = curr - curr.Link = next - if next.Link != nil && next.Link.Opt == curr { - next.Link.Opt = next - } - - if bb.last == curr { - bb.last = next - } -} - -// A pretty printer for basic blocks. -func printblock(bb *BasicBlock) { - fmt.Printf("basic block %d\n", bb.rpo) - fmt.Printf("\tpred:") - for _, pred := range bb.pred { - fmt.Printf(" %d", pred.rpo) - } - fmt.Printf("\n") - fmt.Printf("\tsucc:") - for _, succ := range bb.succ { - fmt.Printf(" %d", succ.rpo) - } - fmt.Printf("\n") - fmt.Printf("\tprog:\n") - for prog := bb.first; ; prog = prog.Link { - fmt.Printf("\t\t%v\n", prog) - if prog == bb.last { - break - } - } -} - -// Iterates over a basic block applying a callback to each instruction. There -// are two criteria for termination. If the end of basic block is reached a -// value of zero is returned. If the callback returns a non-zero value, the -// iteration is stopped and the value of the callback is returned. -func blockany(bb *BasicBlock, f func(*obj.Prog) bool) bool { - for p := bb.last; p != nil; p = p.Opt.(*obj.Prog) { - if f(p) { - return true - } - } - return false -} - -// Collects and returns and array of Node*s for functions arguments and local -// variables. -func getvariables(fn *Node) []*Node { - result := make([]*Node, 0, 0) - for ll := fn.Func.Dcl; ll != nil; ll = ll.Next { - if ll.N.Op == ONAME { - // In order for GODEBUG=gcdead=1 to work, each bitmap needs - // to contain information about all variables covered by the bitmap. - // For local variables, the bitmap only covers the stkptrsize - // bytes in the frame where variables containing pointers live. - // For arguments and results, the bitmap covers all variables, - // so we must include all the variables, even the ones without - // pointers. - // - // The Node.opt field is available for use by optimization passes. - // We use it to hold the index of the node in the variables array, plus 1 - // (so that 0 means the Node is not in the variables array). - // Each pass should clear opt when done, but you never know, - // so clear them all ourselves too. - // The Node.curfn field is supposed to be set to the current function - // already, but for some compiler-introduced names it seems not to be, - // so fix that here. - // Later, when we want to find the index of a node in the variables list, - // we will check that n->curfn == curfn and n->opt > 0. Then n->opt - 1 - // is the index in the variables list. - ll.N.Opt = nil - - // The compiler doesn't emit initializations for zero-width parameters or results. - if ll.N.Type.Width == 0 { - continue - } - - ll.N.Curfn = Curfn - switch ll.N.Class { - case PAUTO: - if haspointers(ll.N.Type) { - ll.N.Opt = int32(len(result)) - result = append(result, ll.N) - } - - case PPARAM, PPARAMOUT: - ll.N.Opt = int32(len(result)) - result = append(result, ll.N) - } - } - } - - return result -} - -// A pretty printer for control flow graphs. Takes an array of BasicBlock*s. -func printcfg(cfg []*BasicBlock) { - for _, bb := range cfg { - printblock(bb) - } -} - -// Assigns a reverse post order number to each connected basic block using the -// standard algorithm. Unconnected blocks will not be affected. -func reversepostorder(root *BasicBlock, rpo *int32) { - root.mark = VISITED - for _, bb := range root.succ { - if bb.mark == UNVISITED { - reversepostorder(bb, rpo) - } - } - *rpo -= 1 - root.rpo = int(*rpo) -} - -// Comparison predicate used for sorting basic blocks by their rpo in ascending -// order. -type blockrpocmp []*BasicBlock - -func (x blockrpocmp) Len() int { return len(x) } -func (x blockrpocmp) Swap(i, j int) { x[i], x[j] = x[j], x[i] } -func (x blockrpocmp) Less(i, j int) bool { return x[i].rpo < x[j].rpo } - -// A pattern matcher for call instructions. Returns true when the instruction -// is a call to a specific package qualified function name. -func iscall(prog *obj.Prog, name *obj.LSym) bool { - if prog == nil { - Fatal("iscall: prog is nil") - } - if name == nil { - Fatal("iscall: function name is nil") - } - if prog.As != obj.ACALL { - return false - } - return name == prog.To.Sym -} - -// Returns true for instructions that call a runtime function implementing a -// select communication clause. - -var selectNames [4]*obj.LSym - -func isselectcommcasecall(prog *obj.Prog) bool { - if selectNames[0] == nil { - selectNames[0] = Linksym(Pkglookup("selectsend", Runtimepkg)) - selectNames[1] = Linksym(Pkglookup("selectrecv", Runtimepkg)) - selectNames[2] = Linksym(Pkglookup("selectrecv2", Runtimepkg)) - selectNames[3] = Linksym(Pkglookup("selectdefault", Runtimepkg)) - } - - for _, name := range selectNames { - if iscall(prog, name) { - return true - } - } - return false -} - -// Returns true for call instructions that target runtime·newselect. - -var isnewselect_sym *obj.LSym - -func isnewselect(prog *obj.Prog) bool { - if isnewselect_sym == nil { - isnewselect_sym = Linksym(Pkglookup("newselect", Runtimepkg)) - } - return iscall(prog, isnewselect_sym) -} - -// Returns true for call instructions that target runtime·selectgo. - -var isselectgocall_sym *obj.LSym - -func isselectgocall(prog *obj.Prog) bool { - if isselectgocall_sym == nil { - isselectgocall_sym = Linksym(Pkglookup("selectgo", Runtimepkg)) - } - return iscall(prog, isselectgocall_sym) -} - -var isdeferreturn_sym *obj.LSym - -func isdeferreturn(prog *obj.Prog) bool { - if isdeferreturn_sym == nil { - isdeferreturn_sym = Linksym(Pkglookup("deferreturn", Runtimepkg)) - } - return iscall(prog, isdeferreturn_sym) -} - -// Walk backwards from a runtime·selectgo call up to its immediately dominating -// runtime·newselect call. Any successor nodes of communication clause nodes -// are implicit successors of the runtime·selectgo call node. The goal of this -// analysis is to add these missing edges to complete the control flow graph. -func addselectgosucc(selectgo *BasicBlock) { - var succ *BasicBlock - - pred := selectgo - for { - if len(pred.pred) == 0 { - Fatal("selectgo does not have a newselect") - } - pred = pred.pred[0] - if blockany(pred, isselectcommcasecall) { - // A select comm case block should have exactly one - // successor. - if len(pred.succ) != 1 { - Fatal("select comm case has too many successors") - } - succ = pred.succ[0] - - // Its successor should have exactly two successors. - // The drop through should flow to the selectgo block - // and the branch should lead to the select case - // statements block. - if len(succ.succ) != 2 { - Fatal("select comm case successor has too many successors") - } - - // Add the block as a successor of the selectgo block. - addedge(selectgo, succ) - } - - if blockany(pred, isnewselect) { - // Reached the matching newselect. - break - } - } -} - -// The entry point for the missing selectgo control flow algorithm. Takes an -// array of BasicBlock*s containing selectgo calls. -func fixselectgo(selectgo []*BasicBlock) { - for _, bb := range selectgo { - addselectgosucc(bb) - } -} - -// Constructs a control flow graph from a sequence of instructions. This -// procedure is complicated by various sources of implicit control flow that are -// not accounted for using the standard cfg construction algorithm. Returns an -// array of BasicBlock*s in control flow graph form (basic blocks ordered by -// their RPO number). -func newcfg(firstp *obj.Prog) []*BasicBlock { - // Reset the opt field of each prog to nil. In the first and second - // passes, instructions that are labels temporarily use the opt field to - // point to their basic block. In the third pass, the opt field reset - // to point to the predecessor of an instruction in its basic block. - for p := firstp; p != nil; p = p.Link { - p.Opt = nil - } - - // Allocate an array to remember where we have seen selectgo calls. - // These blocks will be revisited to add successor control flow edges. - selectgo := make([]*BasicBlock, 0, 0) - - // Loop through all instructions identifying branch targets - // and fall-throughs and allocate basic blocks. - cfg := make([]*BasicBlock, 0, 0) - - bb := newblock(firstp) - cfg = append(cfg, bb) - for p := firstp; p != nil; p = p.Link { - Thearch.Proginfo(p) - if p.To.Type == obj.TYPE_BRANCH { - if p.To.Val == nil { - Fatal("prog branch to nil") - } - if p.To.Val.(*obj.Prog).Opt == nil { - p.To.Val.(*obj.Prog).Opt = newblock(p.To.Val.(*obj.Prog)) - cfg = append(cfg, p.To.Val.(*obj.Prog).Opt.(*BasicBlock)) - } - - if p.As != obj.AJMP && p.Link != nil && p.Link.Opt == nil { - p.Link.Opt = newblock(p.Link) - cfg = append(cfg, p.Link.Opt.(*BasicBlock)) - } - } else if isselectcommcasecall(p) || isselectgocall(p) { - // Accommodate implicit selectgo control flow. - if p.Link.Opt == nil { - p.Link.Opt = newblock(p.Link) - cfg = append(cfg, p.Link.Opt.(*BasicBlock)) - } - } - } - - // Loop through all basic blocks maximally growing the list of - // contained instructions until a label is reached. Add edges - // for branches and fall-through instructions. - for _, bb := range cfg { - for p := bb.last; p != nil; p = p.Link { - if p.Opt != nil && p != bb.last { - break - } - bb.last = p - - // Stop before an unreachable RET, to avoid creating - // unreachable control flow nodes. - if p.Link != nil && p.Link.As == obj.ARET && p.Link.Mode == 1 { - break - } - - // Collect basic blocks with selectgo calls. - if isselectgocall(p) { - selectgo = append(selectgo, bb) - } - } - - if bb.last.To.Type == obj.TYPE_BRANCH { - addedge(bb, bb.last.To.Val.(*obj.Prog).Opt.(*BasicBlock)) - } - if bb.last.Link != nil { - // Add a fall-through when the instruction is - // not an unconditional control transfer. - if bb.last.As != obj.AJMP && bb.last.As != obj.ARET && bb.last.As != obj.AUNDEF { - addedge(bb, bb.last.Link.Opt.(*BasicBlock)) - } - } - } - - // Add back links so the instructions in a basic block can be traversed - // backward. This is the final state of the instruction opt field. - for _, bb := range cfg { - p := bb.first - var prev *obj.Prog - for { - p.Opt = prev - if p == bb.last { - break - } - prev = p - p = p.Link - } - } - - // Add missing successor edges to the selectgo blocks. - if len(selectgo) != 0 { - fixselectgo([]*BasicBlock(selectgo)) - } - - // Find a depth-first order and assign a depth-first number to - // all basic blocks. - for _, bb := range cfg { - bb.mark = UNVISITED - } - bb = cfg[0] - rpo := int32(len(cfg)) - reversepostorder(bb, &rpo) - - // Sort the basic blocks by their depth first number. The - // array is now a depth-first spanning tree with the first - // node being the root. - sort.Sort(blockrpocmp(cfg)) - - // Unreachable control flow nodes are indicated by a -1 in the rpo - // field. If we see these nodes something must have gone wrong in an - // upstream compilation phase. - bb = cfg[0] - if bb.rpo == -1 { - fmt.Printf("newcfg: unreachable basic block for %v\n", bb.last) - printcfg(cfg) - Fatal("newcfg: invalid control flow graph") - } - - return cfg -} - -// Frees a control flow graph (an array of BasicBlock*s) and all of its leaf -// data structures. -func freecfg(cfg []*BasicBlock) { - if len(cfg) > 0 { - bb0 := cfg[0] - for p := bb0.first; p != nil; p = p.Link { - p.Opt = nil - } - } -} - -// Returns true if the node names a variable that is otherwise uninteresting to -// the liveness computation. -func isfunny(n *Node) bool { - return n.Sym != nil && (n.Sym.Name == ".fp" || n.Sym.Name == ".args") -} - -// Computes the effects of an instruction on a set of -// variables. The vars argument is an array of Node*s. -// -// The output vectors give bits for variables: -// uevar - used by this instruction -// varkill - killed by this instruction -// for variables without address taken, means variable was set -// for variables with address taken, means variable was marked dead -// avarinit - initialized or referred to by this instruction, -// only for variables with address taken but not escaping to heap -// -// The avarinit output serves as a signal that the data has been -// initialized, because any use of a variable must come after its -// initialization. -func progeffects(prog *obj.Prog, vars []*Node, uevar Bvec, varkill Bvec, avarinit Bvec) { - bvresetall(uevar) - bvresetall(varkill) - bvresetall(avarinit) - - if prog.As == obj.ARET { - // Return instructions implicitly read all the arguments. For - // the sake of correctness, out arguments must be read. For the - // sake of backtrace quality, we read in arguments as well. - // - // A return instruction with a p->to is a tail return, which brings - // the stack pointer back up (if it ever went down) and then jumps - // to a new function entirely. That form of instruction must read - // all the parameters for correctness, and similarly it must not - // read the out arguments - they won't be set until the new - // function runs. - for i, node := range vars { - switch node.Class &^ PHEAP { - case PPARAM: - bvset(uevar, int32(i)) - - // If the result had its address taken, it is being tracked - // by the avarinit code, which does not use uevar. - // If we added it to uevar too, we'd not see any kill - // and decide that the varible was live entry, which it is not. - // So only use uevar in the non-addrtaken case. - // The p->to.type == thearch.D_NONE limits the bvset to - // non-tail-call return instructions; see note above - // the for loop for details. - case PPARAMOUT: - if !node.Addrtaken && prog.To.Type == obj.TYPE_NONE { - bvset(uevar, int32(i)) - } - } - } - - return - } - - if prog.As == obj.ATEXT { - // A text instruction marks the entry point to a function and - // the definition point of all in arguments. - for i, node := range vars { - switch node.Class &^ PHEAP { - case PPARAM: - if node.Addrtaken { - bvset(avarinit, int32(i)) - } - bvset(varkill, int32(i)) - } - } - - return - } - - if prog.Info.Flags&(LeftRead|LeftWrite|LeftAddr) != 0 { - from := &prog.From - if from.Node != nil && from.Sym != nil && ((from.Node).(*Node)).Curfn == Curfn { - switch ((from.Node).(*Node)).Class &^ PHEAP { - case PAUTO, PPARAM, PPARAMOUT: - pos, ok := from.Node.(*Node).Opt.(int32) // index in vars - if !ok { - goto Next - } - if pos >= int32(len(vars)) || vars[pos] != from.Node { - Fatal("bad bookkeeping in liveness %v %d", Nconv(from.Node.(*Node), 0), pos) - } - if ((from.Node).(*Node)).Addrtaken { - bvset(avarinit, pos) - } else { - if prog.Info.Flags&(LeftRead|LeftAddr) != 0 { - bvset(uevar, pos) - } - if prog.Info.Flags&LeftWrite != 0 { - if from.Node != nil && !Isfat(((from.Node).(*Node)).Type) { - bvset(varkill, pos) - } - } - } - } - } - } - -Next: - if prog.Info.Flags&(RightRead|RightWrite|RightAddr) != 0 { - to := &prog.To - if to.Node != nil && to.Sym != nil && ((to.Node).(*Node)).Curfn == Curfn { - switch ((to.Node).(*Node)).Class &^ PHEAP { - case PAUTO, PPARAM, PPARAMOUT: - pos, ok := to.Node.(*Node).Opt.(int32) // index in vars - if !ok { - return - } - if pos >= int32(len(vars)) || vars[pos] != to.Node { - Fatal("bad bookkeeping in liveness %v %d", Nconv(to.Node.(*Node), 0), pos) - } - if ((to.Node).(*Node)).Addrtaken { - if prog.As != obj.AVARKILL { - bvset(avarinit, pos) - } - if prog.As == obj.AVARDEF || prog.As == obj.AVARKILL { - bvset(varkill, pos) - } - } else { - // RightRead is a read, obviously. - // RightAddr by itself is also implicitly a read. - // - // RightAddr|RightWrite means that the address is being taken - // but only so that the instruction can write to the value. - // It is not a read. It is equivalent to RightWrite except that - // having the RightAddr bit set keeps the registerizer from - // trying to substitute a register for the memory location. - if (prog.Info.Flags&RightRead != 0) || prog.Info.Flags&(RightAddr|RightWrite) == RightAddr { - bvset(uevar, pos) - } - if prog.Info.Flags&RightWrite != 0 { - if to.Node != nil && (!Isfat(((to.Node).(*Node)).Type) || prog.As == obj.AVARDEF) { - bvset(varkill, pos) - } - } - } - } - } - } -} - -// Constructs a new liveness structure used to hold the global state of the -// liveness computation. The cfg argument is an array of BasicBlock*s and the -// vars argument is an array of Node*s. -func newliveness(fn *Node, ptxt *obj.Prog, cfg []*BasicBlock, vars []*Node) *Liveness { - result := new(Liveness) - result.fn = fn - result.ptxt = ptxt - result.cfg = cfg - result.vars = vars - - nblocks := int32(len(cfg)) - nvars := int32(len(vars)) - bulk := bvbulkalloc(nvars, nblocks*7) - for _, bb := range cfg { - bb.uevar = bulk.next() - bb.varkill = bulk.next() - bb.livein = bulk.next() - bb.liveout = bulk.next() - bb.avarinit = bulk.next() - bb.avarinitany = bulk.next() - bb.avarinitall = bulk.next() - } - - result.livepointers = make([]Bvec, 0, 0) - result.argslivepointers = make([]Bvec, 0, 0) - return result -} - -// Frees the liveness structure and all of its leaf data structures. -func freeliveness(lv *Liveness) { - if lv == nil { - Fatal("freeliveness: cannot free nil") - } -} - -func printeffects(p *obj.Prog, uevar Bvec, varkill Bvec, avarinit Bvec) { - fmt.Printf("effects of %v", p) - fmt.Printf("\nuevar: ") - bvprint(uevar) - fmt.Printf("\nvarkill: ") - bvprint(varkill) - fmt.Printf("\navarinit: ") - bvprint(avarinit) - fmt.Printf("\n") -} - -// Pretty print a variable node. Uses Pascal like conventions for pointers and -// addresses to avoid confusing the C like conventions used in the node variable -// names. -func printnode(node *Node) { - p := "" - if haspointers(node.Type) { - p = "^" - } - a := "" - if node.Addrtaken { - a = "@" - } - fmt.Printf(" %v%s%s", node, p, a) -} - -// Pretty print a list of variables. The vars argument is an array of Node*s. -func printvars(name string, bv Bvec, vars []*Node) { - fmt.Printf("%s:", name) - for i, node := range vars { - if bvget(bv, int32(i)) != 0 { - printnode(node) - } - } - fmt.Printf("\n") -} - -// Prints a basic block annotated with the information computed by liveness -// analysis. -func livenessprintblock(lv *Liveness, bb *BasicBlock) { - fmt.Printf("basic block %d\n", bb.rpo) - - fmt.Printf("\tpred:") - for _, pred := range bb.pred { - fmt.Printf(" %d", pred.rpo) - } - fmt.Printf("\n") - - fmt.Printf("\tsucc:") - for _, succ := range bb.succ { - fmt.Printf(" %d", succ.rpo) - } - fmt.Printf("\n") - - printvars("\tuevar", bb.uevar, []*Node(lv.vars)) - printvars("\tvarkill", bb.varkill, []*Node(lv.vars)) - printvars("\tlivein", bb.livein, []*Node(lv.vars)) - printvars("\tliveout", bb.liveout, []*Node(lv.vars)) - printvars("\tavarinit", bb.avarinit, []*Node(lv.vars)) - printvars("\tavarinitany", bb.avarinitany, []*Node(lv.vars)) - printvars("\tavarinitall", bb.avarinitall, []*Node(lv.vars)) - - fmt.Printf("\tprog:\n") - for prog := bb.first; ; prog = prog.Link { - fmt.Printf("\t\t%v", prog) - if prog.As == obj.APCDATA && prog.From.Offset == obj.PCDATA_StackMapIndex { - pos := int32(prog.To.Offset) - live := lv.livepointers[pos] - fmt.Printf(" ") - bvprint(live) - } - - fmt.Printf("\n") - if prog == bb.last { - break - } - } -} - -// Prints a control flow graph annotated with any information computed by -// liveness analysis. -func livenessprintcfg(lv *Liveness) { - for _, bb := range lv.cfg { - livenessprintblock(lv, bb) - } -} - -func checkauto(fn *Node, p *obj.Prog, n *Node) { - for l := fn.Func.Dcl; l != nil; l = l.Next { - if l.N.Op == ONAME && l.N.Class == PAUTO && l.N == n { - return - } - } - - if n == nil { - fmt.Printf("%v: checkauto %v: nil node in %v\n", p.Line(), Curfn, p) - return - } - - fmt.Printf("checkauto %v: %v (%p; class=%d) not found in %v\n", Curfn, n, n, n.Class, p) - for l := fn.Func.Dcl; l != nil; l = l.Next { - fmt.Printf("\t%v (%p; class=%d)\n", l.N, l.N, l.N.Class) - } - Yyerror("checkauto: invariant lost") -} - -func checkparam(fn *Node, p *obj.Prog, n *Node) { - if isfunny(n) { - return - } - var a *Node - var class uint8 - for l := fn.Func.Dcl; l != nil; l = l.Next { - a = l.N - class = a.Class &^ PHEAP - if a.Op == ONAME && (class == PPARAM || class == PPARAMOUT) && a == n { - return - } - } - - fmt.Printf("checkparam %v: %v (%p; class=%d) not found in %v\n", Curfn, n, n, n.Class, p) - for l := fn.Func.Dcl; l != nil; l = l.Next { - fmt.Printf("\t%v (%p; class=%d)\n", l.N, l.N, l.N.Class) - } - Yyerror("checkparam: invariant lost") -} - -func checkprog(fn *Node, p *obj.Prog) { - if p.From.Name == obj.NAME_AUTO { - checkauto(fn, p, p.From.Node.(*Node)) - } - if p.From.Name == obj.NAME_PARAM { - checkparam(fn, p, p.From.Node.(*Node)) - } - if p.To.Name == obj.NAME_AUTO { - checkauto(fn, p, p.To.Node.(*Node)) - } - if p.To.Name == obj.NAME_PARAM { - checkparam(fn, p, p.To.Node.(*Node)) - } -} - -// Check instruction invariants. We assume that the nodes corresponding to the -// sources and destinations of memory operations will be declared in the -// function. This is not strictly true, as is the case for the so-called funny -// nodes and there are special cases to skip over that stuff. The analysis will -// fail if this invariant blindly changes. -func checkptxt(fn *Node, firstp *obj.Prog) { - if debuglive == 0 { - return - } - - for p := firstp; p != nil; p = p.Link { - if false { - fmt.Printf("analyzing '%v'\n", p) - } - if p.As != obj.ADATA && p.As != obj.AGLOBL && p.As != obj.ATYPE { - checkprog(fn, p) - } - } -} - -// NOTE: The bitmap for a specific type t should be cached in t after the first run -// and then simply copied into bv at the correct offset on future calls with -// the same type t. On https://rsc.googlecode.com/hg/testdata/slow.go, onebitwalktype1 -// accounts for 40% of the 6g execution time. -func onebitwalktype1(t *Type, xoffset *int64, bv Bvec) { - if t.Align > 0 && *xoffset&int64(t.Align-1) != 0 { - Fatal("onebitwalktype1: invalid initial alignment, %v", t) - } - - switch t.Etype { - case TINT8, - TUINT8, - TINT16, - TUINT16, - TINT32, - TUINT32, - TINT64, - TUINT64, - TINT, - TUINT, - TUINTPTR, - TBOOL, - TFLOAT32, - TFLOAT64, - TCOMPLEX64, - TCOMPLEX128: - *xoffset += t.Width - - case TPTR32, - TPTR64, - TUNSAFEPTR, - TFUNC, - TCHAN, - TMAP: - if *xoffset&int64(Widthptr-1) != 0 { - Fatal("onebitwalktype1: invalid alignment, %v", t) - } - bvset(bv, int32(*xoffset/int64(Widthptr))) // pointer - *xoffset += t.Width - - case TSTRING: - // struct { byte *str; intgo len; } - if *xoffset&int64(Widthptr-1) != 0 { - Fatal("onebitwalktype1: invalid alignment, %v", t) - } - bvset(bv, int32(*xoffset/int64(Widthptr))) //pointer in first slot - *xoffset += t.Width - - case TINTER: - // struct { Itab *tab; void *data; } - // or, when isnilinter(t)==true: - // struct { Type *type; void *data; } - if *xoffset&int64(Widthptr-1) != 0 { - Fatal("onebitwalktype1: invalid alignment, %v", t) - } - bvset(bv, int32(*xoffset/int64(Widthptr))) // pointer in first slot - bvset(bv, int32(*xoffset/int64(Widthptr)+1)) // pointer in second slot - *xoffset += t.Width - - case TARRAY: - // 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) - } - if Isslice(t) { - // struct { byte *array; uintgo len; uintgo cap; } - if *xoffset&int64(Widthptr-1) != 0 { - Fatal("onebitwalktype1: invalid TARRAY alignment, %v", t) - } - bvset(bv, int32(*xoffset/int64(Widthptr))) // pointer in first slot (BitsPointer) - *xoffset += t.Width - } else { - for i := int64(0); i < t.Bound; i++ { - onebitwalktype1(t.Type, xoffset, bv) - } - } - - case TSTRUCT: - o := int64(0) - var fieldoffset int64 - for t1 := t.Type; t1 != nil; t1 = t1.Down { - fieldoffset = t1.Width - *xoffset += fieldoffset - o - onebitwalktype1(t1.Type, xoffset, bv) - o = fieldoffset + t1.Type.Width - } - - *xoffset += t.Width - o - - default: - Fatal("onebitwalktype1: unexpected type, %v", t) - } -} - -// Returns the number of words of local variables. -func localswords() int32 { - return int32(stkptrsize / int64(Widthptr)) -} - -// Returns the number of words of in and out arguments. -func argswords() int32 { - return int32(Curfn.Type.Argwid / int64(Widthptr)) -} - -// Generates live pointer value maps for arguments and local variables. The -// this argument and the in arguments are always assumed live. The vars -// argument is an array of Node*s. -func onebitlivepointermap(lv *Liveness, liveout Bvec, vars []*Node, args Bvec, locals Bvec) { - var node *Node - var xoffset int64 - - for i := int32(0); ; i++ { - i = int32(bvnext(liveout, i)) - if i < 0 { - break - } - node = vars[i] - switch node.Class { - case PAUTO: - xoffset = node.Xoffset + stkptrsize - onebitwalktype1(node.Type, &xoffset, locals) - - case PPARAM, PPARAMOUT: - xoffset = node.Xoffset - onebitwalktype1(node.Type, &xoffset, args) - } - } - - // The node list only contains declared names. - // If the receiver or arguments are unnamed, they will be omitted - // from the list above. Preserve those values - even though they are unused - - // in order to keep their addresses live for use in stack traces. - thisargtype := getthisx(lv.fn.Type) - - if thisargtype != nil { - xoffset = 0 - onebitwalktype1(thisargtype, &xoffset, args) - } - - inargtype := getinargx(lv.fn.Type) - if inargtype != nil { - xoffset = 0 - onebitwalktype1(inargtype, &xoffset, args) - } -} - -// Construct a disembodied instruction. -func unlinkedprog(as int) *obj.Prog { - p := Ctxt.NewProg() - Clearp(p) - p.As = int16(as) - return p -} - -// Construct a new PCDATA instruction associated with and for the purposes of -// covering an existing instruction. -func newpcdataprog(prog *obj.Prog, index int32) *obj.Prog { - var from Node - var to Node - - Nodconst(&from, Types[TINT32], obj.PCDATA_StackMapIndex) - Nodconst(&to, Types[TINT32], int64(index)) - pcdata := unlinkedprog(obj.APCDATA) - pcdata.Lineno = prog.Lineno - Naddr(&pcdata.From, &from) - Naddr(&pcdata.To, &to) - return pcdata -} - -// Returns true for instructions that are safe points that must be annotated -// with liveness information. -func issafepoint(prog *obj.Prog) bool { - return prog.As == obj.ATEXT || prog.As == obj.ACALL -} - -// Initializes the sets for solving the live variables. Visits all the -// instructions in each basic block to summarizes the information at each basic -// block -func livenessprologue(lv *Liveness) { - nvars := int32(len(lv.vars)) - uevar := bvalloc(nvars) - varkill := bvalloc(nvars) - avarinit := bvalloc(nvars) - for _, bb := range lv.cfg { - // Walk the block instructions backward and update the block - // effects with the each prog effects. - for p := bb.last; p != nil; p = p.Opt.(*obj.Prog) { - progeffects(p, []*Node(lv.vars), uevar, varkill, avarinit) - if debuglive >= 3 { - printeffects(p, uevar, varkill, avarinit) - } - bvor(bb.varkill, bb.varkill, varkill) - bvandnot(bb.uevar, bb.uevar, varkill) - bvor(bb.uevar, bb.uevar, uevar) - } - - // Walk the block instructions forward to update avarinit bits. - // avarinit describes the effect at the end of the block, not the beginning. - bvresetall(varkill) - - for p := bb.first; ; p = p.Link { - progeffects(p, []*Node(lv.vars), uevar, varkill, avarinit) - if debuglive >= 3 { - printeffects(p, uevar, varkill, avarinit) - } - bvandnot(bb.avarinit, bb.avarinit, varkill) - bvor(bb.avarinit, bb.avarinit, avarinit) - if p == bb.last { - break - } - } - } -} - -// Solve the liveness dataflow equations. -func livenesssolve(lv *Liveness) { - // These temporary bitvectors exist to avoid successive allocations and - // frees within the loop. - newlivein := bvalloc(int32(len(lv.vars))) - - newliveout := bvalloc(int32(len(lv.vars))) - any := bvalloc(int32(len(lv.vars))) - all := bvalloc(int32(len(lv.vars))) - - // Push avarinitall, avarinitany forward. - // avarinitall says the addressed var is initialized along all paths reaching the block exit. - // avarinitany says the addressed var is initialized along some path reaching the block exit. - for i, bb := range lv.cfg { - if i == 0 { - bvcopy(bb.avarinitall, bb.avarinit) - } else { - bvresetall(bb.avarinitall) - bvnot(bb.avarinitall) - } - bvcopy(bb.avarinitany, bb.avarinit) - } - - change := int32(1) - for change != 0 { - change = 0 - for _, bb := range lv.cfg { - bvresetall(any) - bvresetall(all) - for j, pred := range bb.pred { - if j == 0 { - bvcopy(any, pred.avarinitany) - bvcopy(all, pred.avarinitall) - } else { - bvor(any, any, pred.avarinitany) - bvand(all, all, pred.avarinitall) - } - } - - bvandnot(any, any, bb.varkill) - bvandnot(all, all, bb.varkill) - bvor(any, any, bb.avarinit) - bvor(all, all, bb.avarinit) - if bvcmp(any, bb.avarinitany) != 0 { - change = 1 - bvcopy(bb.avarinitany, any) - } - - if bvcmp(all, bb.avarinitall) != 0 { - change = 1 - bvcopy(bb.avarinitall, all) - } - } - } - - // Iterate through the blocks in reverse round-robin fashion. A work - // queue might be slightly faster. As is, the number of iterations is - // so low that it hardly seems to be worth the complexity. - change = 1 - - for change != 0 { - change = 0 - - // Walk blocks in the general direction of propagation. This - // improves convergence. - for i := len(lv.cfg) - 1; i >= 0; i-- { - bb := lv.cfg[i] - - // A variable is live on output from this block - // if it is live on input to some successor. - // - // out[b] = \bigcup_{s \in succ[b]} in[s] - bvresetall(newliveout) - for _, succ := range bb.succ { - bvor(newliveout, newliveout, succ.livein) - } - - if bvcmp(bb.liveout, newliveout) != 0 { - change = 1 - bvcopy(bb.liveout, newliveout) - } - - // A variable is live on input to this block - // if it is live on output from this block and - // not set by the code in this block. - // - // in[b] = uevar[b] \cup (out[b] \setminus varkill[b]) - bvandnot(newlivein, bb.liveout, bb.varkill) - - bvor(bb.livein, newlivein, bb.uevar) - } - } -} - -// This function is slow but it is only used for generating debug prints. -// Check whether n is marked live in args/locals. -func islive(n *Node, args Bvec, locals Bvec) bool { - switch n.Class { - case PPARAM, PPARAMOUT: - for i := 0; int64(i) < n.Type.Width/int64(Widthptr); i++ { - if bvget(args, int32(n.Xoffset/int64(Widthptr)+int64(i))) != 0 { - return true - } - } - - case PAUTO: - for i := 0; int64(i) < n.Type.Width/int64(Widthptr); i++ { - if bvget(locals, int32((n.Xoffset+stkptrsize)/int64(Widthptr)+int64(i))) != 0 { - return true - } - } - } - - return false -} - -// Visits all instructions in a basic block and computes a bit vector of live -// variables at each safe point locations. -func livenessepilogue(lv *Liveness) { - var pred *BasicBlock - var args Bvec - var locals Bvec - var n *Node - var p *obj.Prog - var j int32 - var pos int32 - var xoffset int64 - - nvars := int32(len(lv.vars)) - livein := bvalloc(nvars) - liveout := bvalloc(nvars) - uevar := bvalloc(nvars) - varkill := bvalloc(nvars) - avarinit := bvalloc(nvars) - any := bvalloc(nvars) - all := bvalloc(nvars) - ambig := bvalloc(localswords()) - nmsg := int32(0) - startmsg := int32(0) - - for _, bb := range lv.cfg { - // Compute avarinitany and avarinitall for entry to block. - // This duplicates information known during livenesssolve - // but avoids storing two more vectors for each block. - bvresetall(any) - - bvresetall(all) - for j = 0; j < int32(len(bb.pred)); j++ { - pred = bb.pred[j] - if j == 0 { - bvcopy(any, pred.avarinitany) - bvcopy(all, pred.avarinitall) - } else { - bvor(any, any, pred.avarinitany) - bvand(all, all, pred.avarinitall) - } - } - - // Walk forward through the basic block instructions and - // allocate liveness maps for those instructions that need them. - // Seed the maps with information about the addrtaken variables. - for p = bb.first; ; p = p.Link { - progeffects(p, []*Node(lv.vars), uevar, varkill, avarinit) - bvandnot(any, any, varkill) - bvandnot(all, all, varkill) - bvor(any, any, avarinit) - bvor(all, all, avarinit) - - if issafepoint(p) { - // Annotate ambiguously live variables so that they can - // be zeroed at function entry. - // livein and liveout are dead here and used as temporaries. - bvresetall(livein) - - bvandnot(liveout, any, all) - if !bvisempty(liveout) { - for pos = 0; pos < liveout.n; pos++ { - if bvget(liveout, pos) == 0 { - continue - } - bvset(all, pos) // silence future warnings in this block - n = lv.vars[pos] - 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)) - } - - // Record in 'ambiguous' bitmap. - xoffset = n.Xoffset + stkptrsize - - onebitwalktype1(n.Type, &xoffset, ambig) - } - } - } - - // Allocate a bit vector for each class and facet of - // value we are tracking. - - // Live stuff first. - args = bvalloc(argswords()) - - lv.argslivepointers = append(lv.argslivepointers, args) - locals = bvalloc(localswords()) - lv.livepointers = append(lv.livepointers, locals) - - if debuglive >= 3 { - fmt.Printf("%v\n", p) - printvars("avarinitany", any, lv.vars) - } - - // Record any values with an "address taken" reaching - // this code position as live. Must do now instead of below - // because the any/all calculation requires walking forward - // over the block (as this loop does), while the liveout - // requires walking backward (as the next loop does). - onebitlivepointermap(lv, any, lv.vars, args, locals) - } - - if p == bb.last { - break - } - } - - bb.lastbitmapindex = len(lv.livepointers) - 1 - } - - var fmt_ string - var next *obj.Prog - var numlive int32 - var msg []string - for _, bb := range lv.cfg { - if debuglive >= 1 && Curfn.Nname.Sym.Name != "init" && Curfn.Nname.Sym.Name[0] != '.' { - nmsg = int32(len(lv.livepointers)) - startmsg = nmsg - msg = make([]string, nmsg) - for j = 0; j < nmsg; j++ { - msg[j] = "" - } - } - - // walk backward, emit pcdata and populate the maps - pos = int32(bb.lastbitmapindex) - - if pos < 0 { - // the first block we encounter should have the ATEXT so - // at no point should pos ever be less than zero. - Fatal("livenessepilogue") - } - - bvcopy(livein, bb.liveout) - for p = bb.last; p != nil; p = next { - next = p.Opt.(*obj.Prog) // splicebefore modifies p->opt - - // Propagate liveness information - progeffects(p, lv.vars, uevar, varkill, avarinit) - - bvcopy(liveout, livein) - bvandnot(livein, liveout, varkill) - bvor(livein, livein, uevar) - if debuglive >= 3 && issafepoint(p) { - fmt.Printf("%v\n", p) - printvars("uevar", uevar, lv.vars) - printvars("varkill", varkill, lv.vars) - printvars("livein", livein, lv.vars) - printvars("liveout", liveout, lv.vars) - } - - if issafepoint(p) { - // Found an interesting instruction, record the - // corresponding liveness information. - - // Useful sanity check: on entry to the function, - // the only things that can possibly be live are the - // input parameters. - if p.As == obj.ATEXT { - for j = 0; j < liveout.n; j++ { - if bvget(liveout, j) == 0 { - continue - } - n = lv.vars[j] - if n.Class != PPARAM { - yyerrorl(int(p.Lineno), "internal error: %v %v recorded as live on entry", Curfn.Nname, Nconv(n, obj.FmtLong)) - } - } - } - - // Record live pointers. - args = lv.argslivepointers[pos] - - locals = lv.livepointers[pos] - onebitlivepointermap(lv, liveout, lv.vars, args, locals) - - // Ambiguously live variables are zeroed immediately after - // function entry. Mark them live for all the non-entry bitmaps - // so that GODEBUG=gcdead=1 mode does not poison them. - if p.As == obj.ACALL { - bvor(locals, locals, ambig) - } - - // Show live pointer bitmaps. - // We're interpreting the args and locals bitmap instead of liveout so that we - // include the bits added by the avarinit logic in the - // previous loop. - if msg != nil { - fmt_ = "" - fmt_ += fmt.Sprintf("%v: live at ", p.Line()) - if p.As == obj.ACALL && p.To.Node != nil { - fmt_ += fmt.Sprintf("call to %s:", ((p.To.Node).(*Node)).Sym.Name) - } else if p.As == obj.ACALL { - fmt_ += "indirect call:" - } else { - fmt_ += fmt.Sprintf("entry to %s:", ((p.From.Node).(*Node)).Sym.Name) - } - numlive = 0 - for j = 0; j < int32(len(lv.vars)); j++ { - n = lv.vars[j] - if islive(n, args, locals) { - fmt_ += fmt.Sprintf(" %v", n) - numlive++ - } - } - - fmt_ += "\n" - if numlive == 0 { // squelch message - - } else { - startmsg-- - msg[startmsg] = fmt_ - } - } - - // Only CALL instructions need a PCDATA annotation. - // The TEXT instruction annotation is implicit. - if p.As == obj.ACALL { - if isdeferreturn(p) { - // runtime.deferreturn modifies its return address to return - // back to the CALL, not to the subsequent instruction. - // Because the return comes back one instruction early, - // the PCDATA must begin one instruction early too. - // The instruction before a call to deferreturn is always a - // no-op, to keep PC-specific data unambiguous. - splicebefore(lv, bb, newpcdataprog(p.Opt.(*obj.Prog), pos), p.Opt.(*obj.Prog)) - } else { - splicebefore(lv, bb, newpcdataprog(p, pos), p) - } - } - - pos-- - } - } - - if msg != nil { - for j = startmsg; j < nmsg; j++ { - if msg[j] != "" { - fmt.Printf("%s", msg[j]) - } - } - - msg = nil - nmsg = 0 - startmsg = 0 - } - } - - Flusherrors() -} - -// FNV-1 hash function constants. -const ( - H0 = 2166136261 - Hp = 16777619 -) - -func hashbitmap(h uint32, bv Bvec) uint32 { - var w uint32 - - n := int((bv.n + 31) / 32) - for i := 0; i < n; i++ { - w = bv.b[i] - h = (h * Hp) ^ (w & 0xff) - h = (h * Hp) ^ ((w >> 8) & 0xff) - h = (h * Hp) ^ ((w >> 16) & 0xff) - h = (h * Hp) ^ ((w >> 24) & 0xff) - } - - return h -} - -// Compact liveness information by coalescing identical per-call-site bitmaps. -// The merging only happens for a single function, not across the entire binary. -// -// There are actually two lists of bitmaps, one list for the local variables and one -// list for the function arguments. Both lists are indexed by the same PCDATA -// index, so the corresponding pairs must be considered together when -// merging duplicates. The argument bitmaps change much less often during -// function execution than the local variable bitmaps, so it is possible that -// we could introduce a separate PCDATA index for arguments vs locals and -// then compact the set of argument bitmaps separately from the set of -// local variable bitmaps. As of 2014-04-02, doing this to the godoc binary -// is actually a net loss: we save about 50k of argument bitmaps but the new -// PCDATA tables cost about 100k. So for now we keep using a single index for -// both bitmap lists. -func livenesscompact(lv *Liveness) { - // Linear probing hash table of bitmaps seen so far. - // The hash table has 4n entries to keep the linear - // scan short. An entry of -1 indicates an empty slot. - n := len(lv.livepointers) - - tablesize := 4 * n - table := make([]int, tablesize) - for i := range table { - table[i] = -1 - } - - // remap[i] = the new index of the old bit vector #i. - remap := make([]int, n) - - for i := range remap { - remap[i] = -1 - } - uniq := 0 // unique tables found so far - - // Consider bit vectors in turn. - // If new, assign next number using uniq, - // record in remap, record in lv->livepointers and lv->argslivepointers - // under the new index, and add entry to hash table. - // If already seen, record earlier index in remap and free bitmaps. - var jarg Bvec - var j int - var h uint32 - var arg Bvec - var jlocal Bvec - var local Bvec - for i := 0; i < n; i++ { - local = lv.livepointers[i] - arg = lv.argslivepointers[i] - h = hashbitmap(hashbitmap(H0, local), arg) % uint32(tablesize) - - for { - j = table[h] - if j < 0 { - break - } - jlocal = lv.livepointers[j] - jarg = lv.argslivepointers[j] - if bvcmp(local, jlocal) == 0 && bvcmp(arg, jarg) == 0 { - remap[i] = j - goto Next - } - - h++ - if h == uint32(tablesize) { - h = 0 - } - } - - table[h] = uniq - remap[i] = uniq - lv.livepointers[uniq] = local - lv.argslivepointers[uniq] = arg - uniq++ - Next: - } - - // We've already reordered lv->livepointers[0:uniq] - // and lv->argslivepointers[0:uniq] and freed the bitmaps - // we don't need anymore. Clear the pointers later in the - // array so that we can tell where the coalesced bitmaps stop - // and so that we don't double-free when cleaning up. - for j := uniq; j < n; j++ { - lv.livepointers[j] = Bvec{} - lv.argslivepointers[j] = Bvec{} - } - - // Rewrite PCDATA instructions to use new numbering. - var i int - for p := lv.ptxt; p != nil; p = p.Link { - if p.As == obj.APCDATA && p.From.Offset == obj.PCDATA_StackMapIndex { - i = int(p.To.Offset) - if i >= 0 { - p.To.Offset = int64(remap[i]) - } - } - } -} - -func printbitset(printed int, name string, vars []*Node, bits Bvec) int { - started := 0 - for i, n := range vars { - if bvget(bits, int32(i)) == 0 { - continue - } - if started == 0 { - if printed == 0 { - fmt.Printf("\t") - } else { - fmt.Printf(" ") - } - started = 1 - printed = 1 - fmt.Printf("%s=", name) - } else { - fmt.Printf(",") - } - - fmt.Printf("%s", n.Sym.Name) - } - - return printed -} - -// Prints the computed liveness information and inputs, for debugging. -// This format synthesizes the information used during the multiple passes -// into a single presentation. -func livenessprintdebug(lv *Liveness) { - var j int - var printed int - var p *obj.Prog - var args Bvec - var locals Bvec - var n *Node - - fmt.Printf("liveness: %s\n", Curfn.Nname.Sym.Name) - - uevar := bvalloc(int32(len(lv.vars))) - varkill := bvalloc(int32(len(lv.vars))) - avarinit := bvalloc(int32(len(lv.vars))) - - pcdata := 0 - for i, bb := range lv.cfg { - if i > 0 { - fmt.Printf("\n") - } - - // bb#0 pred=1,2 succ=3,4 - fmt.Printf("bb#%d pred=", i) - - for j = 0; j < len(bb.pred); j++ { - if j > 0 { - fmt.Printf(",") - } - fmt.Printf("%d", (bb.pred[j]).rpo) - } - - fmt.Printf(" succ=") - for j = 0; j < len(bb.succ); j++ { - if j > 0 { - fmt.Printf(",") - } - fmt.Printf("%d", (bb.succ[j]).rpo) - } - - fmt.Printf("\n") - - // initial settings - printed = 0 - - printed = printbitset(printed, "uevar", lv.vars, bb.uevar) - printed = printbitset(printed, "livein", lv.vars, bb.livein) - if printed != 0 { - fmt.Printf("\n") - } - - // program listing, with individual effects listed - for p = bb.first; ; p = p.Link { - fmt.Printf("%v\n", p) - if p.As == obj.APCDATA && p.From.Offset == obj.PCDATA_StackMapIndex { - pcdata = int(p.To.Offset) - } - progeffects(p, lv.vars, uevar, varkill, avarinit) - printed = 0 - printed = printbitset(printed, "uevar", lv.vars, uevar) - printed = printbitset(printed, "varkill", lv.vars, varkill) - printed = printbitset(printed, "avarinit", lv.vars, avarinit) - if printed != 0 { - fmt.Printf("\n") - } - if issafepoint(p) { - args = lv.argslivepointers[pcdata] - locals = lv.livepointers[pcdata] - fmt.Printf("\tlive=") - printed = 0 - for j = 0; j < len(lv.vars); j++ { - n = lv.vars[j] - if islive(n, args, locals) { - tmp9 := printed - printed++ - if tmp9 != 0 { - fmt.Printf(",") - } - fmt.Printf("%v", n) - } - } - - fmt.Printf("\n") - } - - if p == bb.last { - break - } - } - - // bb bitsets - fmt.Printf("end\n") - - printed = printbitset(printed, "varkill", lv.vars, bb.varkill) - printed = printbitset(printed, "liveout", lv.vars, bb.liveout) - printed = printbitset(printed, "avarinit", lv.vars, bb.avarinit) - printed = printbitset(printed, "avarinitany", lv.vars, bb.avarinitany) - printed = printbitset(printed, "avarinitall", lv.vars, bb.avarinitall) - if printed != 0 { - fmt.Printf("\n") - } - } - - fmt.Printf("\n") -} - -// Dumps an array of bitmaps to a symbol as a sequence of uint32 values. The -// first word dumped is the total number of bitmaps. The second word is the -// length of the bitmaps. All bitmaps are assumed to be of equal length. The -// words that are followed are the raw bitmap words. The arr argument is an -// array of Node*s. -func onebitwritesymbol(arr []Bvec, sym *Sym) { - var i int - var j int - var word uint32 - - n := len(arr) - off := 0 - off += 4 // number of bitmaps, to fill in later - bv := arr[0] - off = duint32(sym, off, uint32(bv.n)) // number of bits in each bitmap - for i = 0; i < n; i++ { - // bitmap words - bv = arr[i] - - if bv.b == nil { - break - } - for j = 0; int32(j) < bv.n; j += 32 { - word = bv.b[j/32] - - // Runtime reads the bitmaps as byte arrays. Oblige. - off = duint8(sym, off, uint8(word)) - - off = duint8(sym, off, uint8(word>>8)) - off = duint8(sym, off, uint8(word>>16)) - off = duint8(sym, off, uint8(word>>24)) - } - } - - duint32(sym, 0, uint32(i)) // number of bitmaps - ggloblsym(sym, int32(off), obj.RODATA) -} - -func printprog(p *obj.Prog) { - for p != nil { - fmt.Printf("%v\n", p) - p = p.Link - } -} - -// Entry pointer for liveness analysis. Constructs a complete CFG, solves for -// the liveness of pointer variables in the function, and emits a runtime data -// structure read by the garbage collector. -func liveness(fn *Node, firstp *obj.Prog, argssym *Sym, livesym *Sym) { - // Change name to dump debugging information only for a specific function. - debugdelta := 0 - - if Curfn.Nname.Sym.Name == "!" { - debugdelta = 2 - } - - debuglive += debugdelta - if debuglive >= 3 { - fmt.Printf("liveness: %s\n", Curfn.Nname.Sym.Name) - printprog(firstp) - } - - checkptxt(fn, firstp) - - // Construct the global liveness state. - cfg := newcfg(firstp) - - if debuglive >= 3 { - printcfg([]*BasicBlock(cfg)) - } - vars := getvariables(fn) - lv := newliveness(fn, firstp, cfg, vars) - - // Run the dataflow framework. - livenessprologue(lv) - - if debuglive >= 3 { - livenessprintcfg(lv) - } - livenesssolve(lv) - if debuglive >= 3 { - livenessprintcfg(lv) - } - livenessepilogue(lv) - if debuglive >= 3 { - livenessprintcfg(lv) - } - livenesscompact(lv) - - if debuglive >= 2 { - livenessprintdebug(lv) - } - - // Emit the live pointer map data structures - onebitwritesymbol(lv.livepointers, livesym) - - onebitwritesymbol(lv.argslivepointers, argssym) - - // Free everything. - for l := fn.Func.Dcl; l != nil; l = l.Next { - if l.N != nil { - l.N.Opt = nil - } - } - freeliveness(lv) - - freecfg([]*BasicBlock(cfg)) - - debuglive -= debugdelta -} diff --git a/src/cmd/internal/gc/popt.go b/src/cmd/internal/gc/popt.go deleted file mode 100644 index ce904e155b..0000000000 --- a/src/cmd/internal/gc/popt.go +++ /dev/null @@ -1,1086 +0,0 @@ -// Derived from Inferno utils/6c/gc.h -// http://code.google.com/p/inferno-os/source/browse/utils/6c/gc.h -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -// "Portable" optimizations. - -package gc - -import ( - "cmd/internal/obj" - "fmt" - "sort" - "strings" -) - -type OptStats struct { - Ncvtreg int32 - Nspill int32 - Nreload int32 - Ndelmov int32 - Nvar int32 - Naddr int32 -} - -var Ostats OptStats - -var noreturn_symlist [10]*Sym - -// p is a call instruction. Does the call fail to return? -func Noreturn(p *obj.Prog) bool { - if noreturn_symlist[0] == nil { - noreturn_symlist[0] = Pkglookup("panicindex", Runtimepkg) - noreturn_symlist[1] = Pkglookup("panicslice", Runtimepkg) - noreturn_symlist[2] = Pkglookup("throwinit", Runtimepkg) - noreturn_symlist[3] = Pkglookup("gopanic", Runtimepkg) - noreturn_symlist[4] = Pkglookup("panicwrap", Runtimepkg) - noreturn_symlist[5] = Pkglookup("throwreturn", Runtimepkg) - noreturn_symlist[6] = Pkglookup("selectgo", Runtimepkg) - noreturn_symlist[7] = Pkglookup("block", Runtimepkg) - } - - if p.To.Node == nil { - return false - } - s := ((p.To.Node).(*Node)).Sym - if s == nil { - return false - } - for i := 0; noreturn_symlist[i] != nil; i++ { - if s == noreturn_symlist[i] { - return true - } - } - return false -} - -// JMP chasing and removal. -// -// The code generator depends on being able to write out jump -// instructions that it can jump to now but fill in later. -// the linker will resolve them nicely, but they make the code -// longer and more difficult to follow during debugging. -// Remove them. - -/* what instruction does a JMP to p eventually land on? */ -func chasejmp(p *obj.Prog, jmploop *int) *obj.Prog { - n := 0 - for p != nil && p.As == obj.AJMP && p.To.Type == obj.TYPE_BRANCH { - n++ - if n > 10 { - *jmploop = 1 - break - } - - p = p.To.Val.(*obj.Prog) - } - - return p -} - -/* - * reuse reg pointer for mark/sweep state. - * leave reg==nil at end because alive==nil. - */ -var alive interface{} = nil -var dead interface{} = 1 - -/* mark all code reachable from firstp as alive */ -func mark(firstp *obj.Prog) { - for p := firstp; p != nil; p = p.Link { - if p.Opt != dead { - break - } - p.Opt = alive - if p.As != obj.ACALL && p.To.Type == obj.TYPE_BRANCH && p.To.Val.(*obj.Prog) != nil { - mark(p.To.Val.(*obj.Prog)) - } - if p.As == obj.AJMP || p.As == obj.ARET || p.As == obj.AUNDEF { - break - } - } -} - -func fixjmp(firstp *obj.Prog) { - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf("\nfixjmp\n") - } - - // pass 1: resolve jump to jump, mark all code as dead. - jmploop := 0 - - for p := firstp; p != nil; p = p.Link { - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf("%v\n", p) - } - if p.As != obj.ACALL && p.To.Type == obj.TYPE_BRANCH && p.To.Val.(*obj.Prog) != nil && p.To.Val.(*obj.Prog).As == obj.AJMP { - p.To.Val = chasejmp(p.To.Val.(*obj.Prog), &jmploop) - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf("->%v\n", p) - } - } - - p.Opt = dead - } - - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf("\n") - } - - // pass 2: mark all reachable code alive - mark(firstp) - - // pass 3: delete dead code (mostly JMPs). - var last *obj.Prog - - for p := firstp; p != nil; p = p.Link { - if p.Opt == dead { - if p.Link == nil && p.As == obj.ARET && last != nil && last.As != obj.ARET { - // This is the final ARET, and the code so far doesn't have one. - // Let it stay. The register allocator assumes that all live code in - // the function can be traversed by starting at all the RET instructions - // and following predecessor links. If we remove the final RET, - // this assumption will not hold in the case of an infinite loop - // at the end of a function. - // Keep the RET but mark it dead for the liveness analysis. - p.Mode = 1 - } else { - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf("del %v\n", p) - } - continue - } - } - - if last != nil { - last.Link = p - } - last = p - } - - last.Link = nil - - // pass 4: elide JMP to next instruction. - // only safe if there are no jumps to JMPs anymore. - if jmploop == 0 { - var last *obj.Prog - for p := firstp; p != nil; p = p.Link { - if p.As == obj.AJMP && p.To.Type == obj.TYPE_BRANCH && p.To.Val == p.Link { - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf("del %v\n", p) - } - continue - } - - if last != nil { - last.Link = p - } - last = p - } - - last.Link = nil - } - - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf("\n") - for p := firstp; p != nil; p = p.Link { - fmt.Printf("%v\n", p) - } - fmt.Printf("\n") - } -} - -// Control flow analysis. The Flow structures hold predecessor and successor -// information as well as basic loop analysis. -// -// graph = flowstart(firstp, 0); -// ... use flow graph ... -// flowend(graph); // free graph -// -// Typical uses of the flow graph are to iterate over all the flow-relevant instructions: -// -// for(f = graph->start; f != nil; f = f->link) -// -// or, given an instruction f, to iterate over all the predecessors, which is -// f->p1 and this list: -// -// for(f2 = f->p2; f2 != nil; f2 = f2->p2link) -// -// The size argument to flowstart specifies an amount of zeroed memory -// to allocate in every f->data field, for use by the client. -// If size == 0, f->data will be nil. - -var flowmark int - -// MaxFlowProg is the maximum size program (counted in instructions) -// for which the flow code will build a graph. Functions larger than this limit -// will not have flow graphs and consequently will not be optimized. -const MaxFlowProg = 50000 - -func Flowstart(firstp *obj.Prog, newData func() interface{}) *Graph { - // Count and mark instructions to annotate. - nf := 0 - - for p := firstp; p != nil; p = p.Link { - p.Opt = nil // should be already, but just in case - Thearch.Proginfo(p) - if p.Info.Flags&Skip != 0 { - continue - } - p.Opt = &flowmark - nf++ - } - - if nf == 0 { - return nil - } - - if nf >= MaxFlowProg { - if Debug['v'] != 0 { - Warn("%v is too big (%d instructions)", Curfn.Nname.Sym, nf) - } - return nil - } - - // Allocate annotations and assign to instructions. - graph := new(Graph) - ff := make([]Flow, nf) - start := &ff[0] - id := 0 - var last *Flow - for p := firstp; p != nil; p = p.Link { - if p.Opt == nil { - continue - } - f := &ff[0] - ff = ff[1:] - p.Opt = f - f.Prog = p - if last != nil { - last.Link = f - } - last = f - if newData != nil { - f.Data = newData() - } - f.Id = int32(id) - id++ - } - - // Fill in pred/succ information. - var f1 *Flow - var p *obj.Prog - for f := start; f != nil; f = f.Link { - p = f.Prog - if p.Info.Flags&Break == 0 { - f1 = f.Link - f.S1 = f1 - f1.P1 = f - } - - if p.To.Type == obj.TYPE_BRANCH { - if p.To.Val == nil { - Fatal("pnil %v", p) - } - f1 = p.To.Val.(*obj.Prog).Opt.(*Flow) - if f1 == nil { - Fatal("fnil %v / %v", p, p.To.Val.(*obj.Prog)) - } - if f1 == f { - //fatal("self loop %P", p); - continue - } - - f.S2 = f1 - f.P2link = f1.P2 - f1.P2 = f - } - } - - graph.Start = start - graph.Num = nf - return graph -} - -func Flowend(graph *Graph) { - for f := graph.Start; f != nil; f = f.Link { - f.Prog.Info.Flags = 0 // drop cached proginfo - f.Prog.Opt = nil - } -} - -/* - * find looping structure - * - * 1) find reverse postordering - * 2) find approximate dominators, - * the actual dominators if the flow graph is reducible - * otherwise, dominators plus some other non-dominators. - * See Matthew S. Hecht and Jeffrey D. Ullman, - * "Analysis of a Simple Algorithm for Global Data Flow Problems", - * Conf. Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts, - * Oct. 1-3, 1973, pp. 207-217. - * 3) find all nodes with a predecessor dominated by the current node. - * such a node is a loop head. - * recursively, all preds with a greater rpo number are in the loop - */ -func postorder(r *Flow, rpo2r []*Flow, n int32) int32 { - r.Rpo = 1 - r1 := r.S1 - if r1 != nil && r1.Rpo == 0 { - n = postorder(r1, rpo2r, n) - } - r1 = r.S2 - if r1 != nil && r1.Rpo == 0 { - n = postorder(r1, rpo2r, n) - } - rpo2r[n] = r - n++ - return n -} - -func rpolca(idom []int32, rpo1 int32, rpo2 int32) int32 { - if rpo1 == -1 { - return rpo2 - } - var t int32 - for rpo1 != rpo2 { - if rpo1 > rpo2 { - t = rpo2 - rpo2 = rpo1 - rpo1 = t - } - - for rpo1 < rpo2 { - t = idom[rpo2] - if t >= rpo2 { - Fatal("bad idom") - } - rpo2 = t - } - } - - return rpo1 -} - -func doms(idom []int32, r int32, s int32) bool { - for s > r { - s = idom[s] - } - return s == r -} - -func loophead(idom []int32, r *Flow) bool { - src := r.Rpo - if r.P1 != nil && doms(idom, src, r.P1.Rpo) { - return true - } - for r = r.P2; r != nil; r = r.P2link { - if doms(idom, src, r.Rpo) { - return true - } - } - return false -} - -func loopmark(rpo2r **Flow, head int32, r *Flow) { - if r.Rpo < head || r.Active == head { - return - } - r.Active = head - r.Loop += LOOP - if r.P1 != nil { - loopmark(rpo2r, head, r.P1) - } - for r = r.P2; r != nil; r = r.P2link { - loopmark(rpo2r, head, r) - } -} - -func flowrpo(g *Graph) { - g.Rpo = make([]*Flow, g.Num) - idom := make([]int32, g.Num) - - for r1 := g.Start; r1 != nil; r1 = r1.Link { - r1.Active = 0 - } - - rpo2r := g.Rpo - d := postorder(g.Start, rpo2r, 0) - nr := int32(g.Num) - if d > nr { - Fatal("too many reg nodes %d %d", d, nr) - } - nr = d - var r1 *Flow - for i := int32(0); i < nr/2; i++ { - r1 = rpo2r[i] - rpo2r[i] = rpo2r[nr-1-i] - rpo2r[nr-1-i] = r1 - } - - for i := int32(0); i < nr; i++ { - rpo2r[i].Rpo = i - } - - idom[0] = 0 - var me int32 - for i := int32(0); i < nr; i++ { - r1 = rpo2r[i] - me = r1.Rpo - d = -1 - - // rpo2r[r->rpo] == r protects against considering dead code, - // which has r->rpo == 0. - if r1.P1 != nil && rpo2r[r1.P1.Rpo] == r1.P1 && r1.P1.Rpo < me { - d = r1.P1.Rpo - } - for r1 = r1.P2; r1 != nil; r1 = r1.P2link { - if rpo2r[r1.Rpo] == r1 && r1.Rpo < me { - d = rpolca(idom, d, r1.Rpo) - } - } - idom[i] = d - } - - for i := int32(0); i < nr; i++ { - r1 = rpo2r[i] - r1.Loop++ - if r1.P2 != nil && loophead(idom, r1) { - loopmark(&rpo2r[0], i, r1) - } - } - - for r1 := g.Start; r1 != nil; r1 = r1.Link { - r1.Active = 0 - } -} - -func Uniqp(r *Flow) *Flow { - r1 := r.P1 - if r1 == nil { - r1 = r.P2 - if r1 == nil || r1.P2link != nil { - return nil - } - } else if r.P2 != nil { - return nil - } - return r1 -} - -func Uniqs(r *Flow) *Flow { - r1 := r.S1 - if r1 == nil { - r1 = r.S2 - if r1 == nil { - return nil - } - } else if r.S2 != nil { - return nil - } - return r1 -} - -// The compilers assume they can generate temporary variables -// as needed to preserve the right semantics or simplify code -// generation and the back end will still generate good code. -// This results in a large number of ephemeral temporary variables. -// Merge temps with non-overlapping lifetimes and equal types using the -// greedy algorithm in Poletto and Sarkar, "Linear Scan Register Allocation", -// ACM TOPLAS 1999. - -type TempVar struct { - node *Node - def *Flow // definition of temp var - use *Flow // use list, chained through Flow.data - merge *TempVar // merge var with this one - start int64 // smallest Prog.pc in live range - end int64 // largest Prog.pc in live range - addr uint8 // address taken - no accurate end - removed uint8 // removed from program -} - -type startcmp []*TempVar - -func (x startcmp) Len() int { - return len(x) -} - -func (x startcmp) Swap(i, j int) { - x[i], x[j] = x[j], x[i] -} - -func (x startcmp) Less(i, j int) bool { - a := x[i] - b := x[j] - - if a.start < b.start { - return true - } - if a.start > b.start { - return false - } - - // Order what's left by id or symbol name, - // just so that sort is forced into a specific ordering, - // so that the result of the sort does not depend on - // the sort implementation. - if a.def != b.def { - return int(a.def.Id-b.def.Id) < 0 - } - if a.node != b.node { - return stringsCompare(a.node.Sym.Name, b.node.Sym.Name) < 0 - } - return false -} - -// Is n available for merging? -func canmerge(n *Node) bool { - return n.Class == PAUTO && strings.HasPrefix(n.Sym.Name, "autotmp") -} - -func mergetemp(firstp *obj.Prog) { - const ( - debugmerge = 0 - ) - - g := Flowstart(firstp, nil) - if g == nil { - return - } - - // Build list of all mergeable variables. - nvar := 0 - for l := Curfn.Func.Dcl; l != nil; l = l.Next { - if canmerge(l.N) { - nvar++ - } - } - - var_ := make([]TempVar, nvar) - nvar = 0 - var n *Node - var v *TempVar - for l := Curfn.Func.Dcl; l != nil; l = l.Next { - n = l.N - if canmerge(n) { - v = &var_[nvar] - nvar++ - n.Opt = v - v.node = n - } - } - - // Build list of uses. - // We assume that the earliest reference to a temporary is its definition. - // This is not true of variables in general but our temporaries are all - // single-use (that's why we have so many!). - for f := g.Start; f != nil; f = f.Link { - p := f.Prog - if p.From.Node != nil && ((p.From.Node).(*Node)).Opt != nil && p.To.Node != nil && ((p.To.Node).(*Node)).Opt != nil { - Fatal("double node %v", p) - } - v = nil - n, _ = p.From.Node.(*Node) - if n != nil { - v, _ = n.Opt.(*TempVar) - } - if v == nil { - n, _ = p.To.Node.(*Node) - if n != nil { - v, _ = n.Opt.(*TempVar) - } - } - if v != nil { - if v.def == nil { - v.def = f - } - f.Data = v.use - v.use = f - if n == p.From.Node && (p.Info.Flags&LeftAddr != 0) { - v.addr = 1 - } - } - } - - if debugmerge > 1 && Debug['v'] != 0 { - Dumpit("before", g.Start, 0) - } - - nkill := 0 - - // Special case. - for i := 0; i < len(var_); i++ { - v = &var_[i] - if v.addr != 0 { - continue - } - - // Used in only one instruction, which had better be a write. - f := v.use - if f != nil && f.Data.(*Flow) == nil { - p := f.Prog - if p.To.Node == v.node && (p.Info.Flags&RightWrite != 0) && p.Info.Flags&RightRead == 0 { - p.As = obj.ANOP - p.To = obj.Addr{} - v.removed = 1 - if debugmerge > 0 && Debug['v'] != 0 { - fmt.Printf("drop write-only %v\n", v.node.Sym) - } - } else { - Fatal("temp used and not set: %v", p) - } - nkill++ - continue - } - - // Written in one instruction, read in the next, otherwise unused, - // no jumps to the next instruction. Happens mainly in 386 compiler. - f = v.use - if f != nil && f.Link == f.Data.(*Flow) && (f.Data.(*Flow)).Data.(*Flow) == nil && Uniqp(f.Link) == f { - p := f.Prog - p1 := f.Link.Prog - const ( - SizeAny = SizeB | SizeW | SizeL | SizeQ | SizeF | SizeD - ) - if p.From.Node == v.node && p1.To.Node == v.node && (p.Info.Flags&Move != 0) && (p.Info.Flags|p1.Info.Flags)&(LeftAddr|RightAddr) == 0 && p.Info.Flags&SizeAny == p1.Info.Flags&SizeAny { - p1.From = p.From - Thearch.Excise(f) - v.removed = 1 - if debugmerge > 0 && Debug['v'] != 0 { - fmt.Printf("drop immediate-use %v\n", v.node.Sym) - } - } - - nkill++ - continue - } - } - - // Traverse live range of each variable to set start, end. - // Each flood uses a new value of gen so that we don't have - // to clear all the r->active words after each variable. - gen := int32(0) - - for i := 0; i < len(var_); i++ { - v = &var_[i] - gen++ - for f := v.use; f != nil; f = f.Data.(*Flow) { - mergewalk(v, f, uint32(gen)) - } - if v.addr != 0 { - gen++ - for f := v.use; f != nil; f = f.Data.(*Flow) { - varkillwalk(v, f, uint32(gen)) - } - } - } - - // Sort variables by start. - bystart := make([]*TempVar, len(var_)) - - for i := 0; i < len(var_); i++ { - bystart[i] = &var_[i] - } - sort.Sort(startcmp(bystart[:len(var_)])) - - // List of in-use variables, sorted by end, so that the ones that - // will last the longest are the earliest ones in the array. - // The tail inuse[nfree:] holds no-longer-used variables. - // In theory we should use a sorted tree so that insertions are - // guaranteed O(log n) and then the loop is guaranteed O(n log n). - // In practice, it doesn't really matter. - inuse := make([]*TempVar, len(var_)) - - ninuse := 0 - nfree := len(var_) - var t *Type - var v1 *TempVar - var j int - for i := 0; i < len(var_); i++ { - v = bystart[i] - if debugmerge > 0 && Debug['v'] != 0 { - fmt.Printf("consider %v: removed=%d\n", Nconv(v.node, obj.FmtSharp), v.removed) - } - - if v.removed != 0 { - continue - } - - // Expire no longer in use. - for ninuse > 0 && inuse[ninuse-1].end < v.start { - ninuse-- - v1 = inuse[ninuse] - nfree-- - inuse[nfree] = v1 - } - - if debugmerge > 0 && Debug['v'] != 0 { - fmt.Printf("consider %v: removed=%d nfree=%d nvar=%d\n", Nconv(v.node, obj.FmtSharp), v.removed, nfree, len(var_)) - } - - // Find old temp to reuse if possible. - t = v.node.Type - - for j = nfree; j < len(var_); j++ { - v1 = inuse[j] - if debugmerge > 0 && Debug['v'] != 0 { - fmt.Printf("consider %v: maybe %v: type=%v,%v addrtaken=%v,%v\n", Nconv(v.node, obj.FmtSharp), Nconv(v1.node, obj.FmtSharp), t, v1.node.Type, v.node.Addrtaken, v1.node.Addrtaken) - } - - // Require the types to match but also require the addrtaken bits to match. - // If a variable's address is taken, that disables registerization for the individual - // words of the variable (for example, the base,len,cap of a slice). - // We don't want to merge a non-addressed var with an addressed one and - // inhibit registerization of the former. - if Eqtype(t, v1.node.Type) && v.node.Addrtaken == v1.node.Addrtaken { - inuse[j] = inuse[nfree] - nfree++ - if v1.merge != nil { - v.merge = v1.merge - } else { - v.merge = v1 - } - nkill++ - break - } - } - - // Sort v into inuse. - j = ninuse - ninuse++ - - for j > 0 && inuse[j-1].end < v.end { - inuse[j] = inuse[j-1] - j-- - } - - inuse[j] = v - } - - if debugmerge > 0 && Debug['v'] != 0 { - fmt.Printf("%v [%d - %d]\n", Curfn.Nname.Sym, len(var_), nkill) - var v *TempVar - for i := 0; i < len(var_); i++ { - v = &var_[i] - fmt.Printf("var %v %v %d-%d", Nconv(v.node, obj.FmtSharp), v.node.Type, v.start, v.end) - if v.addr != 0 { - fmt.Printf(" addr=1") - } - if v.removed != 0 { - fmt.Printf(" dead=1") - } - if v.merge != nil { - fmt.Printf(" merge %v", Nconv(v.merge.node, obj.FmtSharp)) - } - if v.start == v.end && v.def != nil { - fmt.Printf(" %v", v.def.Prog) - } - fmt.Printf("\n") - } - - if debugmerge > 1 && Debug['v'] != 0 { - Dumpit("after", g.Start, 0) - } - } - - // Update node references to use merged temporaries. - for f := g.Start; f != nil; f = f.Link { - p := f.Prog - n, _ = p.From.Node.(*Node) - if n != nil { - v, _ = n.Opt.(*TempVar) - if v != nil && v.merge != nil { - p.From.Node = v.merge.node - } - } - n, _ = p.To.Node.(*Node) - if n != nil { - v, _ = n.Opt.(*TempVar) - if v != nil && v.merge != nil { - p.To.Node = v.merge.node - } - } - } - - // Delete merged nodes from declaration list. - var l *NodeList - for lp := &Curfn.Func.Dcl; ; { - l = *lp - if l == nil { - break - } - - Curfn.Func.Dcl.End = l - n = l.N - v, _ = n.Opt.(*TempVar) - if v != nil && (v.merge != nil || v.removed != 0) { - *lp = l.Next - continue - } - - lp = &l.Next - } - - // Clear aux structures. - for i := 0; i < len(var_); i++ { - var_[i].node.Opt = nil - } - - Flowend(g) -} - -func mergewalk(v *TempVar, f0 *Flow, gen uint32) { - var p *obj.Prog - var f1 *Flow - - for f1 = f0; f1 != nil; f1 = f1.P1 { - if uint32(f1.Active) == gen { - break - } - f1.Active = int32(gen) - p = f1.Prog - if v.end < p.Pc { - v.end = p.Pc - } - if f1 == v.def { - v.start = p.Pc - break - } - } - - var f2 *Flow - for f := f0; f != f1; f = f.P1 { - for f2 = f.P2; f2 != nil; f2 = f2.P2link { - mergewalk(v, f2, gen) - } - } -} - -func varkillwalk(v *TempVar, f0 *Flow, gen uint32) { - var p *obj.Prog - var f1 *Flow - - for f1 = f0; f1 != nil; f1 = f1.S1 { - if uint32(f1.Active) == gen { - break - } - f1.Active = int32(gen) - p = f1.Prog - if v.end < p.Pc { - v.end = p.Pc - } - if v.start > p.Pc { - v.start = p.Pc - } - if p.As == obj.ARET || (p.As == obj.AVARKILL && p.To.Node == v.node) { - break - } - } - - for f := f0; f != f1; f = f.S1 { - varkillwalk(v, f.S2, gen) - } -} - -// Eliminate redundant nil pointer checks. -// -// The code generation pass emits a CHECKNIL for every possibly nil pointer. -// This pass removes a CHECKNIL if every predecessor path has already -// checked this value for nil. -// -// Simple backwards flood from check to definition. -// Run prog loop backward from end of program to beginning to avoid quadratic -// behavior removing a run of checks. -// -// Assume that stack variables with address not taken can be loaded multiple times -// from memory without being rechecked. Other variables need to be checked on -// each load. - -var killed int // f->data is either nil or &killed - -func nilopt(firstp *obj.Prog) { - g := Flowstart(firstp, nil) - if g == nil { - return - } - - if Debug_checknil > 1 { /* || strcmp(curfn->nname->sym->name, "f1") == 0 */ - Dumpit("nilopt", g.Start, 0) - } - - ncheck := 0 - nkill := 0 - var p *obj.Prog - for f := g.Start; f != nil; f = f.Link { - p = f.Prog - if p.As != obj.ACHECKNIL || !Thearch.Regtyp(&p.From) { - continue - } - ncheck++ - if Thearch.Stackaddr(&p.From) { - if Debug_checknil != 0 && p.Lineno > 1 { - Warnl(int(p.Lineno), "removed nil check of SP address") - } - f.Data = &killed - continue - } - - nilwalkfwd(f) - if f.Data != nil { - if Debug_checknil != 0 && p.Lineno > 1 { - Warnl(int(p.Lineno), "removed nil check before indirect") - } - continue - } - - nilwalkback(f) - if f.Data != nil { - if Debug_checknil != 0 && p.Lineno > 1 { - Warnl(int(p.Lineno), "removed repeated nil check") - } - continue - } - } - - for f := g.Start; f != nil; f = f.Link { - if f.Data != nil { - nkill++ - Thearch.Excise(f) - } - } - - Flowend(g) - - if Debug_checknil > 1 { - fmt.Printf("%v: removed %d of %d nil checks\n", Curfn.Nname.Sym, nkill, ncheck) - } -} - -func nilwalkback(fcheck *Flow) { - for f := fcheck; f != nil; f = Uniqp(f) { - p := f.Prog - if (p.Info.Flags&RightWrite != 0) && Thearch.Sameaddr(&p.To, &fcheck.Prog.From) { - // Found initialization of value we're checking for nil. - // without first finding the check, so this one is unchecked. - return - } - - if f != fcheck && p.As == obj.ACHECKNIL && Thearch.Sameaddr(&p.From, &fcheck.Prog.From) { - fcheck.Data = &killed - return - } - } -} - -// Here is a more complex version that scans backward across branches. -// It assumes fcheck->kill = 1 has been set on entry, and its job is to find a reason -// to keep the check (setting fcheck->kill = 0). -// It doesn't handle copying of aggregates as well as I would like, -// nor variables with their address taken, -// and it's too subtle to turn on this late in Go 1.2. Perhaps for Go 1.3. -/* -for(f1 = f0; f1 != nil; f1 = f1->p1) { - if(f1->active == gen) - break; - f1->active = gen; - p = f1->prog; - - // If same check, stop this loop but still check - // alternate predecessors up to this point. - if(f1 != fcheck && p->as == ACHECKNIL && thearch.sameaddr(&p->from, &fcheck->prog->from)) - break; - - if((p.Info.flags & RightWrite) && thearch.sameaddr(&p->to, &fcheck->prog->from)) { - // Found initialization of value we're checking for nil. - // without first finding the check, so this one is unchecked. - fcheck->kill = 0; - return; - } - - if(f1->p1 == nil && f1->p2 == nil) { - print("lost pred for %P\n", fcheck->prog); - for(f1=f0; f1!=nil; f1=f1->p1) { - thearch.proginfo(&info, f1->prog); - print("\t%P %d %d %D %D\n", r1->prog, info.flags&RightWrite, thearch.sameaddr(&f1->prog->to, &fcheck->prog->from), &f1->prog->to, &fcheck->prog->from); - } - fatal("lost pred trail"); - } -} - -for(f = f0; f != f1; f = f->p1) - for(f2 = f->p2; f2 != nil; f2 = f2->p2link) - nilwalkback(fcheck, f2, gen); -*/ - -func nilwalkfwd(fcheck *Flow) { - // If the path down from rcheck dereferences the address - // (possibly with a small offset) before writing to memory - // and before any subsequent checks, it's okay to wait for - // that implicit check. Only consider this basic block to - // avoid problems like: - // _ = *x // should panic - // for {} // no writes but infinite loop may be considered visible - - var last *Flow - for f := Uniqs(fcheck); f != nil; f = Uniqs(f) { - p := f.Prog - if (p.Info.Flags&LeftRead != 0) && Thearch.Smallindir(&p.From, &fcheck.Prog.From) { - fcheck.Data = &killed - return - } - - if (p.Info.Flags&(RightRead|RightWrite) != 0) && Thearch.Smallindir(&p.To, &fcheck.Prog.From) { - fcheck.Data = &killed - return - } - - // Stop if another nil check happens. - if p.As == obj.ACHECKNIL { - return - } - - // Stop if value is lost. - if (p.Info.Flags&RightWrite != 0) && Thearch.Sameaddr(&p.To, &fcheck.Prog.From) { - return - } - - // Stop if memory write. - if (p.Info.Flags&RightWrite != 0) && !Thearch.Regtyp(&p.To) { - return - } - - // Stop if we jump backward. - if last != nil && f.Id <= last.Id { - return - } - last = f - } -} diff --git a/src/cmd/internal/gc/racewalk.go b/src/cmd/internal/gc/racewalk.go deleted file mode 100644 index 446ec038c8..0000000000 --- a/src/cmd/internal/gc/racewalk.go +++ /dev/null @@ -1,639 +0,0 @@ -// Copyright 2012 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 gc - -import ( - "fmt" - "strings" -) - -// The racewalk pass modifies the code tree for the function as follows: -// -// 1. It inserts a call to racefuncenter at the beginning of each function. -// 2. It inserts a call to racefuncexit at the end of each function. -// 3. It inserts a call to raceread before each memory read. -// 4. It inserts a call to racewrite before each memory write. -// -// The rewriting is not yet complete. Certain nodes are not rewritten -// but should be. - -// TODO(dvyukov): do not instrument initialization as writes: -// a := make([]int, 10) - -// Do not instrument the following packages at all, -// at best instrumentation would cause infinite recursion. -var omit_pkgs = []string{"runtime", "runtime/race"} - -// Only insert racefuncenter/racefuncexit into the following packages. -// Memory accesses in the packages are either uninteresting or will cause false positives. -var noinst_pkgs = []string{"sync", "sync/atomic"} - -func ispkgin(pkgs []string) bool { - if myimportpath != "" { - for i := 0; i < len(pkgs); i++ { - if myimportpath == pkgs[i] { - return true - } - } - } - - return false -} - -func isforkfunc(fn *Node) bool { - // Special case for syscall.forkAndExecInChild. - // In the child, this function must not acquire any locks, because - // they might have been locked at the time of the fork. This means - // no rescheduling, no malloc calls, and no new stack segments. - // Race instrumentation does all of the above. - return myimportpath != "" && myimportpath == "syscall" && fn.Nname.Sym.Name == "forkAndExecInChild" -} - -func racewalk(fn *Node) { - if ispkgin(omit_pkgs) || isforkfunc(fn) { - return - } - - if !ispkgin(noinst_pkgs) { - racewalklist(fn.Nbody, nil) - - // nothing interesting for race detector in fn->enter - racewalklist(fn.Func.Exit, nil) - } - - // nodpc is the PC of the caller as extracted by - // getcallerpc. We use -widthptr(FP) for x86. - // BUG: this will not work on arm. - nodpc := Nod(OXXX, nil, nil) - - *nodpc = *nodfp - nodpc.Type = Types[TUINTPTR] - nodpc.Xoffset = int64(-Widthptr) - nd := mkcall("racefuncenter", nil, nil, nodpc) - fn.Func.Enter = concat(list1(nd), fn.Func.Enter) - nd = mkcall("racefuncexit", nil, nil) - fn.Func.Exit = list(fn.Func.Exit, nd) - - if Debug['W'] != 0 { - s := fmt.Sprintf("after racewalk %v", fn.Nname.Sym) - dumplist(s, fn.Nbody) - s = fmt.Sprintf("enter %v", fn.Nname.Sym) - dumplist(s, fn.Func.Enter) - s = fmt.Sprintf("exit %v", fn.Nname.Sym) - dumplist(s, fn.Func.Exit) - } -} - -func racewalklist(l *NodeList, init **NodeList) { - var instr *NodeList - - for ; l != nil; l = l.Next { - instr = nil - racewalknode(&l.N, &instr, 0, 0) - if init == nil { - l.N.Ninit = concat(l.N.Ninit, instr) - } else { - *init = concat(*init, instr) - } - } -} - -// walkexpr and walkstmt combined -// walks the tree and adds calls to the -// instrumentation code to top-level (statement) nodes' init -func racewalknode(np **Node, init **NodeList, wr int, skip int) { - n := *np - - if n == nil { - return - } - - if Debug['w'] > 1 { - Dump("racewalk-before", n) - } - setlineno(n) - if init == nil { - Fatal("racewalk: bad init list") - } - if init == &n.Ninit { - // If init == &n->ninit and n->ninit is non-nil, - // racewalknode might append it to itself. - // nil it out and handle it separately before putting it back. - l := n.Ninit - - n.Ninit = nil - racewalklist(l, nil) - racewalknode(&n, &l, wr, skip) // recurse with nil n->ninit - appendinit(&n, l) - *np = n - return - } - - racewalklist(n.Ninit, nil) - - switch n.Op { - default: - Fatal("racewalk: unknown node type %v", Oconv(int(n.Op), 0)) - - case OAS, OASWB, OAS2FUNC: - racewalknode(&n.Left, init, 1, 0) - racewalknode(&n.Right, init, 0, 0) - goto ret - - // can't matter - case OCFUNC, OVARKILL: - goto ret - - case OBLOCK: - if n.List == nil { - goto ret - } - - switch n.List.N.Op { - // Blocks are used for multiple return function calls. - // x, y := f() becomes BLOCK{CALL f, AS x [SP+0], AS y [SP+n]} - // We don't want to instrument between the statements because it will - // smash the results. - case OCALLFUNC, OCALLMETH, OCALLINTER: - racewalknode(&n.List.N, &n.List.N.Ninit, 0, 0) - - var fini *NodeList - racewalklist(n.List.Next, &fini) - n.List = concat(n.List, fini) - - // Ordinary block, for loop initialization or inlined bodies. - default: - racewalklist(n.List, nil) - } - - goto ret - - case ODEFER: - racewalknode(&n.Left, init, 0, 0) - goto ret - - case OPROC: - racewalknode(&n.Left, init, 0, 0) - goto ret - - case OCALLINTER: - racewalknode(&n.Left, init, 0, 0) - goto ret - - // Instrument dst argument of runtime.writebarrier* calls - // 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 - - case ONOT, - OMINUS, - OPLUS, - OREAL, - OIMAG, - OCOM, - OSQRT: - racewalknode(&n.Left, init, wr, 0) - goto ret - - case ODOTINTER: - racewalknode(&n.Left, init, 0, 0) - goto ret - - case ODOT: - racewalknode(&n.Left, init, 0, 1) - callinstr(&n, init, wr, skip) - goto ret - - case ODOTPTR: // dst = (*x).f with implicit *; otherwise it's ODOT+OIND - racewalknode(&n.Left, init, 0, 0) - - callinstr(&n, init, wr, skip) - goto ret - - case OIND: // *p - racewalknode(&n.Left, init, 0, 0) - - callinstr(&n, init, wr, skip) - goto ret - - case OSPTR, OLEN, OCAP: - racewalknode(&n.Left, init, 0, 0) - if Istype(n.Left.Type, TMAP) { - n1 := Nod(OCONVNOP, n.Left, nil) - n1.Type = Ptrto(Types[TUINT8]) - n1 = Nod(OIND, n1, nil) - typecheck(&n1, Erv) - callinstr(&n1, init, 0, skip) - } - - goto ret - - case OLSH, - ORSH, - OLROT, - OAND, - OANDNOT, - OOR, - OXOR, - OSUB, - OMUL, - OHMUL, - OEQ, - ONE, - OLT, - OLE, - OGE, - OGT, - OADD, - OCOMPLEX: - racewalknode(&n.Left, init, wr, 0) - racewalknode(&n.Right, init, wr, 0) - goto ret - - case OANDAND, OOROR: - racewalknode(&n.Left, init, wr, 0) - - // walk has ensured the node has moved to a location where - // side effects are safe. - // n->right may not be executed, - // so instrumentation goes to n->right->ninit, not init. - racewalknode(&n.Right, &n.Right.Ninit, wr, 0) - - goto ret - - case ONAME: - callinstr(&n, init, wr, skip) - goto ret - - case OCONV: - racewalknode(&n.Left, init, wr, 0) - goto ret - - case OCONVNOP: - racewalknode(&n.Left, init, wr, 0) - goto ret - - case ODIV, OMOD: - racewalknode(&n.Left, init, wr, 0) - racewalknode(&n.Right, init, wr, 0) - goto ret - - case OINDEX: - if !Isfixedarray(n.Left.Type) { - racewalknode(&n.Left, init, 0, 0) - } else if !islvalue(n.Left) { - // index of unaddressable array, like Map[k][i]. - racewalknode(&n.Left, init, wr, 0) - - racewalknode(&n.Right, init, 0, 0) - goto ret - } - - racewalknode(&n.Right, init, 0, 0) - if n.Left.Type.Etype != TSTRING { - callinstr(&n, init, wr, skip) - } - goto ret - - case OSLICE, OSLICEARR, OSLICE3, OSLICE3ARR: - racewalknode(&n.Left, init, 0, 0) - goto ret - - case OADDR: - racewalknode(&n.Left, init, 0, 1) - goto ret - - // n->left is Type* which is not interesting. - case OEFACE: - racewalknode(&n.Right, init, 0, 0) - - goto ret - - case OITAB: - racewalknode(&n.Left, init, 0, 0) - goto ret - - // should not appear in AST by now - case OSEND, - ORECV, - OCLOSE, - ONEW, - OXCASE, - OXFALL, - OCASE, - OPANIC, - ORECOVER, - OCONVIFACE, - OCMPIFACE, - OMAKECHAN, - OMAKEMAP, - OMAKESLICE, - OCALL, - OCOPY, - OAPPEND, - ORUNESTR, - OARRAYBYTESTR, - OARRAYRUNESTR, - OSTRARRAYBYTE, - OSTRARRAYRUNE, - OINDEXMAP, - // lowered to call - OCMPSTR, - OADDSTR, - ODOTTYPE, - ODOTTYPE2, - OAS2DOTTYPE, - OCALLPART, - // lowered to PTRLIT - OCLOSURE, // lowered to PTRLIT - ORANGE, // lowered to ordinary for loop - OARRAYLIT, // lowered to assignments - OMAPLIT, - OSTRUCTLIT, - OAS2, - OAS2RECV, - OAS2MAPR, - OASOP: - Yyerror("racewalk: %v must be lowered by now", Oconv(int(n.Op), 0)) - - goto ret - - // impossible nodes: only appear in backend. - case ORROTC, OEXTEND: - Yyerror("racewalk: %v cannot exist now", Oconv(int(n.Op), 0)) - goto ret - - case OGETG: - Yyerror("racewalk: OGETG can happen only in runtime which we don't instrument") - goto ret - - // just do generic traversal - case OFOR, - OIF, - OCALLMETH, - ORETURN, - ORETJMP, - OSWITCH, - OSELECT, - OEMPTY, - OBREAK, - OCONTINUE, - OFALL, - OGOTO, - OLABEL: - goto ret - - // does not require instrumentation - case OPRINT, // don't bother instrumenting it - OPRINTN, // don't bother instrumenting it - OCHECKNIL, // always followed by a read. - OPARAM, // it appears only in fn->exit to copy heap params back - OCLOSUREVAR, // immutable pointer to captured variable - ODOTMETH, // either part of CALLMETH or CALLPART (lowered to PTRLIT) - OINDREG, // at this stage, only n(SP) nodes from nodarg - ODCL, // declarations (without value) cannot be races - ODCLCONST, - ODCLTYPE, - OTYPE, - ONONAME, - OLITERAL, - OSLICESTR, // always preceded by bounds checking, avoid double instrumentation. - OTYPESW: // ignored by code generation, do not instrument. - goto ret - } - -ret: - if n.Op != OBLOCK { // OBLOCK is handled above in a special way. - racewalklist(n.List, init) - } - if n.Ntest != nil { - racewalknode(&n.Ntest, &n.Ntest.Ninit, 0, 0) - } - if n.Nincr != nil { - racewalknode(&n.Nincr, &n.Nincr.Ninit, 0, 0) - } - racewalklist(n.Nbody, nil) - racewalklist(n.Nelse, nil) - racewalklist(n.Rlist, nil) - *np = n -} - -func isartificial(n *Node) bool { - // compiler-emitted artificial things that we do not want to instrument, - // cant' possibly participate in a data race. - if n.Op == ONAME && n.Sym != nil && n.Sym.Name != "" { - if n.Sym.Name == "_" { - return true - } - - // autotmp's are always local - if strings.HasPrefix(n.Sym.Name, "autotmp_") { - return true - } - - // statictmp's are read-only - if strings.HasPrefix(n.Sym.Name, "statictmp_") { - return true - } - - // go.itab is accessed only by the compiler and runtime (assume safe) - if n.Sym.Pkg != nil && n.Sym.Pkg.Name != "" && n.Sym.Pkg.Name == "go.itab" { - return true - } - } - - return false -} - -func callinstr(np **Node, init **NodeList, wr int, skip int) bool { - n := *np - - //print("callinstr for %+N [ %O ] etype=%E class=%d\n", - // n, n->op, n->type ? n->type->etype : -1, n->class); - - if skip != 0 || n.Type == nil || n.Type.Etype >= TIDEAL { - return false - } - t := n.Type - if isartificial(n) { - return false - } - - b := outervalue(n) - - // it skips e.g. stores to ... parameter array - if isartificial(b) { - return false - } - class := b.Class - - // BUG: we _may_ want to instrument PAUTO sometimes - // e.g. if we've got a local variable/method receiver - // that has got a pointer inside. Whether it points to - // the heap or not is impossible to know at compile time - if (class&PHEAP != 0) || class == PPARAMREF || class == PEXTERN || b.Op == OINDEX || b.Op == ODOTPTR || b.Op == OIND { - hascalls := 0 - foreach(n, hascallspred, &hascalls) - if hascalls != 0 { - n = detachexpr(n, init) - *np = n - } - - n = treecopy(n) - makeaddable(n) - var f *Node - if t.Etype == TSTRUCT || Isfixedarray(t) { - name := "racereadrange" - if wr != 0 { - name = "racewriterange" - } - f = mkcall(name, nil, init, uintptraddr(n), Nodintconst(t.Width)) - } else { - name := "raceread" - if wr != 0 { - name = "racewrite" - } - f = mkcall(name, nil, init, uintptraddr(n)) - } - - *init = list(*init, f) - return true - } - - return false -} - -// makeaddable returns a node whose memory location is the -// same as n, but which is addressable in the Go language -// sense. -// This is different from functions like cheapexpr that may make -// a copy of their argument. -func makeaddable(n *Node) { - // The arguments to uintptraddr technically have an address but - // may not be addressable in the Go sense: for example, in the case - // of T(v).Field where T is a struct type and v is - // an addressable value. - switch n.Op { - case OINDEX: - if Isfixedarray(n.Left.Type) { - makeaddable(n.Left) - } - - // Turn T(v).Field into v.Field - case ODOT, OXDOT: - if n.Left.Op == OCONVNOP { - n.Left = n.Left.Left - } - makeaddable(n.Left) - - // nothing to do - case ODOTPTR: - fallthrough - default: - break - } -} - -func uintptraddr(n *Node) *Node { - r := Nod(OADDR, n, nil) - r.Bounded = true - r = conv(r, Types[TUNSAFEPTR]) - r = conv(r, Types[TUINTPTR]) - return r -} - -func detachexpr(n *Node, init **NodeList) *Node { - addr := Nod(OADDR, n, nil) - l := temp(Ptrto(n.Type)) - as := Nod(OAS, l, addr) - typecheck(&as, Etop) - walkexpr(&as, init) - *init = list(*init, as) - ind := Nod(OIND, l, nil) - typecheck(&ind, Erv) - walkexpr(&ind, init) - return ind -} - -func foreachnode(n *Node, f func(*Node, interface{}), c interface{}) { - if n != nil { - f(n, c) - } -} - -func foreachlist(l *NodeList, f func(*Node, interface{}), c interface{}) { - for ; l != nil; l = l.Next { - foreachnode(l.N, f, c) - } -} - -func foreach(n *Node, f func(*Node, interface{}), c interface{}) { - foreachlist(n.Ninit, f, c) - foreachnode(n.Left, f, c) - foreachnode(n.Right, f, c) - foreachlist(n.List, f, c) - foreachnode(n.Ntest, f, c) - foreachnode(n.Nincr, f, c) - foreachlist(n.Nbody, f, c) - foreachlist(n.Nelse, f, c) - foreachlist(n.Rlist, f, c) -} - -func hascallspred(n *Node, c interface{}) { - switch n.Op { - case OCALL, OCALLFUNC, OCALLMETH, OCALLINTER: - (*c.(*int))++ - } -} - -// appendinit is like addinit in subr.go -// but appends rather than prepends. -func appendinit(np **Node, init *NodeList) { - if init == nil { - return - } - - n := *np - switch n.Op { - // There may be multiple refs to this node; - // introduce OCONVNOP to hold init list. - case ONAME, OLITERAL: - n = Nod(OCONVNOP, n, nil) - - n.Type = n.Left.Type - n.Typecheck = 1 - *np = n - } - - n.Ninit = concat(n.Ninit, init) - n.Ullman = UINF -} diff --git a/src/cmd/internal/gc/range.go b/src/cmd/internal/gc/range.go deleted file mode 100644 index 03beb1c673..0000000000 --- a/src/cmd/internal/gc/range.go +++ /dev/null @@ -1,406 +0,0 @@ -// Copyright 2009 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 gc - -import "cmd/internal/obj" - -/* - * range - */ -func typecheckrange(n *Node) { - var toomany int - var why string - var t1 *Type - var t2 *Type - var v1 *Node - var v2 *Node - - // Typechecking order is important here: - // 0. first typecheck range expression (slice/map/chan), - // it is evaluated only once and so logically it is not part of the loop. - // 1. typcheck produced values, - // this part can declare new vars and so it must be typechecked before body, - // because body can contain a closure that captures the vars. - // 2. decldepth++ to denote loop body. - // 3. typecheck body. - // 4. decldepth--. - - typecheck(&n.Right, Erv) - - t := n.Right.Type - if t == nil { - goto out - } - - // delicate little dance. see typecheckas2 - for ll := n.List; ll != nil; ll = ll.Next { - if ll.N.Defn != n { - typecheck(&ll.N, Erv|Easgn) - } - } - - if Isptr[t.Etype] && Isfixedarray(t.Type) { - t = t.Type - } - n.Type = t - - toomany = 0 - switch t.Etype { - default: - Yyerror("cannot range over %v", Nconv(n.Right, obj.FmtLong)) - goto out - - case TARRAY: - t1 = Types[TINT] - t2 = t.Type - - case TMAP: - t1 = t.Down - t2 = t.Type - - case TCHAN: - if t.Chan&Crecv == 0 { - Yyerror("invalid operation: range %v (receive from send-only type %v)", n.Right, n.Right.Type) - goto out - } - - t1 = t.Type - t2 = nil - if count(n.List) == 2 { - toomany = 1 - } - - case TSTRING: - t1 = Types[TINT] - t2 = runetype - } - - if count(n.List) > 2 || toomany != 0 { - Yyerror("too many variables in range") - } - - v1 = nil - if n.List != nil { - v1 = n.List.N - } - v2 = nil - if n.List != nil && n.List.Next != nil { - v2 = n.List.Next.N - } - - // this is not only a optimization but also a requirement in the spec. - // "if the second iteration variable is the blank identifier, the range - // clause is equivalent to the same clause with only the first variable - // present." - if isblank(v2) { - if v1 != nil { - n.List = list1(v1) - } - v2 = nil - } - - if v1 != nil { - if v1.Defn == n { - v1.Type = t1 - } else if v1.Type != nil && assignop(t1, v1.Type, &why) == 0 { - Yyerror("cannot assign type %v to %v in range%s", t1, Nconv(v1, obj.FmtLong), why) - } - checkassign(n, v1) - } - - if v2 != nil { - if v2.Defn == n { - v2.Type = t2 - } else if v2.Type != nil && assignop(t2, v2.Type, &why) == 0 { - Yyerror("cannot assign type %v to %v in range%s", t2, Nconv(v2, obj.FmtLong), why) - } - checkassign(n, v2) - } - - // second half of dance -out: - n.Typecheck = 1 - - for ll := n.List; ll != nil; ll = ll.Next { - if ll.N.Typecheck == 0 { - typecheck(&ll.N, Erv|Easgn) - } - } - - decldepth++ - typechecklist(n.Nbody, Etop) - decldepth-- -} - -func walkrange(n *Node) { - // variable name conventions: - // ohv1, hv1, hv2: hidden (old) val 1, 2 - // ha, hit: hidden aggregate, iterator - // hn, hp: hidden len, pointer - // hb: hidden bool - // a, v1, v2: not hidden aggregate, val 1, 2 - - t := n.Type - - a := n.Right - lno := int(setlineno(a)) - - var v1 *Node - if n.List != nil { - v1 = n.List.N - } - var v2 *Node - if n.List != nil && n.List.Next != nil && !isblank(n.List.Next.N) { - v2 = n.List.Next.N - } - - // n->list has no meaning anymore, clear it - // to avoid erroneous processing by racewalk. - n.List = nil - - var body *NodeList - var init *NodeList - switch t.Etype { - default: - Fatal("walkrange") - - // Lower n into runtime·memclr if possible, for - // fast zeroing of slices and arrays (issue 5373). - // Look for instances of - // - // for i := range a { - // a[i] = zero - // } - // - // in which the evaluation of a is side-effect-free. - case TARRAY: - if Debug['N'] == 0 { - if flag_race == 0 { - if v1 != nil { - if v2 == nil { - if n.Nbody != nil { - if n.Nbody.N != nil { // at least one statement in body - if n.Nbody.Next == nil { // at most one statement in body - tmp := n.Nbody.N // first statement of body - if tmp.Op == OAS { - if tmp.Left.Op == OINDEX { - if samesafeexpr(tmp.Left.Left, a) { - if samesafeexpr(tmp.Left.Right, v1) { - if t.Type.Width > 0 { - if iszero(tmp.Right) { - // Convert to - // if len(a) != 0 { - // hp = &a[0] - // hn = len(a)*sizeof(elem(a)) - // memclr(hp, hn) - // i = len(a) - 1 - // } - n.Op = OIF - - n.Nbody = nil - n.Ntest = Nod(ONE, Nod(OLEN, a, nil), Nodintconst(0)) - n.Nincr = nil - - // hp = &a[0] - hp := temp(Ptrto(Types[TUINT8])) - - tmp := Nod(OINDEX, a, Nodintconst(0)) - tmp.Bounded = true - tmp = Nod(OADDR, tmp, nil) - tmp = Nod(OCONVNOP, tmp, nil) - tmp.Type = Ptrto(Types[TUINT8]) - n.Nbody = list(n.Nbody, Nod(OAS, hp, tmp)) - - // hn = len(a) * sizeof(elem(a)) - hn := temp(Types[TUINTPTR]) - - tmp = Nod(OLEN, a, nil) - tmp = Nod(OMUL, tmp, Nodintconst(t.Type.Width)) - tmp = conv(tmp, Types[TUINTPTR]) - n.Nbody = list(n.Nbody, Nod(OAS, hn, tmp)) - - // memclr(hp, hn) - fn := mkcall("memclr", nil, nil, hp, hn) - - n.Nbody = list(n.Nbody, fn) - - // i = len(a) - 1 - v1 = Nod(OAS, v1, Nod(OSUB, Nod(OLEN, a, nil), Nodintconst(1))) - - n.Nbody = list(n.Nbody, v1) - - typecheck(&n.Ntest, Erv) - typechecklist(n.Nbody, Etop) - walkstmt(&n) - lineno = int32(lno) - return - } - } - } - } - } - } - } - } - } - } - } - } - } - - // orderstmt arranged for a copy of the array/slice variable if needed. - ha := a - - hv1 := temp(Types[TINT]) - hn := temp(Types[TINT]) - var hp *Node - - init = list(init, Nod(OAS, hv1, nil)) - init = list(init, Nod(OAS, hn, Nod(OLEN, ha, nil))) - if v2 != nil { - hp = temp(Ptrto(n.Type.Type)) - tmp := Nod(OINDEX, ha, Nodintconst(0)) - tmp.Bounded = true - init = list(init, Nod(OAS, hp, Nod(OADDR, tmp, nil))) - } - - n.Ntest = Nod(OLT, hv1, hn) - n.Nincr = Nod(OAS, hv1, Nod(OADD, hv1, Nodintconst(1))) - if v1 == nil { - body = nil - } else if v2 == nil { - body = list1(Nod(OAS, v1, hv1)) - } else { - a := Nod(OAS2, nil, nil) - a.List = list(list1(v1), v2) - a.Rlist = list(list1(hv1), Nod(OIND, hp, nil)) - body = list1(a) - - // Advance pointer as part of increment. - // We used to advance the pointer before executing the loop body, - // but doing so would make the pointer point past the end of the - // array during the final iteration, possibly causing another unrelated - // piece of memory not to be garbage collected until the loop finished. - // Advancing during the increment ensures that the pointer p only points - // pass the end of the array during the final "p++; i++; if(i >= len(x)) break;", - // after which p is dead, so it cannot confuse the collector. - tmp := Nod(OADD, hp, Nodintconst(t.Type.Width)) - - tmp.Type = hp.Type - tmp.Typecheck = 1 - tmp.Right.Type = Types[Tptr] - tmp.Right.Typecheck = 1 - a = Nod(OAS, hp, tmp) - typecheck(&a, Etop) - n.Nincr.Ninit = list1(a) - } - - // orderstmt allocated the iterator for us. - // we only use a once, so no copy needed. - case TMAP: - ha := a - - th := hiter(t) - hit := n.Alloc - hit.Type = th - n.Left = nil - keyname := newname(th.Type.Sym) // depends on layout of iterator struct. See reflect.go:hiter - valname := newname(th.Type.Down.Sym) // ditto - - fn := syslook("mapiterinit", 1) - - substArgTypes(fn, t.Down, t.Type, th) - init = list(init, mkcall1(fn, nil, nil, typename(t), ha, Nod(OADDR, hit, nil))) - n.Ntest = Nod(ONE, Nod(ODOT, hit, keyname), nodnil()) - - fn = syslook("mapiternext", 1) - substArgTypes(fn, th) - n.Nincr = mkcall1(fn, nil, nil, Nod(OADDR, hit, nil)) - - key := Nod(ODOT, hit, keyname) - key = Nod(OIND, key, nil) - if v1 == nil { - body = nil - } else if v2 == nil { - body = list1(Nod(OAS, v1, key)) - } else { - val := Nod(ODOT, hit, valname) - val = Nod(OIND, val, nil) - a := Nod(OAS2, nil, nil) - a.List = list(list1(v1), v2) - a.Rlist = list(list1(key), val) - body = list1(a) - } - - // orderstmt arranged for a copy of the channel variable. - case TCHAN: - ha := a - - n.Ntest = nil - - hv1 := temp(t.Type) - hv1.Typecheck = 1 - if haspointers(t.Type) { - init = list(init, Nod(OAS, hv1, nil)) - } - hb := temp(Types[TBOOL]) - - n.Ntest = Nod(ONE, hb, Nodbool(false)) - a := Nod(OAS2RECV, nil, nil) - a.Typecheck = 1 - a.List = list(list1(hv1), hb) - a.Rlist = list1(Nod(ORECV, ha, nil)) - n.Ntest.Ninit = list1(a) - if v1 == nil { - body = nil - } else { - body = list1(Nod(OAS, v1, hv1)) - } - - // orderstmt arranged for a copy of the string variable. - case TSTRING: - ha := a - - ohv1 := temp(Types[TINT]) - - hv1 := temp(Types[TINT]) - init = list(init, Nod(OAS, hv1, nil)) - - var a *Node - var hv2 *Node - if v2 == nil { - a = Nod(OAS, hv1, mkcall("stringiter", Types[TINT], nil, ha, hv1)) - } else { - hv2 = temp(runetype) - a = Nod(OAS2, nil, nil) - a.List = list(list1(hv1), hv2) - fn := syslook("stringiter2", 0) - a.Rlist = list1(mkcall1(fn, getoutargx(fn.Type), nil, ha, hv1)) - } - - n.Ntest = Nod(ONE, hv1, Nodintconst(0)) - n.Ntest.Ninit = list(list1(Nod(OAS, ohv1, hv1)), a) - - body = nil - if v1 != nil { - body = list1(Nod(OAS, v1, ohv1)) - } - if v2 != nil { - body = list(body, Nod(OAS, v2, hv2)) - } - } - - n.Op = OFOR - typechecklist(init, Etop) - n.Ninit = concat(n.Ninit, init) - typechecklist(n.Ntest.Ninit, Etop) - typecheck(&n.Ntest, Erv) - typecheck(&n.Nincr, Etop) - typechecklist(body, Etop) - n.Nbody = concat(body, n.Nbody) - walkstmt(&n) - - lineno = int32(lno) -} diff --git a/src/cmd/internal/gc/reflect.go b/src/cmd/internal/gc/reflect.go deleted file mode 100644 index 6c0962f258..0000000000 --- a/src/cmd/internal/gc/reflect.go +++ /dev/null @@ -1,1572 +0,0 @@ -// Copyright 2009 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 gc - -import ( - "cmd/internal/gcprog" - "cmd/internal/obj" - "fmt" - "os" -) - -/* - * runtime interface and reflection data structures - */ -var signatlist *NodeList - -func sigcmp(a *Sig, b *Sig) int { - i := stringsCompare(a.name, b.name) - if i != 0 { - return i - } - if a.pkg == b.pkg { - return 0 - } - if a.pkg == nil { - return -1 - } - if b.pkg == nil { - return +1 - } - return stringsCompare(a.pkg.Path, b.pkg.Path) -} - -func lsort(l *Sig, f func(*Sig, *Sig) int) *Sig { - if l == nil || l.link == nil { - return l - } - - l1 := l - l2 := l - for { - l2 = l2.link - if l2 == nil { - break - } - l2 = l2.link - if l2 == nil { - break - } - l1 = l1.link - } - - l2 = l1.link - l1.link = nil - l1 = lsort(l, f) - l2 = lsort(l2, f) - - /* set up lead element */ - if f(l1, l2) < 0 { - l = l1 - l1 = l1.link - } else { - l = l2 - l2 = l2.link - } - - le := l - - for { - if l1 == nil { - for l2 != nil { - le.link = l2 - le = l2 - l2 = l2.link - } - - le.link = nil - break - } - - if l2 == nil { - for l1 != nil { - le.link = l1 - le = l1 - l1 = l1.link - } - - break - } - - if f(l1, l2) < 0 { - le.link = l1 - le = l1 - l1 = l1.link - } else { - le.link = l2 - le = l2 - l2 = l2.link - } - } - - le.link = nil - return l -} - -// Builds a type respresenting a Bucket structure for -// the given map type. This type is not visible to users - -// we include only enough information to generate a correct GC -// program for it. -// Make sure this stays in sync with ../../runtime/hashmap.go! -const ( - BUCKETSIZE = 8 - MAXKEYSIZE = 128 - MAXVALSIZE = 128 -) - -func makefield(name string, t *Type) *Type { - f := typ(TFIELD) - f.Type = t - f.Sym = new(Sym) - f.Sym.Name = name - return f -} - -func mapbucket(t *Type) *Type { - if t.Bucket != nil { - return t.Bucket - } - - bucket := typ(TSTRUCT) - keytype := t.Down - valtype := t.Type - dowidth(keytype) - dowidth(valtype) - if keytype.Width > MAXKEYSIZE { - keytype = Ptrto(keytype) - } - if valtype.Width > MAXVALSIZE { - valtype = Ptrto(valtype) - } - - // The first field is: uint8 topbits[BUCKETSIZE]. - arr := typ(TARRAY) - - arr.Type = Types[TUINT8] - arr.Bound = BUCKETSIZE - var field [4]*Type - field[0] = makefield("topbits", arr) - arr = typ(TARRAY) - arr.Type = keytype - arr.Bound = BUCKETSIZE - field[1] = makefield("keys", arr) - arr = typ(TARRAY) - arr.Type = valtype - arr.Bound = BUCKETSIZE - field[2] = makefield("values", arr) - field[3] = makefield("overflow", Ptrto(bucket)) - - // link up fields - bucket.Noalg = 1 - - bucket.Local = t.Local - bucket.Type = field[0] - for n := int32(0); n < int32(len(field)-1); n++ { - field[n].Down = field[n+1] - } - field[len(field)-1].Down = nil - dowidth(bucket) - - // Pad to the native integer alignment. - // This is usually the same as widthptr; the exception (as usual) is amd64p32. - if Widthreg > Widthptr { - bucket.Width += int64(Widthreg) - int64(Widthptr) - } - - // See comment on hmap.overflow in ../../runtime/hashmap.go. - if !haspointers(t.Type) && !haspointers(t.Down) && t.Type.Width <= MAXKEYSIZE && t.Down.Width <= MAXVALSIZE { - bucket.Haspointers = 1 // no pointers - } - - t.Bucket = bucket - - bucket.Map = t - return bucket -} - -// Builds a type representing a Hmap structure for the given map type. -// Make sure this stays in sync with ../../runtime/hashmap.go! -func hmap(t *Type) *Type { - if t.Hmap != nil { - return t.Hmap - } - - bucket := mapbucket(t) - var field [8]*Type - field[0] = makefield("count", Types[TINT]) - field[1] = makefield("flags", Types[TUINT8]) - field[2] = makefield("B", Types[TUINT8]) - field[3] = makefield("hash0", Types[TUINT32]) - field[4] = makefield("buckets", Ptrto(bucket)) - field[5] = makefield("oldbuckets", Ptrto(bucket)) - field[6] = makefield("nevacuate", Types[TUINTPTR]) - field[7] = makefield("overflow", Types[TUNSAFEPTR]) - - h := typ(TSTRUCT) - h.Noalg = 1 - h.Local = t.Local - h.Type = field[0] - for n := int32(0); n < int32(len(field)-1); n++ { - field[n].Down = field[n+1] - } - field[len(field)-1].Down = nil - dowidth(h) - t.Hmap = h - h.Map = t - return h -} - -func hiter(t *Type) *Type { - if t.Hiter != nil { - return t.Hiter - } - - // build a struct: - // hash_iter { - // key *Key - // val *Value - // t *MapType - // h *Hmap - // buckets *Bucket - // bptr *Bucket - // overflow0 unsafe.Pointer - // overflow1 unsafe.Pointer - // startBucket uintptr - // stuff uintptr - // bucket uintptr - // checkBucket uintptr - // } - // must match ../../runtime/hashmap.go:hash_iter. - var field [12]*Type - field[0] = makefield("key", Ptrto(t.Down)) - - field[1] = makefield("val", Ptrto(t.Type)) - field[2] = makefield("t", Ptrto(Types[TUINT8])) - field[3] = makefield("h", Ptrto(hmap(t))) - field[4] = makefield("buckets", Ptrto(mapbucket(t))) - field[5] = makefield("bptr", Ptrto(mapbucket(t))) - field[6] = makefield("overflow0", Types[TUNSAFEPTR]) - field[7] = makefield("overflow1", Types[TUNSAFEPTR]) - field[8] = makefield("startBucket", Types[TUINTPTR]) - field[9] = makefield("stuff", Types[TUINTPTR]) // offset+wrapped+B+I - field[10] = makefield("bucket", Types[TUINTPTR]) - field[11] = makefield("checkBucket", Types[TUINTPTR]) - - // build iterator struct holding the above fields - i := typ(TSTRUCT) - - i.Noalg = 1 - i.Type = field[0] - for n := int32(0); n < int32(len(field)-1); n++ { - field[n].Down = field[n+1] - } - field[len(field)-1].Down = nil - dowidth(i) - if i.Width != int64(12*Widthptr) { - Yyerror("hash_iter size not correct %d %d", i.Width, 12*Widthptr) - } - t.Hiter = i - i.Map = t - return i -} - -/* - * f is method type, with receiver. - * return function type, receiver as first argument (or not). - */ -func methodfunc(f *Type, receiver *Type) *Type { - var in *NodeList - if receiver != nil { - d := Nod(ODCLFIELD, nil, nil) - d.Type = receiver - in = list(in, d) - } - - var d *Node - for t := getinargx(f).Type; t != nil; t = t.Down { - d = Nod(ODCLFIELD, nil, nil) - d.Type = t.Type - d.Isddd = t.Isddd - in = list(in, d) - } - - var out *NodeList - for t := getoutargx(f).Type; t != nil; t = t.Down { - d = Nod(ODCLFIELD, nil, nil) - d.Type = t.Type - out = list(out, d) - } - - t := functype(nil, in, out) - if f.Nname != nil { - // Link to name of original method function. - t.Nname = f.Nname - } - - return t -} - -/* - * return methods of non-interface type t, sorted by name. - * generates stub functions as needed. - */ -func methods(t *Type) *Sig { - // method type - mt := methtype(t, 0) - - if mt == nil { - return nil - } - expandmeth(mt) - - // type stored in interface word - it := t - - if !isdirectiface(it) { - it = Ptrto(t) - } - - // make list of methods for t, - // generating code if necessary. - var a *Sig - - var this *Type - var b *Sig - var method *Sym - for f := mt.Xmethod; f != nil; f = f.Down { - if f.Etype != TFIELD { - Fatal("methods: not field %v", f) - } - if f.Type.Etype != TFUNC || f.Type.Thistuple == 0 { - Fatal("non-method on %v method %v %v\n", mt, f.Sym, f) - } - if getthisx(f.Type).Type == nil { - Fatal("receiver with no type on %v method %v %v\n", mt, f.Sym, f) - } - if f.Nointerface { - continue - } - - method = f.Sym - if method == nil { - continue - } - - // get receiver type for this particular method. - // if pointer receiver but non-pointer t and - // this is not an embedded pointer inside a struct, - // method does not apply. - this = getthisx(f.Type).Type.Type - - if Isptr[this.Etype] && this.Type == t { - continue - } - if Isptr[this.Etype] && !Isptr[t.Etype] && f.Embedded != 2 && !isifacemethod(f.Type) { - continue - } - - b = new(Sig) - b.link = a - a = b - - a.name = method.Name - if !exportname(method.Name) { - if method.Pkg == nil { - Fatal("methods: missing package") - } - a.pkg = method.Pkg - } - - a.isym = methodsym(method, it, 1) - a.tsym = methodsym(method, t, 0) - a.type_ = methodfunc(f.Type, t) - a.mtype = methodfunc(f.Type, nil) - - if a.isym.Flags&SymSiggen == 0 { - a.isym.Flags |= SymSiggen - if !Eqtype(this, it) || this.Width < Types[Tptr].Width { - compiling_wrappers = 1 - genwrapper(it, f, a.isym, 1) - compiling_wrappers = 0 - } - } - - if a.tsym.Flags&SymSiggen == 0 { - a.tsym.Flags |= SymSiggen - if !Eqtype(this, t) { - compiling_wrappers = 1 - genwrapper(t, f, a.tsym, 0) - compiling_wrappers = 0 - } - } - } - - return lsort(a, sigcmp) -} - -/* - * return methods of interface type t, sorted by name. - */ -func imethods(t *Type) *Sig { - var a *Sig - var method *Sym - var isym *Sym - - var all *Sig - var last *Sig - for f := t.Type; f != nil; f = f.Down { - if f.Etype != TFIELD { - Fatal("imethods: not field") - } - if f.Type.Etype != TFUNC || f.Sym == nil { - continue - } - method = f.Sym - a = new(Sig) - a.name = method.Name - if !exportname(method.Name) { - if method.Pkg == nil { - Fatal("imethods: missing package") - } - a.pkg = method.Pkg - } - - a.mtype = f.Type - a.offset = 0 - a.type_ = methodfunc(f.Type, nil) - - if last != nil && sigcmp(last, a) >= 0 { - Fatal("sigcmp vs sortinter %s %s", last.name, a.name) - } - if last == nil { - all = a - } else { - last.link = a - } - last = a - - // Compiler can only refer to wrappers for non-blank methods. - if isblanksym(method) { - continue - } - - // NOTE(rsc): Perhaps an oversight that - // IfaceType.Method is not in the reflect data. - // Generate the method body, so that compiled - // code can refer to it. - isym = methodsym(method, t, 0) - - if isym.Flags&SymSiggen == 0 { - isym.Flags |= SymSiggen - genwrapper(t, f, isym, 0) - } - } - - return all -} - -var dimportpath_gopkg *Pkg - -func dimportpath(p *Pkg) { - if p.Pathsym != nil { - return - } - - // If we are compiling the runtime package, there are two runtime packages around - // -- localpkg and Runtimepkg. We don't want to produce import path symbols for - // both of them, so just produce one for localpkg. - if myimportpath == "runtime" && p == Runtimepkg { - return - } - - if dimportpath_gopkg == nil { - dimportpath_gopkg = mkpkg("go") - dimportpath_gopkg.Name = "go" - } - - nam := "importpath." + p.Prefix + "." - - n := Nod(ONAME, nil, nil) - n.Sym = Pkglookup(nam, dimportpath_gopkg) - - n.Class = PEXTERN - n.Xoffset = 0 - p.Pathsym = n.Sym - - if p == localpkg { - // Note: myimportpath != "", or else dgopkgpath won't call dimportpath. - gdatastring(n, myimportpath) - } else { - gdatastring(n, p.Path) - } - ggloblsym(n.Sym, int32(Types[TSTRING].Width), obj.DUPOK|obj.RODATA) -} - -func dgopkgpath(s *Sym, ot int, pkg *Pkg) int { - if pkg == nil { - return dgostringptr(s, ot, "") - } - - if pkg == localpkg && myimportpath == "" { - // If we don't know the full path of the package being compiled (i.e. -p - // was not passed on the compiler command line), emit reference to - // go.importpath.""., which 6l will rewrite using the correct import path. - // Every package that imports this one directly defines the symbol. - var ns *Sym - - if ns == nil { - ns = Pkglookup("importpath.\"\".", mkpkg("go")) - } - return dsymptr(s, ot, ns, 0) - } - - dimportpath(pkg) - return dsymptr(s, ot, pkg.Pathsym, 0) -} - -/* - * uncommonType - * ../../runtime/type.go:/uncommonType - */ -func dextratype(sym *Sym, off int, t *Type, ptroff int) int { - m := methods(t) - if t.Sym == nil && m == nil { - return off - } - - // fill in *extraType pointer in header - off = int(Rnd(int64(off), int64(Widthptr))) - - dsymptr(sym, ptroff, sym, off) - - n := 0 - for a := m; a != nil; a = a.link { - dtypesym(a.type_) - n++ - } - - ot := off - s := sym - if t.Sym != nil { - ot = dgostringptr(s, ot, t.Sym.Name) - if t != Types[t.Etype] && t != errortype { - ot = dgopkgpath(s, ot, t.Sym.Pkg) - } else { - ot = dgostringptr(s, ot, "") - } - } else { - ot = dgostringptr(s, ot, "") - ot = dgostringptr(s, ot, "") - } - - // slice header - ot = dsymptr(s, ot, s, ot+Widthptr+2*Widthint) - - ot = duintxx(s, ot, uint64(n), Widthint) - ot = duintxx(s, ot, uint64(n), Widthint) - - // methods - for a := m; a != nil; a = a.link { - // method - // ../../runtime/type.go:/method - ot = dgostringptr(s, ot, a.name) - - ot = dgopkgpath(s, ot, a.pkg) - ot = dsymptr(s, ot, dtypesym(a.mtype), 0) - ot = dsymptr(s, ot, dtypesym(a.type_), 0) - if a.isym != nil { - ot = dsymptr(s, ot, a.isym, 0) - } else { - ot = duintptr(s, ot, 0) - } - if a.tsym != nil { - ot = dsymptr(s, ot, a.tsym, 0) - } else { - ot = duintptr(s, ot, 0) - } - } - - return ot -} - -var kinds = []int{ - TINT: obj.KindInt, - TUINT: obj.KindUint, - TINT8: obj.KindInt8, - TUINT8: obj.KindUint8, - TINT16: obj.KindInt16, - TUINT16: obj.KindUint16, - TINT32: obj.KindInt32, - TUINT32: obj.KindUint32, - TINT64: obj.KindInt64, - TUINT64: obj.KindUint64, - TUINTPTR: obj.KindUintptr, - TFLOAT32: obj.KindFloat32, - TFLOAT64: obj.KindFloat64, - TBOOL: obj.KindBool, - TSTRING: obj.KindString, - TPTR32: obj.KindPtr, - TPTR64: obj.KindPtr, - TSTRUCT: obj.KindStruct, - TINTER: obj.KindInterface, - TCHAN: obj.KindChan, - TMAP: obj.KindMap, - TARRAY: obj.KindArray, - TFUNC: obj.KindFunc, - TCOMPLEX64: obj.KindComplex64, - TCOMPLEX128: obj.KindComplex128, - TUNSAFEPTR: obj.KindUnsafePointer, -} - -func haspointers(t *Type) bool { - if t.Haspointers != 0 { - return t.Haspointers-1 != 0 - } - - var ret bool - switch t.Etype { - case TINT, - TUINT, - TINT8, - TUINT8, - TINT16, - TUINT16, - TINT32, - TUINT32, - TINT64, - TUINT64, - TUINTPTR, - TFLOAT32, - TFLOAT64, - TCOMPLEX64, - TCOMPLEX128, - TBOOL: - ret = false - - case TARRAY: - if t.Bound < 0 { // slice - ret = true - break - } - - if t.Bound == 0 { // empty array - ret = false - break - } - - ret = haspointers(t.Type) - - case TSTRUCT: - ret = false - for t1 := t.Type; t1 != nil; t1 = t1.Down { - if haspointers(t1.Type) { - ret = true - break - } - } - - case TSTRING, - TPTR32, - TPTR64, - TUNSAFEPTR, - TINTER, - TCHAN, - TMAP, - TFUNC: - fallthrough - default: - ret = true - - case TFIELD: - Fatal("haspointers: unexpected type, %v", t) - } - - t.Haspointers = 1 + uint8(obj.Bool2int(ret)) - return ret -} - -// 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) int64 { - if !haspointers(t) { - return 0 - } - - switch t.Etype { - case TPTR32, - TPTR64, - TUNSAFEPTR, - TFUNC, - TCHAN, - TMAP: - return int64(Widthptr) - - case TSTRING: - // struct { byte *str; intgo len; } - return int64(Widthptr) - - case TINTER: - // struct { Itab *tab; void *data; } or - // struct { Type *type; void *data; } - return 2 * int64(Widthptr) - - case TARRAY: - if Isslice(t) { - // struct { byte *array; uintgo len; uintgo cap; } - return int64(Widthptr) - } - // haspointers already eliminated t.Bound == 0. - return (t.Bound-1)*t.Type.Width + typeptrdata(t.Type) - - case TSTRUCT: - // Find the last field that has pointers. - var lastPtrField *Type - for t1 := t.Type; t1 != nil; t1 = t1.Down { - if haspointers(t1.Type) { - lastPtrField = t1 - } - } - return lastPtrField.Width + typeptrdata(lastPtrField.Type) - - default: - Fatal("typeptrdata: unexpected type, %v", t) - return 0 - } -} - -/* - * commonType - * ../../runtime/type.go:/commonType - */ - -var dcommontype_algarray *Sym - -func dcommontype(s *Sym, ot int, t *Type) int { - if ot != 0 { - Fatal("dcommontype %d", ot) - } - - sizeofAlg := 2 * Widthptr - if dcommontype_algarray == nil { - dcommontype_algarray = Pkglookup("algarray", Runtimepkg) - } - dowidth(t) - alg := algtype(t) - var algsym *Sym - if alg < 0 || alg == AMEM { - algsym = dalgsym(t) - } - - var sptr *Sym - if t.Sym != nil && !Isptr[t.Etype] { - sptr = dtypesym(Ptrto(t)) - } else { - sptr = weaktypesym(Ptrto(t)) - } - - // All (non-reflect-allocated) Types share the same zero object. - // Each place in the compiler where a pointer to the zero object - // might be returned by a runtime call (map access return value, - // 2-arg type cast) declares the size of the zerovalue it needs. - // 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 - // a read-only bss, but I don't think such a thing exists. - - // ../../pkg/reflect/type.go:/^type.commonType - // actual type structure - // type commonType struct { - // size uintptr - // ptrsize uintptr - // hash uint32 - // _ uint8 - // align uint8 - // fieldAlign uint8 - // kind uint8 - // alg 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(ptrdata)) - - ot = duint32(s, ot, typehash(t)) - ot = duint8(s, ot, 0) // unused - - // runtime (and common sense) expects alignment to be a power of two. - i := int(t.Align) - - if i == 0 { - i = 1 - } - if i&(i-1) != 0 { - Fatal("invalid alignment %d for %v", t.Align, t) - } - ot = duint8(s, ot, t.Align) // align - ot = duint8(s, ot, t.Align) // fieldAlign - - i = kinds[t.Etype] - if t.Etype == TARRAY && t.Bound < 0 { - i = obj.KindSlice - } - if !haspointers(t) { - i |= obj.KindNoPointers - } - if isdirectiface(t) { - i |= obj.KindDirectIface - } - if useGCProg { - i |= obj.KindGCProg - } - ot = duint8(s, ot, uint8(i)) // kind - if algsym == nil { - ot = dsymptr(s, ot, dcommontype_algarray, alg*sizeofAlg) - } else { - ot = dsymptr(s, ot, algsym, 0) - } - ot = dsymptr(s, ot, gcsym, 0) - - p := Tconv(t, obj.FmtLeft|obj.FmtUnsigned) - - //print("dcommontype: %s\n", p); - ot = dgostringptr(s, ot, p) // string - - // skip pointer to extraType, - // which follows the rest of this type structure. - // caller will fill in if needed. - // otherwise linker will assume 0. - ot += Widthptr - - ot = dsymptr(s, ot, sptr, 0) // ptrto type - ot = dsymptr(s, ot, zero, 0) // ptr to zero value - return ot -} - -func typesym(t *Type) *Sym { - return Pkglookup(Tconv(t, obj.FmtLeft), typepkg) -} - -func tracksym(t *Type) *Sym { - return Pkglookup(Tconv(t.Outer, obj.FmtLeft)+"."+t.Sym.Name, trackpkg) -} - -func typelinksym(t *Type) *Sym { - // %-uT is what the generated Type's string field says. - // It uses (ambiguous) package names instead of import paths. - // %-T is the complete, unambiguous type name. - // We want the types to end up sorted by string field, - // so use that first in the name, and then add :%-T to - // disambiguate. We use a tab character as the separator to - // ensure the types appear sorted by their string field. The - // names are a little long but they are discarded by the linker - // and do not end up in the symbol table of the final binary. - p := Tconv(t, obj.FmtLeft|obj.FmtUnsigned) + "\t" + Tconv(t, obj.FmtLeft) - - s := Pkglookup(p, typelinkpkg) - - //print("typelinksym: %s -> %+S\n", p, s); - - return s -} - -func typesymprefix(prefix string, t *Type) *Sym { - p := prefix + "." + Tconv(t, obj.FmtLeft) - s := Pkglookup(p, typepkg) - - //print("algsym: %s -> %+S\n", p, s); - - return s -} - -func typenamesym(t *Type) *Sym { - if t == nil || (Isptr[t.Etype] && t.Type == nil) || isideal(t) { - Fatal("typename %v", t) - } - s := typesym(t) - if s.Def == nil { - n := Nod(ONAME, nil, nil) - n.Sym = s - n.Type = Types[TUINT8] - n.Addable = true - n.Ullman = 1 - n.Class = PEXTERN - n.Xoffset = 0 - n.Typecheck = 1 - s.Def = n - - signatlist = list(signatlist, typenod(t)) - } - - return s.Def.Sym -} - -func typename(t *Type) *Node { - s := typenamesym(t) - n := Nod(OADDR, s.Def, nil) - n.Type = Ptrto(s.Def.Type) - n.Addable = true - n.Ullman = 2 - n.Typecheck = 1 - return n -} - -func weaktypesym(t *Type) *Sym { - p := Tconv(t, obj.FmtLeft) - s := Pkglookup(p, weaktypepkg) - - //print("weaktypesym: %s -> %+S\n", p, s); - - return s -} - -/* - * Returns 1 if t has a reflexive equality operator. - * That is, if x==x for all x of type t. - */ -func isreflexive(t *Type) bool { - switch t.Etype { - case TBOOL, - TINT, - TUINT, - TINT8, - TUINT8, - TINT16, - TUINT16, - TINT32, - TUINT32, - TINT64, - TUINT64, - TUINTPTR, - TPTR32, - TPTR64, - TUNSAFEPTR, - TSTRING, - TCHAN: - return true - - case TFLOAT32, - TFLOAT64, - TCOMPLEX64, - TCOMPLEX128, - TINTER: - return false - - case TARRAY: - if Isslice(t) { - Fatal("slice can't be a map key: %v", t) - } - return isreflexive(t.Type) - - case TSTRUCT: - for t1 := t.Type; t1 != nil; t1 = t1.Down { - if !isreflexive(t1.Type) { - return false - } - } - - return true - - default: - Fatal("bad type for map key: %v", t) - return false - } -} - -func dtypesym(t *Type) *Sym { - // Replace byte, rune aliases with real type. - // They've been separate internally to make error messages - // better, but we have to merge them in the reflect tables. - if t == bytetype || t == runetype { - t = Types[t.Etype] - } - - if isideal(t) { - Fatal("dtypesym %v", t) - } - - s := typesym(t) - if s.Flags&SymSiggen != 0 { - return s - } - s.Flags |= SymSiggen - - // special case (look for runtime below): - // when compiling package runtime, - // emit the type structures for int, float, etc. - tbase := t - - if Isptr[t.Etype] && t.Sym == nil && t.Type.Sym != nil { - tbase = t.Type - } - dupok := 0 - if tbase.Sym == nil { - dupok = obj.DUPOK - } - - if compiling_runtime != 0 && (tbase == Types[tbase.Etype] || tbase == bytetype || tbase == runetype || tbase == errortype) { // int, float, etc - goto ok - } - - // named types from other files are defined only by those files - if tbase.Sym != nil && !tbase.Local { - return s - } - if isforw[tbase.Etype] { - return s - } - -ok: - ot := 0 - xt := 0 - switch t.Etype { - default: - ot = dcommontype(s, ot, t) - xt = ot - 3*Widthptr - - case TARRAY: - if t.Bound >= 0 { - // ../../runtime/type.go:/ArrayType - s1 := dtypesym(t.Type) - - t2 := typ(TARRAY) - t2.Type = t.Type - t2.Bound = -1 // slice - s2 := dtypesym(t2) - ot = dcommontype(s, ot, t) - xt = ot - 3*Widthptr - ot = dsymptr(s, ot, s1, 0) - ot = dsymptr(s, ot, s2, 0) - ot = duintptr(s, ot, uint64(t.Bound)) - } else { - // ../../runtime/type.go:/SliceType - s1 := dtypesym(t.Type) - - ot = dcommontype(s, ot, t) - xt = ot - 3*Widthptr - ot = dsymptr(s, ot, s1, 0) - } - - // ../../runtime/type.go:/ChanType - case TCHAN: - s1 := dtypesym(t.Type) - - ot = dcommontype(s, ot, t) - xt = ot - 3*Widthptr - ot = dsymptr(s, ot, s1, 0) - ot = duintptr(s, ot, uint64(t.Chan)) - - case TFUNC: - for t1 := getthisx(t).Type; t1 != nil; t1 = t1.Down { - dtypesym(t1.Type) - } - isddd := false - for t1 := getinargx(t).Type; t1 != nil; t1 = t1.Down { - isddd = t1.Isddd - dtypesym(t1.Type) - } - - for t1 := getoutargx(t).Type; t1 != nil; t1 = t1.Down { - dtypesym(t1.Type) - } - - ot = dcommontype(s, ot, t) - xt = ot - 3*Widthptr - ot = duint8(s, ot, uint8(obj.Bool2int(isddd))) - - // two slice headers: in and out. - ot = int(Rnd(int64(ot), int64(Widthptr))) - - ot = dsymptr(s, ot, s, ot+2*(Widthptr+2*Widthint)) - n := t.Thistuple + t.Intuple - ot = duintxx(s, ot, uint64(n), Widthint) - ot = duintxx(s, ot, uint64(n), Widthint) - ot = dsymptr(s, ot, s, ot+1*(Widthptr+2*Widthint)+n*Widthptr) - ot = duintxx(s, ot, uint64(t.Outtuple), Widthint) - ot = duintxx(s, ot, uint64(t.Outtuple), Widthint) - - // slice data - for t1 := getthisx(t).Type; t1 != nil; t1 = t1.Down { - ot = dsymptr(s, ot, dtypesym(t1.Type), 0) - n++ - } - for t1 := getinargx(t).Type; t1 != nil; t1 = t1.Down { - ot = dsymptr(s, ot, dtypesym(t1.Type), 0) - n++ - } - for t1 := getoutargx(t).Type; t1 != nil; t1 = t1.Down { - ot = dsymptr(s, ot, dtypesym(t1.Type), 0) - n++ - } - - case TINTER: - m := imethods(t) - n := 0 - for a := m; a != nil; a = a.link { - dtypesym(a.type_) - n++ - } - - // ../../runtime/type.go:/InterfaceType - ot = dcommontype(s, ot, t) - - xt = ot - 3*Widthptr - ot = dsymptr(s, ot, s, ot+Widthptr+2*Widthint) - ot = duintxx(s, ot, uint64(n), Widthint) - ot = duintxx(s, ot, uint64(n), Widthint) - for a := m; a != nil; a = a.link { - // ../../runtime/type.go:/imethod - ot = dgostringptr(s, ot, a.name) - - ot = dgopkgpath(s, ot, a.pkg) - ot = dsymptr(s, ot, dtypesym(a.type_), 0) - } - - // ../../runtime/type.go:/MapType - case TMAP: - s1 := dtypesym(t.Down) - - s2 := dtypesym(t.Type) - s3 := dtypesym(mapbucket(t)) - s4 := dtypesym(hmap(t)) - ot = dcommontype(s, ot, t) - xt = ot - 3*Widthptr - ot = dsymptr(s, ot, s1, 0) - ot = dsymptr(s, ot, s2, 0) - ot = dsymptr(s, ot, s3, 0) - ot = dsymptr(s, ot, s4, 0) - if t.Down.Width > MAXKEYSIZE { - ot = duint8(s, ot, uint8(Widthptr)) - ot = duint8(s, ot, 1) // indirect - } else { - ot = duint8(s, ot, uint8(t.Down.Width)) - ot = duint8(s, ot, 0) // not indirect - } - - if t.Type.Width > MAXVALSIZE { - ot = duint8(s, ot, uint8(Widthptr)) - ot = duint8(s, ot, 1) // indirect - } else { - ot = duint8(s, ot, uint8(t.Type.Width)) - ot = duint8(s, ot, 0) // not indirect - } - - ot = duint16(s, ot, uint16(mapbucket(t).Width)) - ot = duint8(s, ot, uint8(obj.Bool2int(isreflexive(t.Down)))) - - case TPTR32, TPTR64: - if t.Type.Etype == TANY { - // ../../runtime/type.go:/UnsafePointerType - ot = dcommontype(s, ot, t) - - break - } - - // ../../runtime/type.go:/PtrType - s1 := dtypesym(t.Type) - - ot = dcommontype(s, ot, t) - xt = ot - 3*Widthptr - ot = dsymptr(s, ot, s1, 0) - - // ../../runtime/type.go:/StructType - // for security, only the exported fields. - case TSTRUCT: - n := 0 - - for t1 := t.Type; t1 != nil; t1 = t1.Down { - dtypesym(t1.Type) - n++ - } - - ot = dcommontype(s, ot, t) - xt = ot - 3*Widthptr - ot = dsymptr(s, ot, s, ot+Widthptr+2*Widthint) - ot = duintxx(s, ot, uint64(n), Widthint) - ot = duintxx(s, ot, uint64(n), Widthint) - for t1 := t.Type; t1 != nil; t1 = t1.Down { - // ../../runtime/type.go:/structField - if t1.Sym != nil && t1.Embedded == 0 { - ot = dgostringptr(s, ot, t1.Sym.Name) - if exportname(t1.Sym.Name) { - ot = dgostringptr(s, ot, "") - } else { - ot = dgopkgpath(s, ot, t1.Sym.Pkg) - } - } else { - ot = dgostringptr(s, ot, "") - if t1.Type.Sym != nil && t1.Type.Sym.Pkg == builtinpkg { - ot = dgopkgpath(s, ot, localpkg) - } else { - ot = dgostringptr(s, ot, "") - } - } - - ot = dsymptr(s, ot, dtypesym(t1.Type), 0) - ot = dgostrlitptr(s, ot, t1.Note) - ot = duintptr(s, ot, uint64(t1.Width)) // field offset - } - } - - ot = dextratype(s, ot, t, xt) - ggloblsym(s, int32(ot), int16(dupok|obj.RODATA)) - - // generate typelink.foo pointing at s = type.foo. - // The linker will leave a table of all the typelinks for - // types in the binary, so reflect can find them. - // We only need the link for unnamed composites that - // we want be able to find. - if t.Sym == nil { - switch t.Etype { - case TPTR32, TPTR64: - // The ptrto field of the type data cannot be relied on when - // dynamic linking: a type T may be defined in a module that makes - // no use of pointers to that type, but another module can contain - // a package that imports the first one and does use *T pointers. - // The second module will end up defining type data for *T and a - // type.*T symbol pointing at it. It's important that calling - // .PtrTo() on the refect.Type for T returns this type data and - // not some synthesized object, so we need reflect to be able to - // find it! - if !Ctxt.Flag_dynlink { - break - } - fallthrough - case TARRAY, TCHAN, TFUNC, TMAP: - slink := typelinksym(t) - dsymptr(slink, 0, s, 0) - ggloblsym(slink, int32(Widthptr), int16(dupok|obj.RODATA)) - } - } - - return s -} - -func dumptypestructs() { - var n *Node - - // copy types from externdcl list to signatlist - for l := externdcl; l != nil; l = l.Next { - n = l.N - if n.Op != OTYPE { - continue - } - signatlist = list(signatlist, n) - } - - // process signatlist - var t *Type - for l := signatlist; l != nil; l = l.Next { - n = l.N - if n.Op != OTYPE { - continue - } - t = n.Type - dtypesym(t) - if t.Sym != nil { - dtypesym(Ptrto(t)) - } - } - - // generate import strings for imported packages - for _, p := range pkgs { - if p.Direct != 0 { - dimportpath(p) - } - } - - // do basic types if compiling package runtime. - // they have to be in at least one package, - // and runtime is always loaded implicitly, - // so this is as good as any. - // another possible choice would be package main, - // but using runtime means fewer copies in .6 files. - if compiling_runtime != 0 { - for i := 1; i <= TBOOL; i++ { - dtypesym(Ptrto(Types[i])) - } - dtypesym(Ptrto(Types[TSTRING])) - dtypesym(Ptrto(Types[TUNSAFEPTR])) - - // emit type structs for error and func(error) string. - // The latter is the type of an auto-generated wrapper. - dtypesym(Ptrto(errortype)) - - dtypesym(functype(nil, list1(Nod(ODCLFIELD, nil, typenod(errortype))), list1(Nod(ODCLFIELD, nil, typenod(Types[TSTRING]))))) - - // add paths for runtime and main, which 6l imports implicitly. - dimportpath(Runtimepkg) - - if flag_race != 0 { - dimportpath(racepkg) - } - dimportpath(mkpkg("main")) - } -} - -func dalgsym(t *Type) *Sym { - var s *Sym - var hashfunc *Sym - var eqfunc *Sym - - // dalgsym is only called for a type that needs an algorithm table, - // which implies that the type is comparable (or else it would use ANOEQ). - - if algtype(t) == AMEM { - // we use one algorithm table for all AMEM types of a given size - p := fmt.Sprintf(".alg%d", t.Width) - - s = Pkglookup(p, typepkg) - - if s.Flags&SymAlgGen != 0 { - return s - } - s.Flags |= SymAlgGen - - // make hash closure - p = fmt.Sprintf(".hashfunc%d", t.Width) - - hashfunc = Pkglookup(p, typepkg) - - ot := 0 - ot = dsymptr(hashfunc, ot, Pkglookup("memhash_varlen", Runtimepkg), 0) - ot = duintxx(hashfunc, ot, uint64(t.Width), Widthptr) // size encoded in closure - ggloblsym(hashfunc, int32(ot), obj.DUPOK|obj.RODATA) - - // make equality closure - p = fmt.Sprintf(".eqfunc%d", t.Width) - - eqfunc = Pkglookup(p, typepkg) - - ot = 0 - ot = dsymptr(eqfunc, ot, Pkglookup("memequal_varlen", Runtimepkg), 0) - ot = duintxx(eqfunc, ot, uint64(t.Width), Widthptr) - ggloblsym(eqfunc, int32(ot), obj.DUPOK|obj.RODATA) - } else { - // generate an alg table specific to this type - s = typesymprefix(".alg", t) - - hash := typesymprefix(".hash", t) - eq := typesymprefix(".eq", t) - hashfunc = typesymprefix(".hashfunc", t) - eqfunc = typesymprefix(".eqfunc", t) - - genhash(hash, t) - geneq(eq, t) - - // make Go funcs (closures) for calling hash and equal from Go - dsymptr(hashfunc, 0, hash, 0) - - ggloblsym(hashfunc, int32(Widthptr), obj.DUPOK|obj.RODATA) - dsymptr(eqfunc, 0, eq, 0) - ggloblsym(eqfunc, int32(Widthptr), obj.DUPOK|obj.RODATA) - } - - // ../../runtime/alg.go:/typeAlg - ot := 0 - - ot = dsymptr(s, ot, hashfunc, 0) - ot = dsymptr(s, ot, eqfunc, 0) - ggloblsym(s, int32(ot), obj.DUPOK|obj.RODATA) - return s -} - -// 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 - } - - useGCProg = true - sym, ptrdata = dgcprog(t) - return -} - -// 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(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 { - ptrmask[i/8] |= 1 << (uint(i) % 8) - } - } -} - -// 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 -} - -type GCProg struct { - sym *Sym - symoff int - w gcprog.Writer -} - -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) - } -} - -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 - } - if t.Width == int64(Widthptr) { - p.w.Ptr(offset / int64(Widthptr)) - return - } - switch t.Etype { - default: - Fatal("GCProg.emit: unexpected type %v", t) - - case TSTRING: - p.w.Ptr(offset / int64(Widthptr)) - - case TINTER: - p.w.Ptr(offset / int64(Widthptr)) - p.w.Ptr(offset/int64(Widthptr) + 1) - - case TARRAY: - if Isslice(t) { - 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: - for t1 := t.Type; t1 != nil; t1 = t1.Down { - p.emit(t1.Type, offset+t1.Width) - } - } -} diff --git a/src/cmd/internal/gc/reg.go b/src/cmd/internal/gc/reg.go deleted file mode 100644 index afe95230c7..0000000000 --- a/src/cmd/internal/gc/reg.go +++ /dev/null @@ -1,1559 +0,0 @@ -// Derived from Inferno utils/6c/reg.c -// http://code.google.com/p/inferno-os/source/browse/utils/6c/reg.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package gc - -import ( - "bytes" - "cmd/internal/obj" - "fmt" - "sort" - "strings" -) - -// A Var represents a single variable that may be stored in a register. -// That variable may itself correspond to a hardware register, -// to represent the use of registers in the unoptimized instruction stream. -type Var struct { - offset int64 - node *Node - nextinnode *Var - width int - id int // index in vars - name int8 - etype int8 - addr int8 -} - -// Bits represents a set of Vars, stored as a bit set of var numbers -// (the index in vars, or equivalently v.id). -type Bits struct { - b [BITS]uint64 -} - -const ( - BITS = 3 - NVAR = BITS * 64 -) - -var ( - vars [NVAR]Var // variables under consideration - nvar int // number of vars - - regbits uint64 // bits for hardware registers - - zbits Bits // zero - externs Bits // global variables - params Bits // function parameters and results - ivar Bits // function parameters (inputs) - ovar Bits // function results (outputs) - consts Bits // constant values - addrs Bits // variables with address taken -) - -// A Reg is a wrapper around a single Prog (one instruction) that holds -// register optimization information while the optimizer runs. -// r->prog is the instruction. -type Reg struct { - set Bits // regopt variables written by this instruction. - use1 Bits // regopt variables read by prog->from. - use2 Bits // regopt variables read by prog->to. - - // refahead/refbehind are the regopt variables whose current - // value may be used in the following/preceding instructions - // up to a CALL (or the value is clobbered). - refbehind Bits - refahead Bits - - // calahead/calbehind are similar, but for variables in - // instructions that are reachable after hitting at least one - // CALL. - calbehind Bits - calahead Bits - - regdiff Bits - act Bits - regu uint64 // register used bitmap -} - -// A Rgn represents a single regopt variable over a region of code -// where a register could potentially be dedicated to that variable. -// The code encompassed by a Rgn is defined by the flow graph, -// starting at enter, flood-filling forward while varno is refahead -// and backward while varno is refbehind, and following branches. -// A single variable may be represented by multiple disjoint Rgns and -// each Rgn may choose a different register for that variable. -// Registers are allocated to regions greedily in order of descending -// cost. -type Rgn struct { - enter *Flow - cost int16 - varno int16 - regno int16 -} - -// The Plan 9 C compilers used a limit of 600 regions, -// but the yacc-generated parser in y.go has 3100 regions. -// We set MaxRgn large enough to handle that. -// There's not a huge cost to having too many regions: -// the main processing traces the live area for each variable, -// which is limited by the number of variables times the area, -// not the raw region count. If there are many regions, they -// are almost certainly small and easy to trace. -// The only operation that scales with region count is the -// sorting by cost, which uses sort.Sort and is therefore -// guaranteed n log n. -const MaxRgn = 6000 - -var ( - region []Rgn - nregion int -) - -type rcmp []Rgn - -func (x rcmp) Len() int { - return len(x) -} - -func (x rcmp) Swap(i, j int) { - x[i], x[j] = x[j], x[i] -} - -func (x rcmp) Less(i, j int) bool { - p1 := &x[i] - p2 := &x[j] - if p1.cost != p2.cost { - return int(p2.cost)-int(p1.cost) < 0 - } - if p1.varno != p2.varno { - return int(p2.varno)-int(p1.varno) < 0 - } - if p1.enter != p2.enter { - return int(p2.enter.Id-p1.enter.Id) < 0 - } - return false -} - -func setaddrs(bit Bits) { - var i int - var n int - var v *Var - var node *Node - - for bany(&bit) { - // convert each bit to a variable - i = bnum(bit) - - node = vars[i].node - n = int(vars[i].name) - biclr(&bit, uint(i)) - - // disable all pieces of that variable - for i = 0; i < nvar; i++ { - v = &vars[i] - if v.node == node && int(v.name) == n { - v.addr = 2 - } - } - } -} - -var regnodes [64]*Node - -func walkvardef(n *Node, f *Flow, active int) { - var f1 *Flow - var bn int - var v *Var - - for f1 = f; f1 != nil; f1 = f1.S1 { - if f1.Active == int32(active) { - break - } - f1.Active = int32(active) - if f1.Prog.As == obj.AVARKILL && f1.Prog.To.Node == n { - break - } - for v, _ = n.Opt.(*Var); v != nil; v = v.nextinnode { - bn = v.id - biset(&(f1.Data.(*Reg)).act, uint(bn)) - } - - if f1.Prog.As == obj.ACALL { - break - } - } - - for f2 := f; f2 != f1; f2 = f2.S1 { - if f2.S2 != nil { - walkvardef(n, f2.S2, active) - } - } -} - -/* - * add mov b,rn - * just after r - */ -func addmove(r *Flow, bn int, rn int, f int) { - p1 := Ctxt.NewProg() - Clearp(p1) - p1.Pc = 9999 - - p := r.Prog - p1.Link = p.Link - p.Link = p1 - p1.Lineno = p.Lineno - - v := &vars[bn] - - a := &p1.To - a.Offset = v.offset - a.Etype = uint8(v.etype) - a.Type = obj.TYPE_MEM - a.Name = v.name - a.Node = v.node - a.Sym = Linksym(v.node.Sym) - - /* NOTE(rsc): 9g did - if(a->etype == TARRAY) - a->type = TYPE_ADDR; - else if(a->sym == nil) - a->type = TYPE_CONST; - */ - p1.As = int16(Thearch.Optoas(OAS, Types[uint8(v.etype)])) - - // TODO(rsc): Remove special case here. - if (Thearch.Thechar == '5' || Thearch.Thechar == '7' || Thearch.Thechar == '9') && v.etype == TBOOL { - p1.As = int16(Thearch.Optoas(OAS, Types[TUINT8])) - } - p1.From.Type = obj.TYPE_REG - p1.From.Reg = int16(rn) - p1.From.Name = obj.NAME_NONE - if f == 0 { - p1.From = *a - *a = obj.Addr{} - a.Type = obj.TYPE_REG - a.Reg = int16(rn) - } - - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf("%v ===add=== %v\n", p, p1) - } - Ostats.Nspill++ -} - -func overlap_reg(o1 int64, w1 int, o2 int64, w2 int) bool { - t1 := o1 + int64(w1) - t2 := o2 + int64(w2) - - if t1 <= o2 || t2 <= o1 { - return false - } - - return true -} - -func mkvar(f *Flow, a *obj.Addr) Bits { - /* - * mark registers used - */ - if a.Type == obj.TYPE_NONE { - return zbits - } - - r := f.Data.(*Reg) - r.use1.b[0] |= Thearch.Doregbits(int(a.Index)) // TODO: Use RtoB - - var n int - switch a.Type { - default: - regu := Thearch.Doregbits(int(a.Reg)) | Thearch.RtoB(int(a.Reg)) // TODO: Use RtoB - if regu == 0 { - return zbits - } - bit := zbits - bit.b[0] = regu - return bit - - // TODO(rsc): Remove special case here. - case obj.TYPE_ADDR: - var bit Bits - if Thearch.Thechar == '5' || Thearch.Thechar == '7' || Thearch.Thechar == '9' { - goto memcase - } - a.Type = obj.TYPE_MEM - bit = mkvar(f, a) - setaddrs(bit) - a.Type = obj.TYPE_ADDR - Ostats.Naddr++ - return zbits - - memcase: - fallthrough - - case obj.TYPE_MEM: - if r != nil { - r.use1.b[0] |= Thearch.RtoB(int(a.Reg)) - } - - /* NOTE: 5g did - if(r->f.prog->scond & (C_PBIT|C_WBIT)) - r->set.b[0] |= RtoB(a->reg); - */ - switch a.Name { - default: - // Note: This case handles NAME_EXTERN and NAME_STATIC. - // We treat these as requiring eager writes to memory, due to - // the possibility of a fault handler looking at them, so there is - // not much point in registerizing the loads. - // If we later choose the set of candidate variables from a - // larger list, these cases could be deprioritized instead of - // removed entirely. - return zbits - - case obj.NAME_PARAM, - obj.NAME_AUTO: - n = int(a.Name) - } - } - - node, _ := a.Node.(*Node) - if node == nil || node.Op != ONAME || node.Orig == nil { - return zbits - } - node = node.Orig - if node.Orig != node { - Fatal("%v: bad node", Ctxt.Dconv(a)) - } - if node.Sym == nil || node.Sym.Name[0] == '.' { - return zbits - } - et := int(a.Etype) - o := a.Offset - w := a.Width - if w < 0 { - Fatal("bad width %d for %v", w, Ctxt.Dconv(a)) - } - - flag := 0 - var v *Var - for i := 0; i < nvar; i++ { - v = &vars[i] - if v.node == node && int(v.name) == n { - if v.offset == o { - if int(v.etype) == et { - if int64(v.width) == w { - // TODO(rsc): Remove special case for arm here. - if flag == 0 || Thearch.Thechar != '5' { - return blsh(uint(i)) - } - } - } - } - - // if they overlap, disable both - if overlap_reg(v.offset, v.width, o, int(w)) { - // print("disable overlap %s %d %d %d %d, %E != %E\n", s->name, v->offset, v->width, o, w, v->etype, et); - v.addr = 1 - - flag = 1 - } - } - } - - switch et { - case 0, TFUNC: - return zbits - } - - if nvar >= NVAR { - if Debug['w'] > 1 && node != nil { - Fatal("variable not optimized: %v", Nconv(node, obj.FmtSharp)) - } - if Debug['v'] > 0 { - Warn("variable not optimized: %v", Nconv(node, obj.FmtSharp)) - } - - // If we're not tracking a word in a variable, mark the rest as - // having its address taken, so that we keep the whole thing - // live at all calls. otherwise we might optimize away part of - // a variable but not all of it. - var v *Var - for i := 0; i < nvar; i++ { - v = &vars[i] - if v.node == node { - v.addr = 1 - } - } - - return zbits - } - - i := nvar - nvar++ - v = &vars[i] - v.id = i - v.offset = o - v.name = int8(n) - v.etype = int8(et) - v.width = int(w) - v.addr = int8(flag) // funny punning - v.node = node - - // node->opt is the head of a linked list - // of Vars within the given Node, so that - // we can start at a Var and find all the other - // Vars in the same Go variable. - v.nextinnode, _ = node.Opt.(*Var) - - node.Opt = v - - bit := blsh(uint(i)) - if n == obj.NAME_EXTERN || n == obj.NAME_STATIC { - for z := 0; z < BITS; z++ { - externs.b[z] |= bit.b[z] - } - } - if n == obj.NAME_PARAM { - for z := 0; z < BITS; z++ { - params.b[z] |= bit.b[z] - } - } - - if node.Class == PPARAM { - for z := 0; z < BITS; z++ { - ivar.b[z] |= bit.b[z] - } - } - if node.Class == PPARAMOUT { - for z := 0; z < BITS; z++ { - ovar.b[z] |= bit.b[z] - } - } - - // Treat values with their address taken as live at calls, - // because the garbage collector's liveness analysis in ../gc/plive.c does. - // These must be consistent or else we will elide stores and the garbage - // collector will see uninitialized data. - // The typical case where our own analysis is out of sync is when the - // node appears to have its address taken but that code doesn't actually - // get generated and therefore doesn't show up as an address being - // taken when we analyze the instruction stream. - // One instance of this case is when a closure uses the same name as - // an outer variable for one of its own variables declared with :=. - // The parser flags the outer variable as possibly shared, and therefore - // sets addrtaken, even though it ends up not being actually shared. - // If we were better about _ elision, _ = &x would suffice too. - // The broader := in a closure problem is mentioned in a comment in - // closure.c:/^typecheckclosure and dcl.c:/^oldname. - if node.Addrtaken { - v.addr = 1 - } - - // Disable registerization for globals, because: - // (1) we might panic at any time and we want the recovery code - // to see the latest values (issue 1304). - // (2) we don't know what pointers might point at them and we want - // loads via those pointers to see updated values and vice versa (issue 7995). - // - // Disable registerization for results if using defer, because the deferred func - // might recover and return, causing the current values to be used. - if node.Class == PEXTERN || (Hasdefer != 0 && node.Class == PPARAMOUT) { - v.addr = 1 - } - - if Debug['R'] != 0 { - fmt.Printf("bit=%2d et=%v w=%d+%d %v %v flag=%d\n", i, Econv(int(et), 0), o, w, Nconv(node, obj.FmtSharp), Ctxt.Dconv(a), v.addr) - } - Ostats.Nvar++ - - return bit -} - -var change int - -func prop(f *Flow, ref Bits, cal Bits) { - var f1 *Flow - var r1 *Reg - var z int - var i int - var v *Var - var v1 *Var - - for f1 = f; f1 != nil; f1 = f1.P1 { - r1 = f1.Data.(*Reg) - for z = 0; z < BITS; z++ { - ref.b[z] |= r1.refahead.b[z] - if ref.b[z] != r1.refahead.b[z] { - r1.refahead.b[z] = ref.b[z] - change = 1 - } - - cal.b[z] |= r1.calahead.b[z] - if cal.b[z] != r1.calahead.b[z] { - r1.calahead.b[z] = cal.b[z] - change = 1 - } - } - - switch f1.Prog.As { - case obj.ACALL: - if Noreturn(f1.Prog) { - break - } - - // Mark all input variables (ivar) as used, because that's what the - // liveness bitmaps say. The liveness bitmaps say that so that a - // panic will not show stale values in the parameter dump. - // Mark variables with a recent VARDEF (r1->act) as used, - // so that the optimizer flushes initializations to memory, - // so that if a garbage collection happens during this CALL, - // the collector will see initialized memory. Again this is to - // match what the liveness bitmaps say. - for z = 0; z < BITS; z++ { - cal.b[z] |= ref.b[z] | externs.b[z] | ivar.b[z] | r1.act.b[z] - ref.b[z] = 0 - } - - // cal.b is the current approximation of what's live across the call. - // Every bit in cal.b is a single stack word. For each such word, - // find all the other tracked stack words in the same Go variable - // (struct/slice/string/interface) and mark them live too. - // This is necessary because the liveness analysis for the garbage - // collector works at variable granularity, not at word granularity. - // It is fundamental for slice/string/interface: the garbage collector - // needs the whole value, not just some of the words, in order to - // interpret the other bits correctly. Specifically, slice needs a consistent - // ptr and cap, string needs a consistent ptr and len, and interface - // needs a consistent type word and data word. - for z = 0; z < BITS; z++ { - if cal.b[z] == 0 { - continue - } - for i = 0; i < 64; i++ { - if z*64+i >= nvar || (cal.b[z]>>uint(i))&1 == 0 { - continue - } - v = &vars[z*64+i] - if v.node.Opt == nil { // v represents fixed register, not Go variable - continue - } - - // v->node->opt is the head of a linked list of Vars - // corresponding to tracked words from the Go variable v->node. - // Walk the list and set all the bits. - // For a large struct this could end up being quadratic: - // after the first setting, the outer loop (for z, i) would see a 1 bit - // for all of the remaining words in the struct, and for each such - // word would go through and turn on all the bits again. - // To avoid the quadratic behavior, we only turn on the bits if - // v is the head of the list or if the head's bit is not yet turned on. - // This will set the bits at most twice, keeping the overall loop linear. - v1, _ = v.node.Opt.(*Var) - - if v == v1 || !btest(&cal, uint(v1.id)) { - for ; v1 != nil; v1 = v1.nextinnode { - biset(&cal, uint(v1.id)) - } - } - } - } - - case obj.ATEXT: - for z = 0; z < BITS; z++ { - cal.b[z] = 0 - ref.b[z] = 0 - } - - case obj.ARET: - for z = 0; z < BITS; z++ { - cal.b[z] = externs.b[z] | ovar.b[z] - ref.b[z] = 0 - } - } - - for z = 0; z < BITS; z++ { - ref.b[z] = ref.b[z]&^r1.set.b[z] | r1.use1.b[z] | r1.use2.b[z] - cal.b[z] &^= (r1.set.b[z] | r1.use1.b[z] | r1.use2.b[z]) - r1.refbehind.b[z] = ref.b[z] - r1.calbehind.b[z] = cal.b[z] - } - - if f1.Active != 0 { - break - } - f1.Active = 1 - } - - var r *Reg - var f2 *Flow - for ; f != f1; f = f.P1 { - r = f.Data.(*Reg) - for f2 = f.P2; f2 != nil; f2 = f2.P2link { - prop(f2, r.refbehind, r.calbehind) - } - } -} - -func synch(f *Flow, dif Bits) { - var r1 *Reg - var z int - - for f1 := f; f1 != nil; f1 = f1.S1 { - r1 = f1.Data.(*Reg) - for z = 0; z < BITS; z++ { - dif.b[z] = dif.b[z]&^(^r1.refbehind.b[z]&r1.refahead.b[z]) | r1.set.b[z] | r1.regdiff.b[z] - if dif.b[z] != r1.regdiff.b[z] { - r1.regdiff.b[z] = dif.b[z] - change = 1 - } - } - - if f1.Active != 0 { - break - } - f1.Active = 1 - for z = 0; z < BITS; z++ { - dif.b[z] &^= (^r1.calbehind.b[z] & r1.calahead.b[z]) - } - if f1.S2 != nil { - synch(f1.S2, dif) - } - } -} - -func allreg(b uint64, r *Rgn) uint64 { - v := &vars[r.varno] - r.regno = 0 - switch v.etype { - default: - Fatal("unknown etype %d/%v", Bitno(b), Econv(int(v.etype), 0)) - - case TINT8, - TUINT8, - TINT16, - TUINT16, - TINT32, - TUINT32, - TINT64, - TUINT64, - TINT, - TUINT, - TUINTPTR, - TBOOL, - TPTR32, - TPTR64: - i := Thearch.BtoR(^b) - if i != 0 && r.cost > 0 { - r.regno = int16(i) - return Thearch.RtoB(i) - } - - case TFLOAT32, TFLOAT64: - i := Thearch.BtoF(^b) - if i != 0 && r.cost > 0 { - r.regno = int16(i) - return Thearch.FtoB(i) - } - } - - return 0 -} - -func LOAD(r *Reg, z int) uint64 { - return ^r.refbehind.b[z] & r.refahead.b[z] -} - -func STORE(r *Reg, z int) uint64 { - return ^r.calbehind.b[z] & r.calahead.b[z] -} - -// Cost parameters -const ( - CLOAD = 5 // cost of load - CREF = 5 // cost of reference if not registerized - LOOP = 3 // loop execution count (applied in popt.go) -) - -func paint1(f *Flow, bn int) { - z := bn / 64 - bb := uint64(1 << uint(bn%64)) - r := f.Data.(*Reg) - if r.act.b[z]&bb != 0 { - return - } - var f1 *Flow - var r1 *Reg - for { - if r.refbehind.b[z]&bb == 0 { - break - } - f1 = f.P1 - if f1 == nil { - break - } - r1 = f1.Data.(*Reg) - if r1.refahead.b[z]&bb == 0 { - break - } - if r1.act.b[z]&bb != 0 { - break - } - f = f1 - r = r1 - } - - if LOAD(r, z)&^(r.set.b[z]&^(r.use1.b[z]|r.use2.b[z]))&bb != 0 { - change -= CLOAD * int(f.Loop) - } - - for { - r.act.b[z] |= bb - - if f.Prog.As != obj.ANOP { // don't give credit for NOPs - if r.use1.b[z]&bb != 0 { - change += CREF * int(f.Loop) - } - if (r.use2.b[z]|r.set.b[z])&bb != 0 { - change += CREF * int(f.Loop) - } - } - - if STORE(r, z)&r.regdiff.b[z]&bb != 0 { - change -= CLOAD * int(f.Loop) - } - - if r.refbehind.b[z]&bb != 0 { - for f1 = f.P2; f1 != nil; f1 = f1.P2link { - if (f1.Data.(*Reg)).refahead.b[z]&bb != 0 { - paint1(f1, bn) - } - } - } - - if r.refahead.b[z]&bb == 0 { - break - } - f1 = f.S2 - if f1 != nil { - if (f1.Data.(*Reg)).refbehind.b[z]&bb != 0 { - paint1(f1, bn) - } - } - f = f.S1 - if f == nil { - break - } - r = f.Data.(*Reg) - if r.act.b[z]&bb != 0 { - break - } - if r.refbehind.b[z]&bb == 0 { - break - } - } -} - -func paint2(f *Flow, bn int, depth int) uint64 { - z := bn / 64 - bb := uint64(1 << uint(bn%64)) - vreg := regbits - r := f.Data.(*Reg) - if r.act.b[z]&bb == 0 { - return vreg - } - var r1 *Reg - var f1 *Flow - for { - if r.refbehind.b[z]&bb == 0 { - break - } - f1 = f.P1 - if f1 == nil { - break - } - r1 = f1.Data.(*Reg) - if r1.refahead.b[z]&bb == 0 { - break - } - if r1.act.b[z]&bb == 0 { - break - } - f = f1 - r = r1 - } - - for { - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf(" paint2 %d %v\n", depth, f.Prog) - } - - r.act.b[z] &^= bb - - vreg |= r.regu - - if r.refbehind.b[z]&bb != 0 { - for f1 = f.P2; f1 != nil; f1 = f1.P2link { - if (f1.Data.(*Reg)).refahead.b[z]&bb != 0 { - vreg |= paint2(f1, bn, depth+1) - } - } - } - - if r.refahead.b[z]&bb == 0 { - break - } - f1 = f.S2 - if f1 != nil { - if (f1.Data.(*Reg)).refbehind.b[z]&bb != 0 { - vreg |= paint2(f1, bn, depth+1) - } - } - f = f.S1 - if f == nil { - break - } - r = f.Data.(*Reg) - if r.act.b[z]&bb == 0 { - break - } - if r.refbehind.b[z]&bb == 0 { - break - } - } - - return vreg -} - -func paint3(f *Flow, bn int, rb uint64, rn int) { - z := bn / 64 - bb := uint64(1 << uint(bn%64)) - r := f.Data.(*Reg) - if r.act.b[z]&bb != 0 { - return - } - var r1 *Reg - var f1 *Flow - for { - if r.refbehind.b[z]&bb == 0 { - break - } - f1 = f.P1 - if f1 == nil { - break - } - r1 = f1.Data.(*Reg) - if r1.refahead.b[z]&bb == 0 { - break - } - if r1.act.b[z]&bb != 0 { - break - } - f = f1 - r = r1 - } - - if LOAD(r, z)&^(r.set.b[z]&^(r.use1.b[z]|r.use2.b[z]))&bb != 0 { - addmove(f, bn, rn, 0) - } - var p *obj.Prog - for { - r.act.b[z] |= bb - p = f.Prog - - if r.use1.b[z]&bb != 0 { - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf("%v", p) - } - addreg(&p.From, rn) - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf(" ===change== %v\n", p) - } - } - - if (r.use2.b[z]|r.set.b[z])&bb != 0 { - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf("%v", p) - } - addreg(&p.To, rn) - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf(" ===change== %v\n", p) - } - } - - if STORE(r, z)&r.regdiff.b[z]&bb != 0 { - addmove(f, bn, rn, 1) - } - r.regu |= rb - - if r.refbehind.b[z]&bb != 0 { - for f1 = f.P2; f1 != nil; f1 = f1.P2link { - if (f1.Data.(*Reg)).refahead.b[z]&bb != 0 { - paint3(f1, bn, rb, rn) - } - } - } - - if r.refahead.b[z]&bb == 0 { - break - } - f1 = f.S2 - if f1 != nil { - if (f1.Data.(*Reg)).refbehind.b[z]&bb != 0 { - paint3(f1, bn, rb, rn) - } - } - f = f.S1 - if f == nil { - break - } - r = f.Data.(*Reg) - if r.act.b[z]&bb != 0 { - break - } - if r.refbehind.b[z]&bb == 0 { - break - } - } -} - -func addreg(a *obj.Addr, rn int) { - a.Sym = nil - a.Node = nil - a.Offset = 0 - a.Type = obj.TYPE_REG - a.Reg = int16(rn) - a.Name = 0 - - Ostats.Ncvtreg++ -} - -func dumpone(f *Flow, isreg int) { - fmt.Printf("%d:%v", f.Loop, f.Prog) - if isreg != 0 { - r := f.Data.(*Reg) - var bit Bits - for z := 0; z < BITS; z++ { - bit.b[z] = r.set.b[z] | r.use1.b[z] | r.use2.b[z] | r.refbehind.b[z] | r.refahead.b[z] | r.calbehind.b[z] | r.calahead.b[z] | r.regdiff.b[z] | r.act.b[z] | 0 - } - if bany(&bit) { - fmt.Printf("\t") - if bany(&r.set) { - fmt.Printf(" s:%v", &r.set) - } - if bany(&r.use1) { - fmt.Printf(" u1:%v", &r.use1) - } - if bany(&r.use2) { - fmt.Printf(" u2:%v", &r.use2) - } - if bany(&r.refbehind) { - fmt.Printf(" rb:%v ", &r.refbehind) - } - if bany(&r.refahead) { - fmt.Printf(" ra:%v ", &r.refahead) - } - if bany(&r.calbehind) { - fmt.Printf(" cb:%v ", &r.calbehind) - } - if bany(&r.calahead) { - fmt.Printf(" ca:%v ", &r.calahead) - } - if bany(&r.regdiff) { - fmt.Printf(" d:%v ", &r.regdiff) - } - if bany(&r.act) { - fmt.Printf(" a:%v ", &r.act) - } - } - } - - fmt.Printf("\n") -} - -func Dumpit(str string, r0 *Flow, isreg int) { - var r1 *Flow - - fmt.Printf("\n%s\n", str) - for r := r0; r != nil; r = r.Link { - dumpone(r, isreg) - r1 = r.P2 - if r1 != nil { - fmt.Printf("\tpred:") - for ; r1 != nil; r1 = r1.P2link { - fmt.Printf(" %.4d", uint(int(r1.Prog.Pc))) - } - if r.P1 != nil { - fmt.Printf(" (and %.4d)", uint(int(r.P1.Prog.Pc))) - } else { - fmt.Printf(" (only)") - } - fmt.Printf("\n") - } - - // Print successors if it's not just the next one - if r.S1 != r.Link || r.S2 != nil { - fmt.Printf("\tsucc:") - if r.S1 != nil { - fmt.Printf(" %.4d", uint(int(r.S1.Prog.Pc))) - } - if r.S2 != nil { - fmt.Printf(" %.4d", uint(int(r.S2.Prog.Pc))) - } - fmt.Printf("\n") - } - } -} - -func regopt(firstp *obj.Prog) { - mergetemp(firstp) - - /* - * control flow is more complicated in generated go code - * than in generated c code. define pseudo-variables for - * registers, so we have complete register usage information. - */ - var nreg int - regnames := Thearch.Regnames(&nreg) - - nvar = nreg - for i := 0; i < nreg; i++ { - vars[i] = Var{} - } - for i := 0; i < nreg; i++ { - if regnodes[i] == nil { - regnodes[i] = newname(Lookup(regnames[i])) - } - vars[i].node = regnodes[i] - } - - regbits = Thearch.Excludedregs() - externs = zbits - params = zbits - consts = zbits - addrs = zbits - ivar = zbits - ovar = zbits - - /* - * pass 1 - * build aux data structure - * allocate pcs - * find use and set of variables - */ - g := Flowstart(firstp, func() interface{} { return new(Reg) }) - if g == nil { - for i := 0; i < nvar; i++ { - vars[i].node.Opt = nil - } - return - } - - firstf := g.Start - - for f := firstf; f != nil; f = f.Link { - p := f.Prog - if p.As == obj.AVARDEF || p.As == obj.AVARKILL { - continue - } - - // Avoid making variables for direct-called functions. - if p.As == obj.ACALL && p.To.Type == obj.TYPE_MEM && p.To.Name == obj.NAME_EXTERN { - continue - } - - // from vs to doesn't matter for registers. - r := f.Data.(*Reg) - r.use1.b[0] |= p.Info.Reguse | p.Info.Regindex - r.set.b[0] |= p.Info.Regset - - bit := mkvar(f, &p.From) - if bany(&bit) { - if p.Info.Flags&LeftAddr != 0 { - setaddrs(bit) - } - if p.Info.Flags&LeftRead != 0 { - for z := 0; z < BITS; z++ { - r.use1.b[z] |= bit.b[z] - } - } - if p.Info.Flags&LeftWrite != 0 { - for z := 0; z < BITS; z++ { - r.set.b[z] |= bit.b[z] - } - } - } - - // Compute used register for reg - if p.Info.Flags&RegRead != 0 { - r.use1.b[0] |= Thearch.RtoB(int(p.Reg)) - } - - // Currently we never generate three register forms. - // If we do, this will need to change. - if p.From3.Type != obj.TYPE_NONE { - Fatal("regopt not implemented for from3") - } - - bit = mkvar(f, &p.To) - if bany(&bit) { - if p.Info.Flags&RightAddr != 0 { - setaddrs(bit) - } - if p.Info.Flags&RightRead != 0 { - for z := 0; z < BITS; z++ { - r.use2.b[z] |= bit.b[z] - } - } - if p.Info.Flags&RightWrite != 0 { - for z := 0; z < BITS; z++ { - r.set.b[z] |= bit.b[z] - } - } - } - } - - for i := 0; i < nvar; i++ { - v := &vars[i] - if v.addr != 0 { - bit := blsh(uint(i)) - for z := 0; z < BITS; z++ { - addrs.b[z] |= bit.b[z] - } - } - - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf("bit=%2d addr=%d et=%v w=%-2d s=%v + %d\n", i, v.addr, Econv(int(v.etype), 0), v.width, v.node, v.offset) - } - } - - if Debug['R'] != 0 && Debug['v'] != 0 { - Dumpit("pass1", firstf, 1) - } - - /* - * pass 2 - * find looping structure - */ - flowrpo(g) - - if Debug['R'] != 0 && Debug['v'] != 0 { - Dumpit("pass2", firstf, 1) - } - - /* - * pass 2.5 - * iterate propagating fat vardef covering forward - * r->act records vars with a VARDEF since the last CALL. - * (r->act will be reused in pass 5 for something else, - * but we'll be done with it by then.) - */ - active := 0 - - for f := firstf; f != nil; f = f.Link { - f.Active = 0 - r := f.Data.(*Reg) - r.act = zbits - } - - for f := firstf; f != nil; f = f.Link { - p := f.Prog - if p.As == obj.AVARDEF && Isfat(((p.To.Node).(*Node)).Type) && ((p.To.Node).(*Node)).Opt != nil { - active++ - walkvardef(p.To.Node.(*Node), f, active) - } - } - - /* - * pass 3 - * iterate propagating usage - * back until flow graph is complete - */ - var f1 *Flow - var i int - var f *Flow -loop1: - change = 0 - - for f = firstf; f != nil; f = f.Link { - f.Active = 0 - } - for f = firstf; f != nil; f = f.Link { - if f.Prog.As == obj.ARET { - prop(f, zbits, zbits) - } - } - - /* pick up unreachable code */ -loop11: - i = 0 - - for f = firstf; f != nil; f = f1 { - f1 = f.Link - if f1 != nil && f1.Active != 0 && f.Active == 0 { - prop(f, zbits, zbits) - i = 1 - } - } - - if i != 0 { - goto loop11 - } - if change != 0 { - goto loop1 - } - - if Debug['R'] != 0 && Debug['v'] != 0 { - Dumpit("pass3", firstf, 1) - } - - /* - * pass 4 - * iterate propagating register/variable synchrony - * forward until graph is complete - */ -loop2: - change = 0 - - for f = firstf; f != nil; f = f.Link { - f.Active = 0 - } - synch(firstf, zbits) - if change != 0 { - goto loop2 - } - - if Debug['R'] != 0 && Debug['v'] != 0 { - Dumpit("pass4", firstf, 1) - } - - /* - * pass 4.5 - * move register pseudo-variables into regu. - */ - mask := uint64((1 << uint(nreg)) - 1) - for f := firstf; f != nil; f = f.Link { - r := f.Data.(*Reg) - r.regu = (r.refbehind.b[0] | r.set.b[0]) & mask - r.set.b[0] &^= mask - r.use1.b[0] &^= mask - r.use2.b[0] &^= mask - r.refbehind.b[0] &^= mask - r.refahead.b[0] &^= mask - r.calbehind.b[0] &^= mask - r.calahead.b[0] &^= mask - r.regdiff.b[0] &^= mask - r.act.b[0] &^= mask - } - - if Debug['R'] != 0 && Debug['v'] != 0 { - Dumpit("pass4.5", firstf, 1) - } - - /* - * pass 5 - * isolate regions - * calculate costs (paint1) - */ - var bit Bits - if f := firstf; f != nil { - r := f.Data.(*Reg) - for z := 0; z < BITS; z++ { - bit.b[z] = (r.refahead.b[z] | r.calahead.b[z]) &^ (externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]) - } - if bany(&bit) && f.Refset == 0 { - // should never happen - all variables are preset - if Debug['w'] != 0 { - fmt.Printf("%v: used and not set: %v\n", f.Prog.Line(), &bit) - } - f.Refset = 1 - } - } - - for f := firstf; f != nil; f = f.Link { - (f.Data.(*Reg)).act = zbits - } - nregion = 0 - region = region[:0] - var rgp *Rgn - for f := firstf; f != nil; f = f.Link { - r := f.Data.(*Reg) - for z := 0; z < BITS; z++ { - bit.b[z] = r.set.b[z] &^ (r.refahead.b[z] | r.calahead.b[z] | addrs.b[z]) - } - if bany(&bit) && f.Refset == 0 { - if Debug['w'] != 0 { - fmt.Printf("%v: set and not used: %v\n", f.Prog.Line(), &bit) - } - f.Refset = 1 - Thearch.Excise(f) - } - - for z := 0; z < BITS; z++ { - bit.b[z] = LOAD(r, z) &^ (r.act.b[z] | addrs.b[z]) - } - for bany(&bit) { - i = bnum(bit) - change = 0 - paint1(f, i) - biclr(&bit, uint(i)) - if change <= 0 { - continue - } - if nregion >= MaxRgn { - nregion++ - continue - } - - region = append(region, Rgn{ - enter: f, - cost: int16(change), - varno: int16(i), - }) - nregion++ - } - } - - if false && Debug['v'] != 0 && strings.Contains(Curfn.Nname.Sym.Name, "Parse") { - Warn("regions: %d\n", nregion) - } - if nregion >= MaxRgn { - if Debug['v'] != 0 { - Warn("too many regions: %d\n", nregion) - } - nregion = MaxRgn - } - - sort.Sort(rcmp(region[:nregion])) - - if Debug['R'] != 0 && Debug['v'] != 0 { - Dumpit("pass5", firstf, 1) - } - - /* - * pass 6 - * determine used registers (paint2) - * replace code (paint3) - */ - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf("\nregisterizing\n") - } - var usedreg uint64 - var vreg uint64 - for i := 0; i < nregion; i++ { - rgp = ®ion[i] - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf("region %d: cost %d varno %d enter %d\n", i, rgp.cost, rgp.varno, rgp.enter.Prog.Pc) - } - bit = blsh(uint(rgp.varno)) - usedreg = paint2(rgp.enter, int(rgp.varno), 0) - vreg = allreg(usedreg, rgp) - if rgp.regno != 0 { - if Debug['R'] != 0 && Debug['v'] != 0 { - v := &vars[rgp.varno] - fmt.Printf("registerize %v+%d (bit=%2d et=%v) in %v usedreg=%#x vreg=%#x\n", v.node, v.offset, rgp.varno, Econv(int(v.etype), 0), obj.Rconv(int(rgp.regno)), usedreg, vreg) - } - - paint3(rgp.enter, int(rgp.varno), vreg, int(rgp.regno)) - } - } - - /* - * free aux structures. peep allocates new ones. - */ - for i := 0; i < nvar; i++ { - vars[i].node.Opt = nil - } - Flowend(g) - firstf = nil - - if Debug['R'] != 0 && Debug['v'] != 0 { - // Rebuild flow graph, since we inserted instructions - g := Flowstart(firstp, nil) - firstf = g.Start - Dumpit("pass6", firstf, 0) - Flowend(g) - firstf = nil - } - - /* - * pass 7 - * peep-hole on basic block - */ - if Debug['R'] == 0 || Debug['P'] != 0 { - Thearch.Peep(firstp) - } - - /* - * eliminate nops - */ - for p := firstp; p != nil; p = p.Link { - for p.Link != nil && p.Link.As == obj.ANOP { - p.Link = p.Link.Link - } - if p.To.Type == obj.TYPE_BRANCH { - for p.To.Val.(*obj.Prog) != nil && p.To.Val.(*obj.Prog).As == obj.ANOP { - p.To.Val = p.To.Val.(*obj.Prog).Link - } - } - } - - if Debug['R'] != 0 { - if Ostats.Ncvtreg != 0 || Ostats.Nspill != 0 || Ostats.Nreload != 0 || Ostats.Ndelmov != 0 || Ostats.Nvar != 0 || Ostats.Naddr != 0 || false { - fmt.Printf("\nstats\n") - } - - if Ostats.Ncvtreg != 0 { - fmt.Printf("\t%4d cvtreg\n", Ostats.Ncvtreg) - } - if Ostats.Nspill != 0 { - fmt.Printf("\t%4d spill\n", Ostats.Nspill) - } - if Ostats.Nreload != 0 { - fmt.Printf("\t%4d reload\n", Ostats.Nreload) - } - if Ostats.Ndelmov != 0 { - fmt.Printf("\t%4d delmov\n", Ostats.Ndelmov) - } - if Ostats.Nvar != 0 { - fmt.Printf("\t%4d var\n", Ostats.Nvar) - } - if Ostats.Naddr != 0 { - fmt.Printf("\t%4d addr\n", Ostats.Naddr) - } - - Ostats = OptStats{} - } -} - -// bany reports whether any bits in a are set. -func bany(a *Bits) bool { - for _, x := range &a.b { // & to avoid making a copy of a.b - if x != 0 { - return true - } - } - return false -} - -// bnum reports the lowest index of a 1 bit in a. -func bnum(a Bits) int { - for i, x := range &a.b { // & to avoid making a copy of a.b - if x != 0 { - return 64*i + Bitno(x) - } - } - - Fatal("bad in bnum") - return 0 -} - -// blsh returns a Bits with 1 at index n, 0 elsewhere (1<>= 32 - } - if b&(1<<16-1) == 0 { - n += 16 - b >>= 16 - } - if b&(1<<8-1) == 0 { - n += 8 - b >>= 8 - } - if b&(1<<4-1) == 0 { - n += 4 - b >>= 4 - } - if b&(1<<2-1) == 0 { - n += 2 - b >>= 2 - } - if b&1 == 0 { - n++ - } - return n -} - -// String returns a space-separated list of the variables represented by bits. -func (bits Bits) String() string { - // Note: This method takes a value receiver, both for convenience - // and to make it safe to modify the bits as we process them. - // Even so, most prints above use &bits, because then the value - // being stored in the interface{} is a pointer and does not require - // an allocation and copy to create the interface{}. - var buf bytes.Buffer - sep := "" - for bany(&bits) { - i := bnum(bits) - buf.WriteString(sep) - sep = " " - v := &vars[i] - if v.node == nil || v.node.Sym == nil { - fmt.Fprintf(&buf, "$%d", i) - } else { - fmt.Fprintf(&buf, "%s(%d)", v.node.Sym.Name, i) - if v.offset != 0 { - fmt.Fprintf(&buf, "%+d", int64(v.offset)) - } - } - biclr(&bits, uint(i)) - } - return buf.String() -} diff --git a/src/cmd/internal/gc/select.go b/src/cmd/internal/gc/select.go deleted file mode 100644 index 3a28ea3c77..0000000000 --- a/src/cmd/internal/gc/select.go +++ /dev/null @@ -1,371 +0,0 @@ -// Copyright 2009 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 gc - -/* - * select - */ -func typecheckselect(sel *Node) { - var ncase *Node - var n *Node - - var def *Node - lno := int(setlineno(sel)) - count := 0 - typechecklist(sel.Ninit, Etop) - for l := sel.List; l != nil; l = l.Next { - count++ - ncase = l.N - setlineno(ncase) - if ncase.Op != OXCASE { - Fatal("typecheckselect %v", Oconv(int(ncase.Op), 0)) - } - - if ncase.List == nil { - // default - if def != nil { - Yyerror("multiple defaults in select (first at %v)", def.Line()) - } else { - def = ncase - } - } else if ncase.List.Next != nil { - Yyerror("select cases cannot be lists") - } else { - n = typecheck(&ncase.List.N, Etop) - ncase.Left = n - ncase.List = nil - setlineno(n) - switch n.Op { - default: - Yyerror("select case must be receive, send or assign recv") - - // convert x = <-c into OSELRECV(x, <-c). - // remove implicit conversions; the eventual assignment - // will reintroduce them. - case OAS: - if (n.Right.Op == OCONVNOP || n.Right.Op == OCONVIFACE) && n.Right.Implicit { - n.Right = n.Right.Left - } - - if n.Right.Op != ORECV { - Yyerror("select assignment must have receive on right hand side") - break - } - - n.Op = OSELRECV - - // convert x, ok = <-c into OSELRECV2(x, <-c) with ntest=ok - case OAS2RECV: - if n.Rlist.N.Op != ORECV { - Yyerror("select assignment must have receive on right hand side") - break - } - - n.Op = OSELRECV2 - n.Left = n.List.N - n.Ntest = n.List.Next.N - n.List = nil - n.Right = n.Rlist.N - n.Rlist = nil - - // convert <-c into OSELRECV(N, <-c) - case ORECV: - n = Nod(OSELRECV, nil, n) - - n.Typecheck = 1 - ncase.Left = n - - case OSEND: - break - } - } - - typechecklist(ncase.Nbody, Etop) - } - - sel.Xoffset = int64(count) - lineno = int32(lno) -} - -func walkselect(sel *Node) { - if sel.List == nil && sel.Xoffset != 0 { - Fatal("double walkselect") // already rewrote - } - - lno := int(setlineno(sel)) - i := count(sel.List) - - // optimization: zero-case select - var init *NodeList - var r *Node - var n *Node - var var_ *Node - var selv *Node - var cas *Node - if i == 0 { - sel.Nbody = list1(mkcall("block", nil, nil)) - goto out - } - - // optimization: one-case select: single op. - // TODO(rsc): Reenable optimization once order.c can handle it. - // golang.org/issue/7672. - if i == 1 { - cas := sel.List.N - setlineno(cas) - l := cas.Ninit - if cas.Left != nil { // not default: - n := cas.Left - l = concat(l, n.Ninit) - n.Ninit = nil - var ch *Node - switch n.Op { - default: - Fatal("select %v", Oconv(int(n.Op), 0)) - - // ok already - case OSEND: - ch = n.Left - - case OSELRECV, OSELRECV2: - ch = n.Right.Left - if n.Op == OSELRECV || n.Ntest == nil { - if n.Left == nil { - n = n.Right - } else { - n.Op = OAS - } - break - } - - if n.Left == nil { - typecheck(&nblank, Erv|Easgn) - n.Left = nblank - } - - n.Op = OAS2 - n.List = list(list1(n.Left), n.Ntest) - n.Rlist = list1(n.Right) - n.Right = nil - n.Left = nil - n.Ntest = nil - n.Typecheck = 0 - typecheck(&n, Etop) - } - - // if ch == nil { block() }; n; - a := Nod(OIF, nil, nil) - - a.Ntest = Nod(OEQ, ch, nodnil()) - a.Nbody = list1(mkcall("block", nil, &l)) - typecheck(&a, Etop) - l = list(l, a) - l = list(l, n) - } - - l = concat(l, cas.Nbody) - sel.Nbody = l - goto out - } - - // convert case value arguments to addresses. - // this rewrite is used by both the general code and the next optimization. - for l := sel.List; l != nil; l = l.Next { - cas = l.N - setlineno(cas) - n = cas.Left - if n == nil { - continue - } - switch n.Op { - case OSEND: - n.Right = Nod(OADDR, n.Right, nil) - typecheck(&n.Right, Erv) - - case OSELRECV, OSELRECV2: - if n.Op == OSELRECV2 && n.Ntest == nil { - n.Op = OSELRECV - } - if n.Op == OSELRECV2 { - n.Ntest = Nod(OADDR, n.Ntest, nil) - typecheck(&n.Ntest, Erv) - } - - if n.Left == nil { - n.Left = nodnil() - } else { - n.Left = Nod(OADDR, n.Left, nil) - typecheck(&n.Left, Erv) - } - } - } - - // optimization: two-case select but one is default: single non-blocking op. - if i == 2 && (sel.List.N.Left == nil || sel.List.Next.N.Left == nil) { - var cas *Node - var dflt *Node - if sel.List.N.Left == nil { - cas = sel.List.Next.N - dflt = sel.List.N - } else { - dflt = sel.List.Next.N - cas = sel.List.N - } - - n := cas.Left - setlineno(n) - r := Nod(OIF, nil, nil) - r.Ninit = cas.Ninit - switch n.Op { - default: - Fatal("select %v", Oconv(int(n.Op), 0)) - - // if selectnbsend(c, v) { body } else { default body } - case OSEND: - ch := n.Left - - r.Ntest = mkcall1(chanfn("selectnbsend", 2, ch.Type), Types[TBOOL], &r.Ninit, typename(ch.Type), ch, n.Right) - - // if c != nil && selectnbrecv(&v, c) { body } else { default body } - case OSELRECV: - r = Nod(OIF, nil, nil) - - r.Ninit = cas.Ninit - ch := n.Right.Left - r.Ntest = mkcall1(chanfn("selectnbrecv", 2, ch.Type), Types[TBOOL], &r.Ninit, typename(ch.Type), n.Left, ch) - - // if c != nil && selectnbrecv2(&v, c) { body } else { default body } - case OSELRECV2: - r = Nod(OIF, nil, nil) - - r.Ninit = cas.Ninit - ch := n.Right.Left - r.Ntest = mkcall1(chanfn("selectnbrecv2", 2, ch.Type), Types[TBOOL], &r.Ninit, typename(ch.Type), n.Left, n.Ntest, ch) - } - - typecheck(&r.Ntest, Erv) - r.Nbody = cas.Nbody - r.Nelse = concat(dflt.Ninit, dflt.Nbody) - sel.Nbody = list1(r) - goto out - } - - init = sel.Ninit - sel.Ninit = nil - - // generate sel-struct - setlineno(sel) - - selv = temp(selecttype(int32(sel.Xoffset))) - r = Nod(OAS, selv, nil) - typecheck(&r, Etop) - init = list(init, r) - var_ = conv(conv(Nod(OADDR, selv, nil), Types[TUNSAFEPTR]), Ptrto(Types[TUINT8])) - r = mkcall("newselect", nil, nil, var_, Nodintconst(selv.Type.Width), Nodintconst(sel.Xoffset)) - typecheck(&r, Etop) - init = list(init, r) - - // register cases - for l := sel.List; l != nil; l = l.Next { - cas = l.N - setlineno(cas) - n = cas.Left - r = Nod(OIF, nil, nil) - r.Ninit = cas.Ninit - cas.Ninit = nil - if n != nil { - r.Ninit = concat(r.Ninit, n.Ninit) - n.Ninit = nil - } - - if n == nil { - // selectdefault(sel *byte); - r.Ntest = mkcall("selectdefault", Types[TBOOL], &r.Ninit, var_) - } else { - switch n.Op { - default: - Fatal("select %v", Oconv(int(n.Op), 0)) - - // selectsend(sel *byte, hchan *chan any, elem *any) (selected bool); - case OSEND: - r.Ntest = mkcall1(chanfn("selectsend", 2, n.Left.Type), Types[TBOOL], &r.Ninit, var_, n.Left, n.Right) - - // selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool); - case OSELRECV: - r.Ntest = mkcall1(chanfn("selectrecv", 2, n.Right.Left.Type), Types[TBOOL], &r.Ninit, var_, n.Right.Left, n.Left) - - // selectrecv2(sel *byte, hchan *chan any, elem *any, received *bool) (selected bool); - case OSELRECV2: - r.Ntest = mkcall1(chanfn("selectrecv2", 2, n.Right.Left.Type), Types[TBOOL], &r.Ninit, var_, n.Right.Left, n.Left, n.Ntest) - } - } - - // selv is no longer alive after use. - r.Nbody = list(r.Nbody, Nod(OVARKILL, selv, nil)) - - r.Nbody = concat(r.Nbody, cas.Nbody) - r.Nbody = list(r.Nbody, Nod(OBREAK, nil, nil)) - init = list(init, r) - } - - // run the select - setlineno(sel) - - init = list(init, mkcall("selectgo", nil, nil, var_)) - sel.Nbody = init - -out: - sel.List = nil - walkstmtlist(sel.Nbody) - lineno = int32(lno) -} - -// Keep in sync with src/runtime/runtime2.go and src/runtime/select.go. -func selecttype(size int32) *Type { - // TODO(dvyukov): it's possible to generate SudoG and Scase only once - // and then cache; and also cache Select per size. - sudog := Nod(OTSTRUCT, nil, nil) - - sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("g")), typenod(Ptrto(Types[TUINT8])))) - sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("selectdone")), typenod(Ptrto(Types[TUINT8])))) - sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("next")), typenod(Ptrto(Types[TUINT8])))) - sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("prev")), typenod(Ptrto(Types[TUINT8])))) - sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("elem")), typenod(Ptrto(Types[TUINT8])))) - sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("releasetime")), typenod(Types[TUINT64]))) - sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("nrelease")), typenod(Types[TINT32]))) - sudog.List = list(sudog.List, Nod(ODCLFIELD, newname(Lookup("waitlink")), typenod(Ptrto(Types[TUINT8])))) - typecheck(&sudog, Etype) - sudog.Type.Noalg = 1 - sudog.Type.Local = true - - scase := Nod(OTSTRUCT, nil, nil) - scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("elem")), typenod(Ptrto(Types[TUINT8])))) - scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("chan")), typenod(Ptrto(Types[TUINT8])))) - scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("pc")), typenod(Types[TUINTPTR]))) - scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("kind")), typenod(Types[TUINT16]))) - scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("so")), typenod(Types[TUINT16]))) - scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("receivedp")), typenod(Ptrto(Types[TUINT8])))) - scase.List = list(scase.List, Nod(ODCLFIELD, newname(Lookup("releasetime")), typenod(Types[TUINT64]))) - typecheck(&scase, Etype) - scase.Type.Noalg = 1 - scase.Type.Local = true - - sel := Nod(OTSTRUCT, nil, nil) - sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("tcase")), typenod(Types[TUINT16]))) - sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("ncase")), typenod(Types[TUINT16]))) - sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("pollorder")), typenod(Ptrto(Types[TUINT8])))) - sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("lockorder")), typenod(Ptrto(Types[TUINT8])))) - arr := Nod(OTARRAY, Nodintconst(int64(size)), scase) - sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("scase")), arr)) - arr = Nod(OTARRAY, Nodintconst(int64(size)), typenod(Ptrto(Types[TUINT8]))) - sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("lockorderarr")), arr)) - arr = Nod(OTARRAY, Nodintconst(int64(size)), typenod(Types[TUINT16])) - sel.List = list(sel.List, Nod(ODCLFIELD, newname(Lookup("pollorderarr")), arr)) - typecheck(&sel, Etype) - sel.Type.Noalg = 1 - sel.Type.Local = true - - return sel.Type -} diff --git a/src/cmd/internal/gc/sinit.go b/src/cmd/internal/gc/sinit.go deleted file mode 100644 index dfaec74de2..0000000000 --- a/src/cmd/internal/gc/sinit.go +++ /dev/null @@ -1,1528 +0,0 @@ -// Copyright 2009 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 gc - -import ( - "cmd/internal/obj" - "fmt" -) - -/* - * static initialization - */ -const ( - InitNotStarted = 0 - InitDone = 1 - InitPending = 2 -) - -var initlist *NodeList - -// init1 walks the AST starting at n, and accumulates in out -// the list of definitions needing init code in dependency order. -func init1(n *Node, out **NodeList) { - if n == nil { - return - } - init1(n.Left, out) - init1(n.Right, out) - for l := n.List; l != nil; l = l.Next { - init1(l.N, out) - } - - if n.Left != nil && n.Type != nil && n.Left.Op == OTYPE && n.Class == PFUNC { - // Methods called as Type.Method(receiver, ...). - // Definitions for method expressions are stored in type->nname. - init1(n.Type.Nname, out) - } - - if n.Op != ONAME { - return - } - switch n.Class { - case PEXTERN, PFUNC: - break - - default: - if isblank(n) && n.Curfn == nil && n.Defn != nil && n.Defn.Initorder == InitNotStarted { - // blank names initialization is part of init() but not - // when they are inside a function. - break - } - - return - } - - if n.Initorder == InitDone { - return - } - if n.Initorder == InitPending { - // Since mutually recursive sets of functions are allowed, - // we don't necessarily raise an error if n depends on a node - // which is already waiting for its dependencies to be visited. - // - // initlist contains a cycle of identifiers referring to each other. - // If this cycle contains a variable, then this variable refers to itself. - // Conversely, if there exists an initialization cycle involving - // a variable in the program, the tree walk will reach a cycle - // involving that variable. - var nv *Node - if n.Class != PFUNC { - nv = n - goto foundinitloop - } - - for l := initlist; l.N != n; l = l.Next { - if l.N.Class != PFUNC { - nv = l.N - goto foundinitloop - } - } - - // The loop involves only functions, ok. - return - - // if there have already been errors printed, - // those errors probably confused us and - // there might not be a loop. let the user - // fix those first. - foundinitloop: - Flusherrors() - - if nerrors > 0 { - errorexit() - } - - // There is a loop involving nv. We know about - // n and initlist = n1 <- ... <- nv <- ... <- n <- ... - fmt.Printf("%v: initialization loop:\n", nv.Line()) - - // Build back pointers in initlist. - for l := initlist; l != nil; l = l.Next { - if l.Next != nil { - l.Next.End = l - } - } - - // Print nv -> ... -> n1 -> n. - var l *NodeList - for l = initlist; l.N != nv; l = l.Next { - } - for ; l != nil; l = l.End { - fmt.Printf("\t%v %v refers to\n", l.N.Line(), l.N.Sym) - } - - // Print n -> ... -> nv. - for l = initlist; l.N != n; l = l.Next { - } - for ; l.N != nv; l = l.End { - fmt.Printf("\t%v %v refers to\n", l.N.Line(), l.N.Sym) - } - fmt.Printf("\t%v %v\n", nv.Line(), nv.Sym) - errorexit() - } - - // reached a new unvisited node. - n.Initorder = InitPending - - l := new(NodeList) - if l == nil { - Flusherrors() - Yyerror("out of memory") - errorexit() - } - - l.Next = initlist - l.N = n - l.End = nil - initlist = l - - // make sure that everything n depends on is initialized. - // n->defn is an assignment to n - if n.Defn != nil { - switch n.Defn.Op { - default: - goto bad - - case ODCLFUNC: - init2list(n.Defn.Nbody, out) - - case OAS: - if n.Defn.Left != n { - goto bad - } - if isblank(n.Defn.Left) && candiscard(n.Defn.Right) { - n.Defn.Op = OEMPTY - n.Defn.Left = nil - n.Defn.Right = nil - break - } - - init2(n.Defn.Right, out) - if Debug['j'] != 0 { - fmt.Printf("%v\n", n.Sym) - } - if isblank(n) || !staticinit(n, out) { - if Debug['%'] != 0 { - Dump("nonstatic", n.Defn) - } - *out = list(*out, n.Defn) - } - - case OAS2FUNC, OAS2MAPR, OAS2DOTTYPE, OAS2RECV: - if n.Defn.Initorder != InitNotStarted { - break - } - n.Defn.Initorder = InitDone - for l := n.Defn.Rlist; l != nil; l = l.Next { - init1(l.N, out) - } - if Debug['%'] != 0 { - Dump("nonstatic", n.Defn) - } - *out = list(*out, n.Defn) - } - } - - l = initlist - initlist = l.Next - if l.N != n { - Fatal("bad initlist") - } - - n.Initorder = InitDone - return - -bad: - Dump("defn", n.Defn) - Fatal("init1: bad defn") -} - -// recurse over n, doing init1 everywhere. -func init2(n *Node, out **NodeList) { - if n == nil || n.Initorder == InitDone { - return - } - - if n.Op == ONAME && n.Ninit != nil { - Fatal("name %v with ninit: %v\n", n.Sym, Nconv(n, obj.FmtSign)) - } - - init1(n, out) - init2(n.Left, out) - init2(n.Right, out) - init2(n.Ntest, out) - init2list(n.Ninit, out) - init2list(n.List, out) - init2list(n.Rlist, out) - init2list(n.Nbody, out) - init2list(n.Nelse, out) - - if n.Op == OCLOSURE { - init2list(n.Closure.Nbody, out) - } - if n.Op == ODOTMETH || n.Op == OCALLPART { - init2(n.Type.Nname, out) - } -} - -func init2list(l *NodeList, out **NodeList) { - for ; l != nil; l = l.Next { - init2(l.N, out) - } -} - -func initreorder(l *NodeList, out **NodeList) { - var n *Node - - for ; l != nil; l = l.Next { - n = l.N - switch n.Op { - case ODCLFUNC, ODCLCONST, ODCLTYPE: - continue - } - - initreorder(n.Ninit, out) - n.Ninit = nil - init1(n, out) - } -} - -// initfix computes initialization order for a list l of top-level -// declarations and outputs the corresponding list of statements -// to include in the init() function body. -func initfix(l *NodeList) *NodeList { - var lout *NodeList - lno := int(lineno) - initreorder(l, &lout) - lineno = int32(lno) - return lout -} - -/* - * compilation of top-level (static) assignments - * into DATA statements if at all possible. - */ -func staticinit(n *Node, out **NodeList) bool { - if n.Op != ONAME || n.Class != PEXTERN || n.Defn == nil || n.Defn.Op != OAS { - Fatal("staticinit") - } - - lineno = n.Lineno - l := n.Defn.Left - r := n.Defn.Right - return staticassign(l, r, out) -} - -// like staticassign but we are copying an already -// initialized value r. -func staticcopy(l *Node, r *Node, out **NodeList) bool { - if r.Op != ONAME { - return false - } - if r.Class == PFUNC { - gdata(l, r, Widthptr) - return true - } - if r.Class != PEXTERN || r.Sym.Pkg != localpkg { - return false - } - if r.Defn == nil { // probably zeroed but perhaps supplied externally and of unknown value - return false - } - if r.Defn.Op != OAS { - return false - } - orig := r - r = r.Defn.Right - - switch r.Op { - case ONAME: - if staticcopy(l, r, out) { - return true - } - *out = list(*out, Nod(OAS, l, r)) - return true - - case OLITERAL: - if iszero(r) { - return true - } - gdata(l, r, int(l.Type.Width)) - return true - - case OADDR: - switch r.Left.Op { - case ONAME: - gdata(l, r, int(l.Type.Width)) - return true - } - - case OPTRLIT: - switch r.Left.Op { - //dump("not static addr", r); - default: - break - - // copy pointer - case OARRAYLIT, OSTRUCTLIT, OMAPLIT: - gdata(l, Nod(OADDR, r.Nname, nil), int(l.Type.Width)) - - return true - } - - case OARRAYLIT: - if Isslice(r.Type) { - // copy slice - a := r.Nname - - n1 := *l - n1.Xoffset = l.Xoffset + int64(Array_array) - gdata(&n1, Nod(OADDR, a, nil), Widthptr) - n1.Xoffset = l.Xoffset + int64(Array_nel) - gdata(&n1, r.Right, Widthint) - n1.Xoffset = l.Xoffset + int64(Array_cap) - gdata(&n1, r.Right, Widthint) - return true - } - fallthrough - - // fall through - case OSTRUCTLIT: - p := r.Initplan - - n1 := *l - var e *InitEntry - var ll *Node - var rr *Node - for i := 0; i < len(p.E); i++ { - e = &p.E[i] - n1.Xoffset = l.Xoffset + e.Xoffset - n1.Type = e.Expr.Type - if e.Expr.Op == OLITERAL { - gdata(&n1, e.Expr, int(n1.Type.Width)) - } else { - ll = Nod(OXXX, nil, nil) - *ll = n1 - ll.Orig = ll // completely separate copy - if !staticassign(ll, e.Expr, out) { - // Requires computation, but we're - // copying someone else's computation. - rr = Nod(OXXX, nil, nil) - - *rr = *orig - rr.Orig = rr // completely separate copy - rr.Type = ll.Type - rr.Xoffset += e.Xoffset - *out = list(*out, Nod(OAS, ll, rr)) - } - } - } - - return true - } - - return false -} - -func staticassign(l *Node, r *Node, out **NodeList) bool { - var n1 Node - - switch r.Op { - //dump("not static", r); - default: - break - - case ONAME: - return staticcopy(l, r, out) - - case OLITERAL: - if iszero(r) { - return true - } - gdata(l, r, int(l.Type.Width)) - return true - - case OADDR: - var nam Node - if stataddr(&nam, r.Left) { - n1 := *r - n1.Left = &nam - gdata(l, &n1, int(l.Type.Width)) - return true - } - fallthrough - - case OPTRLIT: - switch r.Left.Op { - //dump("not static ptrlit", r); - default: - break - - // Init pointer. - case OARRAYLIT, OMAPLIT, OSTRUCTLIT: - a := staticname(r.Left.Type, 1) - - r.Nname = a - gdata(l, Nod(OADDR, a, nil), int(l.Type.Width)) - - // Init underlying literal. - if !staticassign(a, r.Left, out) { - *out = list(*out, Nod(OAS, a, r.Left)) - } - return true - } - - case OSTRARRAYBYTE: - if l.Class == PEXTERN && r.Left.Op == OLITERAL { - sval := r.Left.Val.U.(string) - slicebytes(l, sval, len(sval)) - return true - } - - case OARRAYLIT: - initplan(r) - if Isslice(r.Type) { - // Init slice. - ta := typ(TARRAY) - - ta.Type = r.Type.Type - ta.Bound = Mpgetfix(r.Right.Val.U.(*Mpint)) - a := staticname(ta, 1) - r.Nname = a - n1 = *l - n1.Xoffset = l.Xoffset + int64(Array_array) - gdata(&n1, Nod(OADDR, a, nil), Widthptr) - n1.Xoffset = l.Xoffset + int64(Array_nel) - gdata(&n1, r.Right, Widthint) - n1.Xoffset = l.Xoffset + int64(Array_cap) - gdata(&n1, r.Right, Widthint) - - // Fall through to init underlying array. - l = a - } - fallthrough - - // fall through - case OSTRUCTLIT: - initplan(r) - - p := r.Initplan - n1 = *l - var e *InitEntry - var a *Node - for i := 0; i < len(p.E); i++ { - e = &p.E[i] - n1.Xoffset = l.Xoffset + e.Xoffset - n1.Type = e.Expr.Type - if e.Expr.Op == OLITERAL { - gdata(&n1, e.Expr, int(n1.Type.Width)) - } else { - a = Nod(OXXX, nil, nil) - *a = n1 - a.Orig = a // completely separate copy - if !staticassign(a, e.Expr, out) { - *out = list(*out, Nod(OAS, a, e.Expr)) - } - } - } - - return true - - // TODO: Table-driven map insert. - case OMAPLIT: - break - } - - return false -} - -/* - * from here down is the walk analysis - * of composite literals. - * most of the work is to generate - * data statements for the constant - * part of the composite literal. - */ -func staticname(t *Type, ctxt int) *Node { - n := newname(Lookupf("statictmp_%.4d", statuniqgen)) - statuniqgen++ - if ctxt == 0 { - n.Name.Readonly = true - } - addvar(n, t, PEXTERN) - return n -} - -func isliteral(n *Node) bool { - if n.Op == OLITERAL { - if n.Val.Ctype != CTNIL { - return true - } - } - return false -} - -func simplename(n *Node) bool { - if n.Op != ONAME { - return false - } - if !n.Addable { - return false - } - if n.Class&PHEAP != 0 { - return false - } - if n.Class == PPARAMREF { - return false - } - return true -} - -func litas(l *Node, r *Node, init **NodeList) { - a := Nod(OAS, l, r) - typecheck(&a, Etop) - walkexpr(&a, init) - *init = list(*init, a) -} - -const ( - MODEDYNAM = 1 - MODECONST = 2 -) - -func getdyn(n *Node, top int) int { - mode := 0 - switch n.Op { - default: - if isliteral(n) { - return MODECONST - } - return MODEDYNAM - - case OARRAYLIT: - if top == 0 && n.Type.Bound < 0 { - return MODEDYNAM - } - fallthrough - - case OSTRUCTLIT: - break - } - - var value *Node - for nl := n.List; nl != nil; nl = nl.Next { - value = nl.N.Right - mode |= getdyn(value, 0) - if mode == MODEDYNAM|MODECONST { - break - } - } - - return mode -} - -func structlit(ctxt int, pass int, n *Node, var_ *Node, init **NodeList) { - var r *Node - var a *Node - var index *Node - var value *Node - - for nl := n.List; nl != nil; nl = nl.Next { - r = nl.N - if r.Op != OKEY { - Fatal("structlit: rhs not OKEY: %v", r) - } - index = r.Left - value = r.Right - - switch value.Op { - case OARRAYLIT: - if value.Type.Bound < 0 { - if pass == 1 && ctxt != 0 { - a = Nod(ODOT, var_, newname(index.Sym)) - slicelit(ctxt, value, a, init) - } else if pass == 2 && ctxt == 0 { - a = Nod(ODOT, var_, newname(index.Sym)) - slicelit(ctxt, value, a, init) - } else if pass == 3 { - break - } - continue - } - - a = Nod(ODOT, var_, newname(index.Sym)) - arraylit(ctxt, pass, value, a, init) - continue - - case OSTRUCTLIT: - a = Nod(ODOT, var_, newname(index.Sym)) - structlit(ctxt, pass, value, a, init) - continue - } - - if isliteral(value) { - if pass == 2 { - continue - } - } else if pass == 1 { - continue - } - - // build list of var.field = expr - a = Nod(ODOT, var_, newname(index.Sym)) - - a = Nod(OAS, a, value) - typecheck(&a, Etop) - if pass == 1 { - walkexpr(&a, init) // add any assignments in r to top - if a.Op != OAS { - Fatal("structlit: not as") - } - a.Dodata = 2 - } else { - orderstmtinplace(&a) - walkstmt(&a) - } - - *init = list(*init, a) - } -} - -func arraylit(ctxt int, pass int, n *Node, var_ *Node, init **NodeList) { - var r *Node - var a *Node - var index *Node - var value *Node - - for l := n.List; l != nil; l = l.Next { - r = l.N - if r.Op != OKEY { - Fatal("arraylit: rhs not OKEY: %v", r) - } - index = r.Left - value = r.Right - - switch value.Op { - case OARRAYLIT: - if value.Type.Bound < 0 { - if pass == 1 && ctxt != 0 { - a = Nod(OINDEX, var_, index) - slicelit(ctxt, value, a, init) - } else if pass == 2 && ctxt == 0 { - a = Nod(OINDEX, var_, index) - slicelit(ctxt, value, a, init) - } else if pass == 3 { - break - } - continue - } - - a = Nod(OINDEX, var_, index) - arraylit(ctxt, pass, value, a, init) - continue - - case OSTRUCTLIT: - a = Nod(OINDEX, var_, index) - structlit(ctxt, pass, value, a, init) - continue - } - - if isliteral(index) && isliteral(value) { - if pass == 2 { - continue - } - } else if pass == 1 { - continue - } - - // build list of var[index] = value - a = Nod(OINDEX, var_, index) - - a = Nod(OAS, a, value) - typecheck(&a, Etop) - if pass == 1 { - walkexpr(&a, init) - if a.Op != OAS { - Fatal("arraylit: not as") - } - a.Dodata = 2 - } else { - orderstmtinplace(&a) - walkstmt(&a) - } - - *init = list(*init, a) - } -} - -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.(*Mpint)) - t.Width = 0 - t.Sym = nil - t.Haspointers = 0 - dowidth(t) - - if ctxt != 0 { - // put everything into static array - vstat := staticname(t, ctxt) - - arraylit(ctxt, 1, n, vstat, init) - arraylit(ctxt, 2, n, vstat, init) - - // copy static to slice - a := Nod(OSLICE, vstat, Nod(OKEY, nil, nil)) - - a = Nod(OAS, var_, a) - typecheck(&a, Etop) - a.Dodata = 2 - *init = list(*init, a) - return - } - - // recipe for var = []t{...} - // 1. make a static array - // var vstat [...]t - // 2. assign (data statements) the constant part - // vstat = constpart{} - // 3. make an auto pointer to array and allocate heap to it - // var vauto *[...]t = new([...]t) - // 4. copy the static array to the auto array - // *vauto = vstat - // 5. assign slice of allocated heap to var - // var = [0:]*auto - // 6. for each dynamic part assign to the slice - // var[i] = dynamic part - // - // an optimization is done if there is no constant part - // 3. var vauto *[...]t = new([...]t) - // 5. var = [0:]*auto - // 6. var[i] = dynamic part - - // if the literal contains constants, - // make static initialized array (1),(2) - var vstat *Node - - mode := getdyn(n, 1) - if mode&MODECONST != 0 { - vstat = staticname(t, ctxt) - arraylit(ctxt, 1, n, vstat, init) - } - - // make new auto *array (3 declare) - vauto := temp(Ptrto(t)) - - // set auto to point at new temp or heap (3 assign) - var a *Node - if n.Alloc != nil { - // temp allocated during order.c for dddarg - n.Alloc.Type = t - - if vstat == nil { - a = Nod(OAS, n.Alloc, nil) - typecheck(&a, Etop) - *init = list(*init, a) // zero new temp - } - - a = Nod(OADDR, n.Alloc, nil) - } else if n.Esc == EscNone { - a = temp(t) - if vstat == nil { - a = Nod(OAS, temp(t), nil) - typecheck(&a, Etop) - *init = list(*init, a) // zero new temp - a = a.Left - } - - a = Nod(OADDR, a, nil) - } else { - a = Nod(ONEW, nil, nil) - a.List = list1(typenod(t)) - } - - a = Nod(OAS, vauto, a) - typecheck(&a, Etop) - walkexpr(&a, init) - *init = list(*init, a) - - if vstat != nil { - // copy static to heap (4) - a = Nod(OIND, vauto, nil) - - a = Nod(OAS, a, vstat) - typecheck(&a, Etop) - walkexpr(&a, init) - *init = list(*init, a) - } - - // make slice out of heap (5) - a = Nod(OAS, var_, Nod(OSLICE, vauto, Nod(OKEY, nil, nil))) - - typecheck(&a, Etop) - orderstmtinplace(&a) - walkstmt(&a) - *init = list(*init, a) - - // put dynamics into slice (6) - var value *Node - var r *Node - var index *Node - for l := n.List; l != nil; l = l.Next { - r = l.N - if r.Op != OKEY { - Fatal("slicelit: rhs not OKEY: %v", r) - } - index = r.Left - value = r.Right - a = Nod(OINDEX, var_, index) - a.Bounded = true - - // TODO need to check bounds? - - switch value.Op { - case OARRAYLIT: - if value.Type.Bound < 0 { - break - } - arraylit(ctxt, 2, value, a, init) - continue - - case OSTRUCTLIT: - structlit(ctxt, 2, value, a, init) - continue - } - - if isliteral(index) && isliteral(value) { - continue - } - - // build list of var[c] = expr - a = Nod(OAS, a, value) - - typecheck(&a, Etop) - orderstmtinplace(&a) - walkstmt(&a) - *init = list(*init, a) - } -} - -func maplit(ctxt int, n *Node, var_ *Node, init **NodeList) { - var r *Node - var index *Node - var value *Node - - ctxt = 0 - - // make the map var - nerr := nerrors - - a := Nod(OMAKE, nil, nil) - a.List = list1(typenod(n.Type)) - litas(var_, a, init) - - // count the initializers - b := int64(0) - - for l := n.List; l != nil; l = l.Next { - r = l.N - - if r.Op != OKEY { - Fatal("maplit: rhs not OKEY: %v", r) - } - index = r.Left - value = r.Right - - if isliteral(index) && isliteral(value) { - b++ - } - } - - if b != 0 { - // build type [count]struct { a Tindex, b Tvalue } - t := n.Type - - tk := t.Down - tv := t.Type - - symb := Lookup("b") - t = typ(TFIELD) - t.Type = tv - t.Sym = symb - - syma := Lookup("a") - t1 := t - t = typ(TFIELD) - t.Type = tk - t.Sym = syma - t.Down = t1 - - t1 = t - t = typ(TSTRUCT) - t.Type = t1 - - t1 = t - t = typ(TARRAY) - t.Bound = b - t.Type = t1 - - dowidth(t) - - // make and initialize static array - vstat := staticname(t, ctxt) - - b := int64(0) - var index *Node - var r *Node - var value *Node - for l := n.List; l != nil; l = l.Next { - r = l.N - - if r.Op != OKEY { - Fatal("maplit: rhs not OKEY: %v", r) - } - index = r.Left - value = r.Right - - if isliteral(index) && isliteral(value) { - // build vstat[b].a = key; - a = Nodintconst(b) - - a = Nod(OINDEX, vstat, a) - a = Nod(ODOT, a, newname(syma)) - a = Nod(OAS, a, index) - typecheck(&a, Etop) - walkexpr(&a, init) - a.Dodata = 2 - *init = list(*init, a) - - // build vstat[b].b = value; - a = Nodintconst(b) - - a = Nod(OINDEX, vstat, a) - a = Nod(ODOT, a, newname(symb)) - a = Nod(OAS, a, value) - typecheck(&a, Etop) - walkexpr(&a, init) - a.Dodata = 2 - *init = list(*init, a) - - b++ - } - } - - // loop adding structure elements to map - // for i = 0; i < len(vstat); i++ { - // map[vstat[i].a] = vstat[i].b - // } - index = temp(Types[TINT]) - - a = Nod(OINDEX, vstat, index) - a.Bounded = true - a = Nod(ODOT, a, newname(symb)) - - r = Nod(OINDEX, vstat, index) - r.Bounded = true - r = Nod(ODOT, r, newname(syma)) - r = Nod(OINDEX, var_, r) - - r = Nod(OAS, r, a) - - a = Nod(OFOR, nil, nil) - a.Nbody = list1(r) - - a.Ninit = list1(Nod(OAS, index, Nodintconst(0))) - a.Ntest = Nod(OLT, index, Nodintconst(t.Bound)) - a.Nincr = Nod(OAS, index, Nod(OADD, index, Nodintconst(1))) - - typecheck(&a, Etop) - walkstmt(&a) - *init = list(*init, a) - } - - // put in dynamic entries one-at-a-time - var key *Node - - var val *Node - for l := n.List; l != nil; l = l.Next { - r = l.N - - if r.Op != OKEY { - Fatal("maplit: rhs not OKEY: %v", r) - } - index = r.Left - value = r.Right - - if isliteral(index) && isliteral(value) { - continue - } - - // build list of var[c] = expr. - // use temporary so that mapassign1 can have addressable key, val. - if key == nil { - key = temp(var_.Type.Down) - val = temp(var_.Type.Type) - } - - a = Nod(OAS, key, r.Left) - typecheck(&a, Etop) - walkstmt(&a) - *init = list(*init, a) - a = Nod(OAS, val, r.Right) - typecheck(&a, Etop) - walkstmt(&a) - *init = list(*init, a) - - a = Nod(OAS, Nod(OINDEX, var_, key), val) - typecheck(&a, Etop) - walkstmt(&a) - *init = list(*init, a) - - if nerr != nerrors { - break - } - } - - if key != nil { - a = Nod(OVARKILL, key, nil) - typecheck(&a, Etop) - *init = list(*init, a) - a = Nod(OVARKILL, val, nil) - typecheck(&a, Etop) - *init = list(*init, a) - } -} - -func anylit(ctxt int, n *Node, var_ *Node, init **NodeList) { - t := n.Type - switch n.Op { - default: - Fatal("anylit: not lit") - - case OPTRLIT: - if !Isptr[t.Etype] { - Fatal("anylit: not ptr") - } - - var r *Node - if n.Right != nil { - r = Nod(OADDR, n.Right, nil) - typecheck(&r, Erv) - } else { - r = Nod(ONEW, nil, nil) - r.Typecheck = 1 - r.Type = t - r.Esc = n.Esc - } - - walkexpr(&r, init) - a := Nod(OAS, var_, r) - - typecheck(&a, Etop) - *init = list(*init, a) - - var_ = Nod(OIND, var_, nil) - typecheck(&var_, Erv|Easgn) - anylit(ctxt, n.Left, var_, init) - - case OSTRUCTLIT: - if t.Etype != TSTRUCT { - Fatal("anylit: not struct") - } - - if simplename(var_) && count(n.List) > 4 { - if ctxt == 0 { - // lay out static data - vstat := staticname(t, ctxt) - - structlit(ctxt, 1, n, vstat, init) - - // copy static to var - a := Nod(OAS, var_, vstat) - - typecheck(&a, Etop) - walkexpr(&a, init) - *init = list(*init, a) - - // add expressions to automatic - structlit(ctxt, 2, n, var_, init) - - break - } - - structlit(ctxt, 1, n, var_, init) - structlit(ctxt, 2, n, var_, init) - break - } - - // initialize of not completely specified - if simplename(var_) || count(n.List) < structcount(t) { - a := Nod(OAS, var_, nil) - typecheck(&a, Etop) - walkexpr(&a, init) - *init = list(*init, a) - } - - structlit(ctxt, 3, n, var_, init) - - case OARRAYLIT: - if t.Etype != TARRAY { - Fatal("anylit: not array") - } - if t.Bound < 0 { - slicelit(ctxt, n, var_, init) - break - } - - if simplename(var_) && count(n.List) > 4 { - if ctxt == 0 { - // lay out static data - vstat := staticname(t, ctxt) - - arraylit(1, 1, n, vstat, init) - - // copy static to automatic - a := Nod(OAS, var_, vstat) - - typecheck(&a, Etop) - walkexpr(&a, init) - *init = list(*init, a) - - // add expressions to automatic - arraylit(ctxt, 2, n, var_, init) - - break - } - - arraylit(ctxt, 1, n, var_, init) - arraylit(ctxt, 2, n, var_, init) - break - } - - // initialize of not completely specified - if simplename(var_) || int64(count(n.List)) < t.Bound { - a := Nod(OAS, var_, nil) - typecheck(&a, Etop) - walkexpr(&a, init) - *init = list(*init, a) - } - - arraylit(ctxt, 3, n, var_, init) - - case OMAPLIT: - if t.Etype != TMAP { - Fatal("anylit: not map") - } - maplit(ctxt, n, var_, init) - } -} - -func oaslit(n *Node, init **NodeList) bool { - if n.Left == nil || n.Right == nil { - // not a special composit literal assignment - return false - } - if n.Left.Type == nil || n.Right.Type == nil { - // not a special composit literal assignment - return false - } - if !simplename(n.Left) { - // not a special composit literal assignment - return false - } - if !Eqtype(n.Left.Type, n.Right.Type) { - // not a special composit literal assignment - return false - } - - // context is init() function. - // implies generated data executed - // exactly once and not subject to races. - ctxt := 0 - - // if(n->dodata == 1) - // ctxt = 1; - - switch n.Right.Op { - default: - // not a special composit literal assignment - return false - - case OSTRUCTLIT, OARRAYLIT, OMAPLIT: - if vmatch1(n.Left, n.Right) { - // not a special composit literal assignment - return false - } - anylit(ctxt, n.Right, n.Left, init) - } - - n.Op = OEMPTY - return true -} - -func getlit(lit *Node) int { - if Smallintconst(lit) { - return int(Mpgetfix(lit.Val.U.(*Mpint))) - } - return -1 -} - -func stataddr(nam *Node, n *Node) bool { - if n == nil { - return false - } - - switch n.Op { - case ONAME: - *nam = *n - return n.Addable - - case ODOT: - if !stataddr(nam, n.Left) { - break - } - nam.Xoffset += n.Xoffset - nam.Type = n.Type - return true - - case OINDEX: - if n.Left.Type.Bound < 0 { - break - } - if !stataddr(nam, n.Left) { - break - } - l := getlit(n.Right) - if l < 0 { - break - } - - // Check for overflow. - if n.Type.Width != 0 && Thearch.MAXWIDTH/n.Type.Width <= int64(l) { - break - } - nam.Xoffset += int64(l) * n.Type.Width - nam.Type = n.Type - return true - } - - return false -} - -func initplan(n *Node) { - if n.Initplan != nil { - return - } - p := new(InitPlan) - n.Initplan = p - switch n.Op { - default: - Fatal("initplan") - - case OARRAYLIT: - var a *Node - for l := n.List; l != nil; l = l.Next { - a = l.N - if a.Op != OKEY || !Smallintconst(a.Left) { - Fatal("initplan arraylit") - } - addvalue(p, n.Type.Type.Width*Mpgetfix(a.Left.Val.U.(*Mpint)), nil, a.Right) - } - - case OSTRUCTLIT: - var a *Node - for l := n.List; l != nil; l = l.Next { - a = l.N - if a.Op != OKEY || a.Left.Type == nil { - Fatal("initplan structlit") - } - addvalue(p, a.Left.Type.Width, nil, a.Right) - } - - case OMAPLIT: - var a *Node - for l := n.List; l != nil; l = l.Next { - a = l.N - if a.Op != OKEY { - Fatal("initplan maplit") - } - addvalue(p, -1, a.Left, a.Right) - } - } -} - -func addvalue(p *InitPlan, xoffset int64, key *Node, n *Node) { - // special case: zero can be dropped entirely - if iszero(n) { - p.Zero += n.Type.Width - return - } - - // special case: inline struct and array (not slice) literals - if isvaluelit(n) { - initplan(n) - q := n.Initplan - var e *InitEntry - for i := 0; i < len(q.E); i++ { - e = entry(p) - *e = q.E[i] - e.Xoffset += xoffset - } - - return - } - - // add to plan - if n.Op == OLITERAL { - p.Lit += n.Type.Width - } else { - p.Expr += n.Type.Width - } - - e := entry(p) - e.Xoffset = xoffset - e.Expr = n -} - -func iszero(n *Node) bool { - switch n.Op { - case OLITERAL: - switch n.Val.Ctype { - default: - Dump("unexpected literal", n) - Fatal("iszero") - - case CTNIL: - return true - - case CTSTR: - return n.Val.U.(string) == "" - - case CTBOOL: - return !n.Val.U.(bool) - - case CTINT, CTRUNE: - return mpcmpfixc(n.Val.U.(*Mpint), 0) == 0 - - case CTFLT: - return mpcmpfltc(n.Val.U.(*Mpflt), 0) == 0 - - case CTCPLX: - return mpcmpfltc(&n.Val.U.(*Mpcplx).Real, 0) == 0 && mpcmpfltc(&n.Val.U.(*Mpcplx).Imag, 0) == 0 - } - - case OARRAYLIT: - if Isslice(n.Type) { - break - } - fallthrough - - // fall through - case OSTRUCTLIT: - for l := n.List; l != nil; l = l.Next { - if !iszero(l.N.Right) { - return false - } - } - return true - } - - return false -} - -func isvaluelit(n *Node) bool { - return (n.Op == OARRAYLIT && Isfixedarray(n.Type)) || n.Op == OSTRUCTLIT -} - -func entry(p *InitPlan) *InitEntry { - p.E = append(p.E, InitEntry{}) - return &p.E[len(p.E)-1] -} - -func gen_as_init(n *Node) bool { - var nr *Node - var nl *Node - var nam Node - - if n.Dodata == 0 { - goto no - } - - nr = n.Right - nl = n.Left - if nr == nil { - var nam Node - if !stataddr(&nam, nl) { - goto no - } - if nam.Class != PEXTERN { - goto no - } - return true - } - - if nr.Type == nil || !Eqtype(nl.Type, nr.Type) { - goto no - } - - if !stataddr(&nam, nl) { - goto no - } - - if nam.Class != PEXTERN { - goto no - } - - switch nr.Op { - default: - goto no - - case OCONVNOP: - nr = nr.Left - if nr == nil || nr.Op != OSLICEARR { - goto no - } - fallthrough - - // fall through - case OSLICEARR: - if nr.Right.Op == OKEY && nr.Right.Left == nil && nr.Right.Right == nil { - nr = nr.Left - gused(nil) // in case the data is the dest of a goto - nl := nr - if nr == nil || nr.Op != OADDR { - goto no - } - nr = nr.Left - if nr == nil || nr.Op != ONAME { - goto no - } - - // nr is the array being converted to a slice - if nr.Type == nil || nr.Type.Etype != TARRAY || nr.Type.Bound < 0 { - goto no - } - - nam.Xoffset += int64(Array_array) - gdata(&nam, nl, int(Types[Tptr].Width)) - - nam.Xoffset += int64(Array_nel) - int64(Array_array) - var nod1 Node - Nodconst(&nod1, Types[TINT], nr.Type.Bound) - gdata(&nam, &nod1, Widthint) - - nam.Xoffset += int64(Array_cap) - int64(Array_nel) - gdata(&nam, &nod1, Widthint) - - return true - } - - goto no - - case OLITERAL: - break - } - - switch nr.Type.Etype { - default: - goto no - - case TBOOL, - TINT8, - TUINT8, - TINT16, - TUINT16, - TINT32, - TUINT32, - TINT64, - TUINT64, - TINT, - TUINT, - TUINTPTR, - TPTR32, - TPTR64, - TFLOAT32, - TFLOAT64: - gdata(&nam, nr, int(nr.Type.Width)) - - case TCOMPLEX64, TCOMPLEX128: - gdatacomplex(&nam, nr.Val.U.(*Mpcplx)) - - case TSTRING: - gdatastring(&nam, nr.Val.U.(string)) - } - - return true - -no: - if n.Dodata == 2 { - Dump("\ngen_as_init", n) - Fatal("gen_as_init couldnt make data statement") - } - - return false -} diff --git a/src/cmd/internal/gc/subr.go b/src/cmd/internal/gc/subr.go deleted file mode 100644 index b10a6b3d3d..0000000000 --- a/src/cmd/internal/gc/subr.go +++ /dev/null @@ -1,3571 +0,0 @@ -// Copyright 2009 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 gc - -import ( - "bytes" - "cmd/internal/obj" - "crypto/md5" - "encoding/binary" - "fmt" - "os" - "sort" - "strings" - "unicode" - "unicode/utf8" -) - -type Error struct { - lineno int - seq int - msg string -} - -var errors []Error - -func errorexit() { - Flusherrors() - if outfile != "" { - os.Remove(outfile) - } - os.Exit(2) -} - -func parserline() int { - if parsing && theparser.Lookahead() > 0 { - // parser has one symbol lookahead - return int(prevlineno) - } - return int(lineno) -} - -func adderrorname(n *Node) { - if n.Op != ODOT { - return - } - old := fmt.Sprintf("%v: undefined: %v\n", n.Line(), n.Left) - if len(errors) > 0 && int32(errors[len(errors)-1].lineno) == n.Lineno && errors[len(errors)-1].msg == old { - errors[len(errors)-1].msg = fmt.Sprintf("%v: undefined: %v in %v\n", n.Line(), n.Left, n) - } -} - -func adderr(line int, format string, args ...interface{}) { - errors = append(errors, Error{ - seq: len(errors), - lineno: line, - msg: fmt.Sprintf("%v: %s\n", Ctxt.Line(line), fmt.Sprintf(format, args...)), - }) -} - -type errcmp []Error - -func (x errcmp) Len() int { - return len(x) -} - -func (x errcmp) Swap(i, j int) { - x[i], x[j] = x[j], x[i] -} - -func (x errcmp) Less(i, j int) bool { - a := &x[i] - b := &x[j] - if a.lineno != b.lineno { - return a.lineno-b.lineno < 0 - } - if a.seq != b.seq { - return a.seq-b.seq < 0 - } - return stringsCompare(a.msg, b.msg) < 0 -} - -func Flusherrors() { - bstdout.Flush() - if len(errors) == 0 { - return - } - sort.Sort(errcmp(errors[:len(errors)])) - for i := 0; i < len(errors); i++ { - if i == 0 || errors[i].msg != errors[i-1].msg { - fmt.Printf("%s", errors[i].msg) - } - } - errors = errors[:0] -} - -func hcrash() { - if Debug['h'] != 0 { - Flusherrors() - if outfile != "" { - os.Remove(outfile) - } - var x *int - *x = 0 - } -} - -func yyerrorl(line int, format string, args ...interface{}) { - adderr(line, format, args...) - - hcrash() - nerrors++ - if nsavederrors+nerrors >= 10 && Debug['e'] == 0 { - Flusherrors() - fmt.Printf("%v: too many errors\n", Ctxt.Line(line)) - errorexit() - } -} - -var yyerror_lastsyntax int - -func Yyerror(format string, args ...interface{}) { - msg := fmt.Sprintf(format, args...) - if strings.HasPrefix(msg, "syntax error") { - nsyntaxerrors++ - - // An unexpected EOF caused a syntax error. Use the previous - // line number since getc generated a fake newline character. - if curio.eofnl != 0 { - lexlineno = prevlineno - } - - // only one syntax error per line - if int32(yyerror_lastsyntax) == lexlineno { - return - } - yyerror_lastsyntax = int(lexlineno) - - // plain "syntax error" gets "near foo" added - if msg == "syntax error" { - yyerrorl(int(lexlineno), "syntax error near %s", lexbuf.String()) - return - } - - // The grammar has { and LBRACE but both show up as {. - // Rewrite syntax error referring to "{ or {" to say just "{". - // The grammar has ? and @ but only for reading imports. - // Silence them in ordinary errors. - msg = strings.Replace(msg, "{ or {", "{", -1) - msg = strings.Replace(msg, " or ?", "", -1) - msg = strings.Replace(msg, " or @", "", -1) - - msg = strings.Replace(msg, "LLITERAL", litbuf, -1) - - yyerrorl(int(lexlineno), "%s", msg) - return - } - - adderr(parserline(), "%s", msg) - - hcrash() - nerrors++ - if nsavederrors+nerrors >= 10 && Debug['e'] == 0 { - Flusherrors() - fmt.Printf("%v: too many errors\n", Ctxt.Line(parserline())) - errorexit() - } -} - -func Warn(fmt_ string, args ...interface{}) { - adderr(parserline(), fmt_, args...) - - hcrash() -} - -func Warnl(line int, fmt_ string, args ...interface{}) { - adderr(line, fmt_, args...) - if Debug['m'] != 0 { - Flusherrors() - } -} - -func Fatal(fmt_ string, args ...interface{}) { - Flusherrors() - - fmt.Printf("%v: internal compiler error: ", Ctxt.Line(int(lineno))) - fmt.Printf(fmt_, args...) - fmt.Printf("\n") - - // If this is a released compiler version, ask for a bug report. - if strings.HasPrefix(obj.Getgoversion(), "release") { - fmt.Printf("\n") - fmt.Printf("Please file a bug report including a short program that triggers the error.\n") - fmt.Printf("https://golang.org/issue/new\n") - } - - hcrash() - errorexit() -} - -func linehistpragma(file string) { - if Debug['i'] != 0 { - fmt.Printf("pragma %s at line %v\n", file, Ctxt.Line(int(lexlineno))) - } - Ctxt.AddImport(file) -} - -func linehistpush(file string) { - if Debug['i'] != 0 { - fmt.Printf("import %s at line %v\n", file, Ctxt.Line(int(lexlineno))) - } - 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 { - lno := lineno - if n != nil { - switch n.Op { - case ONAME, OTYPE, OPACK, OLITERAL: - break - - default: - lineno = n.Lineno - if lineno == 0 { - if Debug['K'] != 0 { - Warn("setlineno: line 0") - } - lineno = lno - } - } - } - - return lno -} - -func Lookup(name string) *Sym { - return localpkg.Lookup(name) -} - -func Lookupf(format string, a ...interface{}) *Sym { - return Lookup(fmt.Sprintf(format, a...)) -} - -func LookupBytes(name []byte) *Sym { - return localpkg.LookupBytes(name) -} - -var initSyms []*Sym - -var nopkg = &Pkg{ - Syms: make(map[string]*Sym), -} - -func (pkg *Pkg) Lookup(name string) *Sym { - if pkg == nil { - pkg = nopkg - } - if s := pkg.Syms[name]; s != nil { - return s - } - - s := &Sym{ - Name: name, - Pkg: pkg, - Lexical: LNAME, - } - if name == "init" { - initSyms = append(initSyms, s) - } - pkg.Syms[name] = s - return s -} - -func (pkg *Pkg) LookupBytes(name []byte) *Sym { - if pkg == nil { - pkg = nopkg - } - if s := pkg.Syms[string(name)]; s != nil { - return s - } - str := internString(name) - return pkg.Lookup(str) -} - -func Pkglookup(name string, pkg *Pkg) *Sym { - return pkg.Lookup(name) -} - -func restrictlookup(name string, pkg *Pkg) *Sym { - if !exportname(name) && pkg != localpkg { - Yyerror("cannot refer to unexported name %s.%s", pkg.Name, name) - } - return Pkglookup(name, pkg) -} - -// find all the exported symbols in package opkg -// and make them available in the current package -func importdot(opkg *Pkg, pack *Node) { - var s1 *Sym - var pkgerror string - - n := 0 - for _, s := range opkg.Syms { - if s.Def == nil { - continue - } - if !exportname(s.Name) || strings.ContainsRune(s.Name, 0xb7) { // 0xb7 = center dot - continue - } - s1 = Lookup(s.Name) - if s1.Def != nil { - pkgerror = fmt.Sprintf("during import %q", opkg.Path) - redeclare(s1, pkgerror) - continue - } - - s1.Def = s.Def - s1.Block = s.Block - s1.Def.Pack = pack - s1.Origpkg = opkg - n++ - } - - if n == 0 { - // can't possibly be used - there were no symbols - yyerrorl(int(pack.Lineno), "imported and not used: %q", opkg.Path) - } -} - -func gethunk() { - nh := int32(NHUNK) - if thunk >= 10*NHUNK { - nh = 10 * NHUNK - } - h := string(make([]byte, nh)) - if h == "" { - Flusherrors() - Yyerror("out of memory") - errorexit() - } - - hunk = h - nhunk = nh - thunk += nh -} - -func Nod(op int, nleft *Node, nright *Node) *Node { - n := new(Node) - n.Op = uint8(op) - n.Left = nleft - n.Right = nright - n.Lineno = int32(parserline()) - n.Xoffset = BADWIDTH - n.Orig = n - n.Curfn = Curfn - 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 -} - -func saveorignode(n *Node) { - if n.Orig != nil { - return - } - norig := Nod(int(n.Op), nil, nil) - *norig = *n - n.Orig = norig -} - -// ispaddedfield reports whether the given field -// is followed by padding. For the case where t is -// the last field, total gives the size of the enclosing struct. -func ispaddedfield(t *Type, total int64) bool { - if t.Etype != TFIELD { - Fatal("ispaddedfield called non-field %v", t) - } - if t.Down == nil { - return t.Width+t.Type.Width != total - } - return t.Width+t.Type.Width != t.Down.Width -} - -func algtype1(t *Type, bad **Type) int { - if bad != nil { - *bad = nil - } - if t.Broke != 0 { - return AMEM - } - if t.Noalg != 0 { - return ANOEQ - } - - switch t.Etype { - // will be defined later. - case TANY, TFORW: - *bad = t - - return -1 - - case TINT8, - TUINT8, - TINT16, - TUINT16, - TINT32, - TUINT32, - TINT64, - TUINT64, - TINT, - TUINT, - TUINTPTR, - TBOOL, - TPTR32, - TPTR64, - TCHAN, - TUNSAFEPTR: - return AMEM - - case TFUNC, TMAP: - if bad != nil { - *bad = t - } - return ANOEQ - - case TFLOAT32: - return AFLOAT32 - - case TFLOAT64: - return AFLOAT64 - - case TCOMPLEX64: - return ACPLX64 - - case TCOMPLEX128: - return ACPLX128 - - case TSTRING: - return ASTRING - - case TINTER: - if isnilinter(t) { - return ANILINTER - } - return AINTER - - case TARRAY: - if Isslice(t) { - if bad != nil { - *bad = t - } - return ANOEQ - } - - a := algtype1(t.Type, bad) - if a == ANOEQ || a == AMEM { - if a == ANOEQ && bad != nil { - *bad = t - } - return a - } - - return -1 // needs special compare - - case TSTRUCT: - if t.Type != nil && t.Type.Down == nil && !isblanksym(t.Type.Sym) { - // One-field struct is same as that one field alone. - return algtype1(t.Type.Type, bad) - } - - ret := AMEM - var a int - for t1 := t.Type; t1 != nil; t1 = t1.Down { - // All fields must be comparable. - a = algtype1(t1.Type, bad) - - if a == ANOEQ { - return ANOEQ - } - - // Blank fields, padded fields, fields with non-memory - // equality need special compare. - if a != AMEM || isblanksym(t1.Sym) || ispaddedfield(t1, t.Width) { - ret = -1 - continue - } - } - - return ret - } - - Fatal("algtype1: unexpected type %v", t) - return 0 -} - -func algtype(t *Type) int { - a := algtype1(t, nil) - if a == AMEM || a == ANOEQ { - if Isslice(t) { - return ASLICE - } - switch t.Width { - case 0: - return a + AMEM0 - AMEM - - case 1: - return a + AMEM8 - AMEM - - case 2: - return a + AMEM16 - AMEM - - case 4: - return a + AMEM32 - AMEM - - case 8: - return a + AMEM64 - AMEM - - case 16: - return a + AMEM128 - AMEM - } - } - - return a -} - -func maptype(key *Type, val *Type) *Type { - if key != nil { - var bad *Type - atype := algtype1(key, &bad) - var mtype int - if bad == nil { - mtype = int(key.Etype) - } else { - mtype = int(bad.Etype) - } - switch mtype { - default: - if atype == ANOEQ { - Yyerror("invalid map key type %v", key) - } - - // will be resolved later. - case TANY: - break - - // map[key] used during definition of key. - // postpone check until key is fully defined. - // if there are multiple uses of map[key] - // before key is fully defined, the error - // will only be printed for the first one. - // good enough. - case TFORW: - if key.Maplineno == 0 { - key.Maplineno = lineno - } - } - } - - t := typ(TMAP) - t.Down = key - t.Type = val - return t -} - -func typ(et int) *Type { - t := new(Type) - t.Etype = uint8(et) - t.Width = BADWIDTH - t.Lineno = int(lineno) - t.Orig = t - return t -} - -type methcmp []*Type - -func (x methcmp) Len() int { - return len(x) -} - -func (x methcmp) Swap(i, j int) { - x[i], x[j] = x[j], x[i] -} - -func (x methcmp) Less(i, j int) bool { - a := x[i] - b := x[j] - if a.Sym == nil && b.Sym == nil { - return false - } - if a.Sym == nil { - return true - } - if b.Sym == nil { - return 1 < 0 - } - k := stringsCompare(a.Sym.Name, b.Sym.Name) - if k != 0 { - return k < 0 - } - if !exportname(a.Sym.Name) { - k := stringsCompare(a.Sym.Pkg.Path, b.Sym.Pkg.Path) - if k != 0 { - return k < 0 - } - } - - return false -} - -func sortinter(t *Type) *Type { - if t.Type == nil || t.Type.Down == nil { - return t - } - - i := 0 - for f := t.Type; f != nil; f = f.Down { - i++ - } - a := make([]*Type, i) - i = 0 - var f *Type - for f = t.Type; f != nil; f = f.Down { - a[i] = f - i++ - } - sort.Sort(methcmp(a[:i])) - for { - tmp11 := i - i-- - if tmp11 <= 0 { - break - } - a[i].Down = f - f = a[i] - } - - t.Type = f - return t -} - -func Nodintconst(v int64) *Node { - c := Nod(OLITERAL, nil, nil) - c.Addable = true - c.Val.U = new(Mpint) - Mpmovecfix(c.Val.U.(*Mpint), v) - c.Val.Ctype = CTINT - c.Type = Types[TIDEAL] - ullmancalc(c) - return c -} - -func nodfltconst(v *Mpflt) *Node { - c := Nod(OLITERAL, nil, nil) - c.Addable = true - c.Val.U = newMpflt() - mpmovefltflt(c.Val.U.(*Mpflt), v) - c.Val.Ctype = CTFLT - c.Type = Types[TIDEAL] - ullmancalc(c) - return c -} - -func Nodconst(n *Node, t *Type, v int64) { - *n = Node{} - n.Op = OLITERAL - n.Addable = true - ullmancalc(n) - n.Val.U = new(Mpint) - Mpmovecfix(n.Val.U.(*Mpint), v) - n.Val.Ctype = CTINT - n.Type = t - - if Isfloat[t.Etype] { - Fatal("nodconst: bad type %v", t) - } -} - -func nodnil() *Node { - c := Nodintconst(0) - c.Val.Ctype = CTNIL - c.Type = Types[TNIL] - return c -} - -func Nodbool(b bool) *Node { - c := Nodintconst(0) - c.Val.Ctype = CTBOOL - c.Val.U = b - c.Type = idealbool - return c -} - -func aindex(b *Node, t *Type) *Type { - bound := int64(-1) // open bound - typecheck(&b, Erv) - if b != nil { - switch consttype(b) { - default: - Yyerror("array bound must be an integer expression") - - case CTINT, CTRUNE: - bound = Mpgetfix(b.Val.U.(*Mpint)) - if bound < 0 { - Yyerror("array bound must be non negative") - } - } - } - - // fixed array - r := typ(TARRAY) - - r.Type = t - r.Bound = bound - return r -} - -func treecopy(n *Node) *Node { - if n == nil { - return nil - } - - var m *Node - switch n.Op { - default: - 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) - if m.Defn != nil { - panic("abort") - } - - case ONONAME: - if n.Sym == Lookup("iota") { - // Not sure yet whether this is the real iota, - // but make a copy of the Node* just in case, - // so that all the copies of this const definition - // don't have the same iota value. - m = Nod(OXXX, nil, nil) - - *m = *n - m.Iota = iota_ - break - } - fallthrough - - case ONAME, OLITERAL, OTYPE: - m = n - } - - return m -} - -func isnil(n *Node) bool { - if n == nil { - return false - } - if n.Op != OLITERAL { - return false - } - if n.Val.Ctype != CTNIL { - return false - } - return true -} - -func isptrto(t *Type, et int) bool { - if t == nil { - return false - } - if !Isptr[t.Etype] { - return false - } - t = t.Type - if t == nil { - return false - } - if int(t.Etype) != et { - return false - } - return true -} - -func Istype(t *Type, et int) bool { - return t != nil && int(t.Etype) == et -} - -func Isfixedarray(t *Type) bool { - return t != nil && t.Etype == TARRAY && t.Bound >= 0 -} - -func Isslice(t *Type) bool { - return t != nil && t.Etype == TARRAY && t.Bound < 0 -} - -func isblank(n *Node) bool { - if n == nil { - return false - } - return isblanksym(n.Sym) -} - -func isblanksym(s *Sym) bool { - return s != nil && s.Name == "_" -} - -func Isinter(t *Type) bool { - return t != nil && t.Etype == TINTER -} - -func isnilinter(t *Type) bool { - if !Isinter(t) { - return false - } - if t.Type != nil { - return false - } - return true -} - -func isideal(t *Type) bool { - if t == nil { - return false - } - if t == idealstring || t == idealbool { - return true - } - switch t.Etype { - case TNIL, TIDEAL: - return true - } - - return false -} - -/* - * given receiver of type t (t == r or t == *r) - * return type to hang methods off (r). - */ -func methtype(t *Type, mustname int) *Type { - if t == nil { - return nil - } - - // strip away pointer if it's there - if Isptr[t.Etype] { - if t.Sym != nil { - return nil - } - t = t.Type - if t == nil { - return nil - } - } - - // need a type name - if t.Sym == nil && (mustname != 0 || t.Etype != TSTRUCT) { - return nil - } - - // check types - if !issimple[t.Etype] { - switch t.Etype { - default: - return nil - - case TSTRUCT, - TARRAY, - TMAP, - TCHAN, - TSTRING, - TFUNC: - break - } - } - - return t -} - -func cplxsubtype(et int) int { - switch et { - case TCOMPLEX64: - return TFLOAT32 - - case TCOMPLEX128: - return TFLOAT64 - } - - Fatal("cplxsubtype: %v\n", Econv(int(et), 0)) - return 0 -} - -func eqnote(a, b *string) bool { - return a == b || a != nil && b != nil && *a == *b -} - -type TypePairList struct { - t1 *Type - t2 *Type - next *TypePairList -} - -func onlist(l *TypePairList, t1 *Type, t2 *Type) bool { - for ; l != nil; l = l.next { - if (l.t1 == t1 && l.t2 == t2) || (l.t1 == t2 && l.t2 == t1) { - return true - } - } - return false -} - -// Return 1 if t1 and t2 are identical, following the spec rules. -// -// Any cyclic type must go through a named type, and if one is -// named, it is only identical to the other if they are the same -// pointer (t1 == t2), so there's no chance of chasing cycles -// ad infinitum, so no need for a depth counter. -func Eqtype(t1 *Type, t2 *Type) bool { - return eqtype1(t1, t2, nil) -} - -func eqtype1(t1 *Type, t2 *Type, assumed_equal *TypePairList) bool { - if t1 == t2 { - return true - } - if t1 == nil || t2 == nil || t1.Etype != t2.Etype { - return false - } - if t1.Sym != nil || t2.Sym != nil { - // Special case: we keep byte and uint8 separate - // for error messages. Treat them as equal. - switch t1.Etype { - case TUINT8: - if (t1 == Types[TUINT8] || t1 == bytetype) && (t2 == Types[TUINT8] || t2 == bytetype) { - return true - } - - case TINT, TINT32: - if (t1 == Types[runetype.Etype] || t1 == runetype) && (t2 == Types[runetype.Etype] || t2 == runetype) { - return true - } - } - - return false - } - - if onlist(assumed_equal, t1, t2) { - return true - } - var l TypePairList - l.next = assumed_equal - l.t1 = t1 - l.t2 = t2 - - switch t1.Etype { - case TINTER, TSTRUCT: - t1 = t1.Type - t2 = t2.Type - for ; t1 != nil && t2 != nil; t1, t2 = t1.Down, t2.Down { - if t1.Etype != TFIELD || t2.Etype != TFIELD { - Fatal("struct/interface missing field: %v %v", t1, t2) - } - if t1.Sym != t2.Sym || t1.Embedded != t2.Embedded || !eqtype1(t1.Type, t2.Type, &l) || !eqnote(t1.Note, t2.Note) { - return false - } - } - - if t1 == nil && t2 == nil { - return true - } - return false - - // Loop over structs: receiver, in, out. - case TFUNC: - t1 = t1.Type - t2 = t2.Type - for ; t1 != nil && t2 != nil; t1, t2 = t1.Down, t2.Down { - if t1.Etype != TSTRUCT || t2.Etype != TSTRUCT { - Fatal("func missing struct: %v %v", t1, t2) - } - - // Loop over fields in structs, ignoring argument names. - ta := t1.Type - tb := t2.Type - for ; ta != nil && tb != nil; ta, tb = ta.Down, tb.Down { - if ta.Etype != TFIELD || tb.Etype != TFIELD { - Fatal("func struct missing field: %v %v", ta, tb) - } - if ta.Isddd != tb.Isddd || !eqtype1(ta.Type, tb.Type, &l) { - return false - } - } - - if ta != nil || tb != nil { - return false - } - } - - if t1 == nil && t2 == nil { - return true - } - return false - - case TARRAY: - if t1.Bound != t2.Bound { - return false - } - - case TCHAN: - if t1.Chan != t2.Chan { - return false - } - } - - if eqtype1(t1.Down, t2.Down, &l) && eqtype1(t1.Type, t2.Type, &l) { - return true - } - return false -} - -// Are t1 and t2 equal struct types when field names are ignored? -// For deciding whether the result struct from g can be copied -// directly when compiling f(g()). -func eqtypenoname(t1 *Type, t2 *Type) bool { - if t1 == nil || t2 == nil || t1.Etype != TSTRUCT || t2.Etype != TSTRUCT { - return false - } - - t1 = t1.Type - t2 = t2.Type - for { - if !Eqtype(t1, t2) { - return false - } - if t1 == nil { - return true - } - t1 = t1.Down - t2 = t2.Down - } -} - -// Is type src assignment compatible to type dst? -// If so, return op code to use in conversion. -// If not, return 0. -func assignop(src *Type, dst *Type, why *string) int { - if why != nil { - *why = "" - } - - // TODO(rsc,lvd): This behaves poorly in the presence of inlining. - // https://golang.org/issue/2795 - if safemode != 0 && importpkg == nil && src != nil && src.Etype == TUNSAFEPTR { - Yyerror("cannot use unsafe.Pointer") - errorexit() - } - - if src == dst { - return OCONVNOP - } - if src == nil || dst == nil || src.Etype == TFORW || dst.Etype == TFORW || src.Orig == nil || dst.Orig == nil { - return 0 - } - - // 1. src type is identical to dst. - if Eqtype(src, dst) { - return OCONVNOP - } - - // 2. src and dst have identical underlying types - // and either src or dst is not a named type or - // both are empty interface types. - // For assignable but different non-empty interface types, - // we want to recompute the itab. - if Eqtype(src.Orig, dst.Orig) && (src.Sym == nil || dst.Sym == nil || isnilinter(src)) { - return OCONVNOP - } - - // 3. dst is an interface type and src implements dst. - if dst.Etype == TINTER && src.Etype != TNIL { - var missing *Type - var ptr int - var have *Type - if implements(src, dst, &missing, &have, &ptr) { - return OCONVIFACE - } - - // we'll have complained about this method anyway, suppress spurious messages. - if have != nil && have.Sym == missing.Sym && (have.Type.Broke != 0 || missing.Type.Broke != 0) { - return OCONVIFACE - } - - if why != nil { - if isptrto(src, TINTER) { - *why = fmt.Sprintf(":\n\t%v is pointer to interface, not interface", src) - } else if have != nil && have.Sym == missing.Sym && have.Nointerface { - *why = fmt.Sprintf(":\n\t%v does not implement %v (%v method is marked 'nointerface')", src, dst, missing.Sym) - } else if have != nil && have.Sym == missing.Sym { - *why = fmt.Sprintf(":\n\t%v does not implement %v (wrong type for %v method)\n"+"\t\thave %v%v\n\t\twant %v%v", src, dst, missing.Sym, have.Sym, Tconv(have.Type, obj.FmtShort|obj.FmtByte), missing.Sym, Tconv(missing.Type, obj.FmtShort|obj.FmtByte)) - } else if ptr != 0 { - *why = fmt.Sprintf(":\n\t%v does not implement %v (%v method has pointer receiver)", src, dst, missing.Sym) - } else if have != nil { - *why = fmt.Sprintf(":\n\t%v does not implement %v (missing %v method)\n"+"\t\thave %v%v\n\t\twant %v%v", src, dst, missing.Sym, have.Sym, Tconv(have.Type, obj.FmtShort|obj.FmtByte), missing.Sym, Tconv(missing.Type, obj.FmtShort|obj.FmtByte)) - } else { - *why = fmt.Sprintf(":\n\t%v does not implement %v (missing %v method)", src, dst, missing.Sym) - } - } - - return 0 - } - - if isptrto(dst, TINTER) { - if why != nil { - *why = fmt.Sprintf(":\n\t%v is pointer to interface, not interface", dst) - } - return 0 - } - - if src.Etype == TINTER && dst.Etype != TBLANK { - var have *Type - var ptr int - var missing *Type - if why != nil && implements(dst, src, &missing, &have, &ptr) { - *why = ": need type assertion" - } - return 0 - } - - // 4. src is a bidirectional channel value, dst is a channel type, - // src and dst have identical element types, and - // either src or dst is not a named type. - if src.Etype == TCHAN && src.Chan == Cboth && dst.Etype == TCHAN { - if Eqtype(src.Type, dst.Type) && (src.Sym == nil || dst.Sym == nil) { - return OCONVNOP - } - } - - // 5. src is the predeclared identifier nil and dst is a nillable type. - if src.Etype == TNIL { - switch dst.Etype { - case TARRAY: - if dst.Bound != -100 { // not slice - break - } - fallthrough - - case TPTR32, - TPTR64, - TFUNC, - TMAP, - TCHAN, - TINTER: - return OCONVNOP - } - } - - // 6. rule about untyped constants - already converted by defaultlit. - - // 7. Any typed value can be assigned to the blank identifier. - if dst.Etype == TBLANK { - return OCONVNOP - } - - return 0 -} - -// Can we convert a value of type src to a value of type dst? -// If so, return op code to use in conversion (maybe OCONVNOP). -// If not, return 0. -func convertop(src *Type, dst *Type, why *string) int { - if why != nil { - *why = "" - } - - if src == dst { - return OCONVNOP - } - if src == nil || dst == nil { - return 0 - } - - // 1. src can be assigned to dst. - op := assignop(src, dst, why) - if op != 0 { - return op - } - - // The rules for interfaces are no different in conversions - // than assignments. If interfaces are involved, stop now - // with the good message from assignop. - // Otherwise clear the error. - if src.Etype == TINTER || dst.Etype == TINTER { - return 0 - } - if why != nil { - *why = "" - } - - // 2. src and dst have identical underlying types. - if Eqtype(src.Orig, dst.Orig) { - return OCONVNOP - } - - // 3. src and dst are unnamed pointer types - // and their base types have identical underlying types. - if Isptr[src.Etype] && Isptr[dst.Etype] && src.Sym == nil && dst.Sym == nil { - if Eqtype(src.Type.Orig, dst.Type.Orig) { - return OCONVNOP - } - } - - // 4. src and dst are both integer or floating point types. - if (Isint[src.Etype] || Isfloat[src.Etype]) && (Isint[dst.Etype] || Isfloat[dst.Etype]) { - if Simtype[src.Etype] == Simtype[dst.Etype] { - return OCONVNOP - } - return OCONV - } - - // 5. src and dst are both complex types. - if Iscomplex[src.Etype] && Iscomplex[dst.Etype] { - if Simtype[src.Etype] == Simtype[dst.Etype] { - return OCONVNOP - } - return OCONV - } - - // 6. src is an integer or has type []byte or []rune - // and dst is a string type. - if Isint[src.Etype] && dst.Etype == TSTRING { - return ORUNESTR - } - - if Isslice(src) && dst.Etype == TSTRING { - if src.Type.Etype == bytetype.Etype { - return OARRAYBYTESTR - } - if src.Type.Etype == runetype.Etype { - return OARRAYRUNESTR - } - } - - // 7. src is a string and dst is []byte or []rune. - // String to slice. - if src.Etype == TSTRING && Isslice(dst) { - if dst.Type.Etype == bytetype.Etype { - return OSTRARRAYBYTE - } - if dst.Type.Etype == runetype.Etype { - return OSTRARRAYRUNE - } - } - - // 8. src is a pointer or uintptr and dst is unsafe.Pointer. - if (Isptr[src.Etype] || src.Etype == TUINTPTR) && dst.Etype == TUNSAFEPTR { - return OCONVNOP - } - - // 9. src is unsafe.Pointer and dst is a pointer or uintptr. - if src.Etype == TUNSAFEPTR && (Isptr[dst.Etype] || dst.Etype == TUINTPTR) { - return OCONVNOP - } - - return 0 -} - -func assignconv(n *Node, t *Type, context string) *Node { - return assignconvfn(n, t, func() string { return context }) -} - -// Convert node n for assignment to type t. -func assignconvfn(n *Node, t *Type, context func() string) *Node { - if n == nil || n.Type == nil || n.Type.Broke != 0 { - return n - } - - if t.Etype == TBLANK && n.Type.Etype == TNIL { - Yyerror("use of untyped nil") - } - - old := n - old.Diag++ // silence errors about n; we'll issue one below - defaultlit(&n, t) - old.Diag-- - if t.Etype == TBLANK { - return n - } - - // Convert ideal bool from comparison to plain bool - // if the next step is non-bool (like interface{}). - if n.Type == idealbool && t.Etype != TBOOL { - if n.Op == ONAME || n.Op == OLITERAL { - r := Nod(OCONVNOP, n, nil) - r.Type = Types[TBOOL] - r.Typecheck = 1 - r.Implicit = true - n = r - } - } - - if Eqtype(n.Type, t) { - return n - } - - var why string - op := assignop(n.Type, t, &why) - if op == 0 { - Yyerror("cannot use %v as type %v in %s%s", Nconv(n, obj.FmtLong), t, context(), why) - op = OCONV - } - - r := Nod(op, n, nil) - r.Type = t - r.Typecheck = 1 - r.Implicit = true - r.Orig = n.Orig - return r -} - -// substArgTypes substitutes the given list of types for -// successive occurrences of the "any" placeholder in the -// type syntax expression n.Type. -func substArgTypes(n *Node, types ...*Type) { - for _, t := range types { - dowidth(t) - } - substAny(&n.Type, &types) - if len(types) > 0 { - Fatal("substArgTypes: too many argument types") - } -} - -// substAny walks *tp, replacing instances of "any" with successive -// elements removed from types. -func substAny(tp **Type, types *[]*Type) { - for { - t := *tp - if t == nil { - return - } - if t.Etype == TANY && t.Copyany != 0 { - if len(*types) == 0 { - Fatal("substArgTypes: not enough argument types") - } - *tp = (*types)[0] - *types = (*types)[1:] - } - - switch t.Etype { - case TPTR32, TPTR64, TCHAN, TARRAY: - tp = &t.Type - continue - - case TMAP: - substAny(&t.Down, types) - tp = &t.Type - continue - - case TFUNC: - substAny(&t.Type, types) - substAny(&t.Type.Down.Down, types) - substAny(&t.Type.Down, types) - - case TSTRUCT: - for t = t.Type; t != nil; t = t.Down { - substAny(&t.Type, types) - } - } - return - } -} - -/* - * Is this a 64-bit type? - */ -func Is64(t *Type) bool { - if t == nil { - return false - } - switch Simtype[t.Etype] { - case TINT64, TUINT64, TPTR64: - return true - } - - return false -} - -/* - * Is a conversion between t1 and t2 a no-op? - */ -func Noconv(t1 *Type, t2 *Type) bool { - e1 := int(Simtype[t1.Etype]) - e2 := int(Simtype[t2.Etype]) - - switch e1 { - case TINT8, TUINT8: - return e2 == TINT8 || e2 == TUINT8 - - case TINT16, TUINT16: - return e2 == TINT16 || e2 == TUINT16 - - case TINT32, TUINT32, TPTR32: - return e2 == TINT32 || e2 == TUINT32 || e2 == TPTR32 - - case TINT64, TUINT64, TPTR64: - return e2 == TINT64 || e2 == TUINT64 || e2 == TPTR64 - - case TFLOAT32: - return e2 == TFLOAT32 - - case TFLOAT64: - return e2 == TFLOAT64 - } - - return false -} - -func shallow(t *Type) *Type { - if t == nil { - return nil - } - nt := typ(0) - *nt = *t - if t.Orig == t { - nt.Orig = nt - } - return nt -} - -func deep(t *Type) *Type { - if t == nil { - return nil - } - - var nt *Type - switch t.Etype { - default: - nt = t // share from here down - - case TANY: - nt = shallow(t) - nt.Copyany = 1 - - case TPTR32, TPTR64, TCHAN, TARRAY: - nt = shallow(t) - nt.Type = deep(t.Type) - - case TMAP: - nt = shallow(t) - nt.Down = deep(t.Down) - nt.Type = deep(t.Type) - - case TFUNC: - nt = shallow(t) - nt.Type = deep(t.Type) - nt.Type.Down = deep(t.Type.Down) - nt.Type.Down.Down = deep(t.Type.Down.Down) - - case TSTRUCT: - nt = shallow(t) - nt.Type = shallow(t.Type) - xt := nt.Type - - for t = t.Type; t != nil; t = t.Down { - xt.Type = deep(t.Type) - xt.Down = shallow(t.Down) - xt = xt.Down - } - } - - return nt -} - -func syslook(name string, copy int) *Node { - s := Pkglookup(name, Runtimepkg) - if s == nil || s.Def == nil { - Fatal("syslook: can't find runtime.%s", name) - } - - if copy == 0 { - return s.Def - } - - n := Nod(0, nil, nil) - *n = *s.Def - n.Type = deep(s.Def.Type) - - return n -} - -/* - * compute a hash value for type t. - * if t is a method type, ignore the receiver - * so that the hash can be used in interface checks. - * %T already contains - * all the necessary logic to generate a representation - * of the type that completely describes it. - * using smprint here avoids duplicating that code. - * using md5 here is overkill, but i got tired of - * accidental collisions making the runtime think - * two types are equal when they really aren't. - */ -func typehash(t *Type) uint32 { - var p string - - if t.Thistuple != 0 { - // hide method receiver from Tpretty - t.Thistuple = 0 - - p = Tconv(t, obj.FmtLeft|obj.FmtUnsigned) - t.Thistuple = 1 - } else { - p = Tconv(t, obj.FmtLeft|obj.FmtUnsigned) - } - - //print("typehash: %s\n", p); - h := md5.Sum([]byte(p)) - return binary.LittleEndian.Uint32(h[:4]) -} - -var initPtrtoDone bool - -var ( - ptrToUint8 *Type - ptrToAny *Type - ptrToString *Type - ptrToBool *Type - ptrToInt32 *Type -) - -func initPtrto() { - ptrToUint8 = ptrto1(Types[TUINT8]) - ptrToAny = ptrto1(Types[TANY]) - ptrToString = ptrto1(Types[TSTRING]) - ptrToBool = ptrto1(Types[TBOOL]) - ptrToInt32 = ptrto1(Types[TINT32]) -} - -func ptrto1(t *Type) *Type { - t1 := typ(Tptr) - t1.Type = t - t1.Width = int64(Widthptr) - t1.Align = uint8(Widthptr) - return t1 -} - -// Ptrto returns the Type *t. -// The returned struct must not be modified. -func Ptrto(t *Type) *Type { - if Tptr == 0 { - Fatal("ptrto: no tptr") - } - // Reduce allocations by pre-creating common cases. - if !initPtrtoDone { - initPtrto() - initPtrtoDone = true - } - switch t { - case Types[TUINT8]: - return ptrToUint8 - case Types[TINT32]: - return ptrToInt32 - case Types[TANY]: - return ptrToAny - case Types[TSTRING]: - return ptrToString - case Types[TBOOL]: - return ptrToBool - } - return ptrto1(t) -} - -func frame(context int) { - var l *NodeList - - if context != 0 { - fmt.Printf("--- external frame ---\n") - l = externdcl - } else if Curfn != nil { - fmt.Printf("--- %v frame ---\n", Curfn.Nname.Sym) - l = Curfn.Func.Dcl - } else { - return - } - - var n *Node - var w int64 - for ; l != nil; l = l.Next { - n = l.N - w = -1 - if n.Type != nil { - w = n.Type.Width - } - switch n.Op { - case ONAME: - fmt.Printf("%v %v G%d %v width=%d\n", Oconv(int(n.Op), 0), n.Sym, n.Vargen, n.Type, w) - - case OTYPE: - fmt.Printf("%v %v width=%d\n", Oconv(int(n.Op), 0), n.Type, w) - } - } -} - -/* - * calculate sethi/ullman number - * roughly how many registers needed to - * compile a node. used to compile the - * hardest side first to minimize registers. - */ -func ullmancalc(n *Node) { - if n == nil { - return - } - - var ul int - var ur int - if n.Ninit != nil { - ul = UINF - goto out - } - - switch n.Op { - case OREGISTER, OLITERAL, ONAME: - ul = 1 - if n.Class == PPARAMREF || (n.Class&PHEAP != 0) { - ul++ - } - goto out - - case OCALL, OCALLFUNC, OCALLMETH, OCALLINTER, OASWB: - ul = UINF - goto out - - // hard with race detector - case OANDAND, OOROR: - if flag_race != 0 { - ul = UINF - goto out - } - } - - ul = 1 - if n.Left != nil { - ul = int(n.Left.Ullman) - } - ur = 1 - if n.Right != nil { - ur = int(n.Right.Ullman) - } - if ul == ur { - ul += 1 - } - if ur > ul { - ul = ur - } - -out: - if ul > 200 { - ul = 200 // clamp to uchar with room to grow - } - n.Ullman = uint8(ul) -} - -func badtype(o int, tl *Type, tr *Type) { - fmt_ := "" - if tl != nil { - fmt_ += fmt.Sprintf("\n\t%v", tl) - } - if tr != nil { - fmt_ += fmt.Sprintf("\n\t%v", tr) - } - - // common mistake: *struct and *interface. - if tl != nil && tr != nil && Isptr[tl.Etype] && Isptr[tr.Etype] { - if tl.Type.Etype == TSTRUCT && tr.Type.Etype == TINTER { - fmt_ += "\n\t(*struct vs *interface)" - } else if tl.Type.Etype == TINTER && tr.Type.Etype == TSTRUCT { - fmt_ += "\n\t(*interface vs *struct)" - } - } - - s := fmt_ - Yyerror("illegal types for operand: %v%s", Oconv(int(o), 0), s) -} - -/* - * iterator to walk a structure declaration - */ -func Structfirst(s *Iter, nn **Type) *Type { - var t *Type - - n := *nn - if n == nil { - goto bad - } - - switch n.Etype { - default: - goto bad - - case TSTRUCT, TINTER, TFUNC: - break - } - - t = n.Type - if t == nil { - return nil - } - - if t.Etype != TFIELD { - Fatal("structfirst: not field %v", t) - } - - s.T = t - return t - -bad: - Fatal("structfirst: not struct %v", n) - - return nil -} - -func structnext(s *Iter) *Type { - n := s.T - t := n.Down - if t == nil { - return nil - } - - if t.Etype != TFIELD { - Fatal("structnext: not struct %v", n) - - return nil - } - - s.T = t - return t -} - -/* - * iterator to this and inargs in a function - */ -func funcfirst(s *Iter, t *Type) *Type { - var fp *Type - - if t == nil { - goto bad - } - - if t.Etype != TFUNC { - goto bad - } - - s.Tfunc = t - s.Done = 0 - fp = Structfirst(s, getthis(t)) - if fp == nil { - s.Done = 1 - fp = Structfirst(s, getinarg(t)) - } - - return fp - -bad: - Fatal("funcfirst: not func %v", t) - return nil -} - -func funcnext(s *Iter) *Type { - fp := structnext(s) - if fp == nil && s.Done == 0 { - s.Done = 1 - fp = Structfirst(s, getinarg(s.Tfunc)) - } - - return fp -} - -func getthis(t *Type) **Type { - if t.Etype != TFUNC { - Fatal("getthis: not a func %v", t) - } - return &t.Type -} - -func Getoutarg(t *Type) **Type { - if t.Etype != TFUNC { - Fatal("getoutarg: not a func %v", t) - } - return &t.Type.Down -} - -func getinarg(t *Type) **Type { - if t.Etype != TFUNC { - Fatal("getinarg: not a func %v", t) - } - return &t.Type.Down.Down -} - -func getthisx(t *Type) *Type { - return *getthis(t) -} - -func getoutargx(t *Type) *Type { - return *Getoutarg(t) -} - -func getinargx(t *Type) *Type { - return *getinarg(t) -} - -// Brcom returns !(op). -// For example, Brcom(==) is !=. -func Brcom(a int) int { - switch a { - case OEQ: - return ONE - case ONE: - return OEQ - case OLT: - return OGE - case OGT: - return OLE - case OLE: - return OGT - case OGE: - return OLT - } - Fatal("brcom: no com for %v\n", Oconv(a, 0)) - return a -} - -// Brrev returns reverse(op). -// For example, Brrev(<) is >. -func Brrev(a int) int { - switch a { - case OEQ: - return OEQ - case ONE: - return ONE - case OLT: - return OGT - case OGT: - return OLT - case OLE: - return OGE - case OGE: - return OLE - } - Fatal("brrev: no rev for %v\n", Oconv(a, 0)) - return a -} - -/* - * return side effect-free n, appending side effects to init. - * result is assignable if n is. - */ -func safeexpr(n *Node, init **NodeList) *Node { - if n == nil { - return nil - } - - if n.Ninit != nil { - walkstmtlist(n.Ninit) - *init = concat(*init, n.Ninit) - n.Ninit = nil - } - - switch n.Op { - case ONAME, OLITERAL: - return n - - case ODOT, OLEN, OCAP: - l := safeexpr(n.Left, init) - if l == n.Left { - return n - } - r := Nod(OXXX, nil, nil) - *r = *n - r.Left = l - typecheck(&r, Erv) - walkexpr(&r, init) - return r - - case ODOTPTR, OIND: - l := safeexpr(n.Left, init) - if l == n.Left { - return n - } - a := Nod(OXXX, nil, nil) - *a = *n - a.Left = l - walkexpr(&a, init) - return a - - case OINDEX, OINDEXMAP: - l := safeexpr(n.Left, init) - r := safeexpr(n.Right, init) - if l == n.Left && r == n.Right { - return n - } - a := Nod(OXXX, nil, nil) - *a = *n - a.Left = l - a.Right = r - walkexpr(&a, init) - return a - } - - // make a copy; must not be used as an lvalue - if islvalue(n) { - Fatal("missing lvalue case in safeexpr: %v", n) - } - return cheapexpr(n, init) -} - -func copyexpr(n *Node, t *Type, init **NodeList) *Node { - l := temp(t) - a := Nod(OAS, l, n) - typecheck(&a, Etop) - walkexpr(&a, init) - *init = list(*init, a) - return l -} - -/* - * return side-effect free and cheap n, appending side effects to init. - * result may not be assignable. - */ -func cheapexpr(n *Node, init **NodeList) *Node { - switch n.Op { - case ONAME, OLITERAL: - return n - } - - return copyexpr(n, n.Type, init) -} - -/* - * return n in a local variable of type t if it is not already. - * the value is guaranteed not to change except by direct - * assignment to it. - */ -func localexpr(n *Node, t *Type, init **NodeList) *Node { - if n.Op == ONAME && (!n.Addrtaken || strings.HasPrefix(n.Sym.Name, "autotmp_")) && (n.Class == PAUTO || n.Class == PPARAM || n.Class == PPARAMOUT) && convertop(n.Type, t, nil) == OCONVNOP { - return n - } - - return copyexpr(n, t, init) -} - -func Setmaxarg(t *Type, extra int32) { - dowidth(t) - w := t.Argwid - if w >= Thearch.MAXWIDTH { - Fatal("bad argwid %v", t) - } - w += int64(extra) - if w >= Thearch.MAXWIDTH { - Fatal("bad argwid %d + %v", extra, t) - } - if w > Maxarg { - Maxarg = w - } -} - -/* - * unicode-aware case-insensitive strcmp - */ - -/* - * code to resolve elided DOTs - * in embedded types - */ - -// search depth 0 -- -// return count of fields+methods -// found with a given name -func lookdot0(s *Sym, t *Type, save **Type, ignorecase int) int { - u := t - if Isptr[u.Etype] { - u = u.Type - } - - c := 0 - if u.Etype == TSTRUCT || u.Etype == TINTER { - for f := u.Type; f != nil; f = f.Down { - if f.Sym == s || (ignorecase != 0 && f.Type.Etype == TFUNC && f.Type.Thistuple > 0 && strings.EqualFold(f.Sym.Name, s.Name)) { - if save != nil { - *save = f - } - c++ - } - } - } - - u = methtype(t, 0) - if u != nil { - for f := u.Method; f != nil; f = f.Down { - if f.Embedded == 0 && (f.Sym == s || (ignorecase != 0 && strings.EqualFold(f.Sym.Name, s.Name))) { - if save != nil { - *save = f - } - c++ - } - } - } - - return c -} - -// search depth d for field/method s -- -// return count of fields+methods -// found at search depth. -// answer is in dotlist array and -// count of number of ways is returned. -func adddot1(s *Sym, t *Type, d int, save **Type, ignorecase int) int { - if t.Trecur != 0 { - return 0 - } - t.Trecur = 1 - - var c int - var u *Type - var a int - if d == 0 { - c = lookdot0(s, t, save, ignorecase) - goto out - } - - c = 0 - u = t - if Isptr[u.Etype] { - u = u.Type - } - if u.Etype != TSTRUCT && u.Etype != TINTER { - goto out - } - - d-- - for f := u.Type; f != nil; f = f.Down { - if f.Embedded == 0 { - continue - } - if f.Sym == nil { - continue - } - a = adddot1(s, f.Type, d, save, ignorecase) - if a != 0 && c == 0 { - dotlist[d].field = f - } - c += a - } - -out: - t.Trecur = 0 - return c -} - -// in T.field -// find missing fields that -// will give shortest unique addressing. -// modify the tree with missing type names. -func adddot(n *Node) *Node { - typecheck(&n.Left, Etype|Erv) - n.Diag |= n.Left.Diag - t := n.Left.Type - if t == nil { - return n - } - - if n.Left.Op == OTYPE { - return n - } - - if n.Right.Op != ONAME { - return n - } - s := n.Right.Sym - if s == nil { - return n - } - - var c int - for d := 0; d < len(dotlist); d++ { - c = adddot1(s, t, d, nil, 0) - if c > 0 { - if c > 1 { - Yyerror("ambiguous selector %v", n) - n.Left = nil - return n - } - - // rebuild elided dots - for c := d - 1; c >= 0; c-- { - if n.Left.Type != nil && Isptr[n.Left.Type.Etype] { - n.Left.Implicit = true - } - n.Left = Nod(ODOT, n.Left, newname(dotlist[c].field.Sym)) - } - - return n - } - } - - return n -} - -/* - * code to help generate trampoline - * functions for methods on embedded - * subtypes. - * these are approx the same as - * the corresponding adddot routines - * except that they expect to be called - * with unique tasks and they return - * the actual methods. - */ -type Symlink struct { - field *Type - good uint8 - followptr uint8 - link *Symlink -} - -var slist *Symlink - -func expand0(t *Type, followptr int) { - u := t - if Isptr[u.Etype] { - followptr = 1 - u = u.Type - } - - if u.Etype == TINTER { - var sl *Symlink - for f := u.Type; f != nil; f = f.Down { - if f.Sym.Flags&SymUniq != 0 { - continue - } - f.Sym.Flags |= SymUniq - sl = new(Symlink) - sl.field = f - sl.link = slist - sl.followptr = uint8(followptr) - slist = sl - } - - return - } - - u = methtype(t, 0) - if u != nil { - var sl *Symlink - for f := u.Method; f != nil; f = f.Down { - if f.Sym.Flags&SymUniq != 0 { - continue - } - f.Sym.Flags |= SymUniq - sl = new(Symlink) - sl.field = f - sl.link = slist - sl.followptr = uint8(followptr) - slist = sl - } - } -} - -func expand1(t *Type, d int, followptr int) { - if t.Trecur != 0 { - return - } - if d == 0 { - return - } - t.Trecur = 1 - - if d != len(dotlist)-1 { - expand0(t, followptr) - } - - u := t - if Isptr[u.Etype] { - followptr = 1 - u = u.Type - } - - if u.Etype != TSTRUCT && u.Etype != TINTER { - goto out - } - - for f := u.Type; f != nil; f = f.Down { - if f.Embedded == 0 { - continue - } - if f.Sym == nil { - continue - } - expand1(f.Type, d-1, followptr) - } - -out: - t.Trecur = 0 -} - -func expandmeth(t *Type) { - if t == nil || t.Xmethod != nil { - return - } - - // mark top-level method symbols - // so that expand1 doesn't consider them. - var f *Type - for f = t.Method; f != nil; f = f.Down { - f.Sym.Flags |= SymUniq - } - - // generate all reachable methods - slist = nil - - expand1(t, len(dotlist)-1, 0) - - // check each method to be uniquely reachable - var c int - var d int - for sl := slist; sl != nil; sl = sl.link { - sl.field.Sym.Flags &^= SymUniq - for d = 0; d < len(dotlist); d++ { - c = adddot1(sl.field.Sym, t, d, &f, 0) - if c == 0 { - continue - } - if c == 1 { - // addot1 may have dug out arbitrary fields, we only want methods. - if f.Type.Etype == TFUNC && f.Type.Thistuple > 0 { - sl.good = 1 - sl.field = f - } - } - - break - } - } - - for f = t.Method; f != nil; f = f.Down { - f.Sym.Flags &^= SymUniq - } - - t.Xmethod = t.Method - for sl := slist; sl != nil; sl = sl.link { - if sl.good != 0 { - // add it to the base type method list - f = typ(TFIELD) - - *f = *sl.field - f.Embedded = 1 // needs a trampoline - if sl.followptr != 0 { - f.Embedded = 2 - } - f.Down = t.Xmethod - t.Xmethod = f - } - } -} - -/* - * Given funarg struct list, return list of ODCLFIELD Node fn args. - */ -func structargs(tl **Type, mustname int) *NodeList { - var savet Iter - var a *Node - var n *Node - var buf string - - var args *NodeList - gen := 0 - for t := Structfirst(&savet, tl); t != nil; t = structnext(&savet) { - n = nil - if mustname != 0 && (t.Sym == nil || t.Sym.Name == "_") { - // invent a name so that we can refer to it in the trampoline - buf = fmt.Sprintf(".anon%d", gen) - gen++ - - n = newname(Lookup(buf)) - } else if t.Sym != nil { - n = newname(t.Sym) - } - a = Nod(ODCLFIELD, n, typenod(t.Type)) - a.Isddd = t.Isddd - if n != nil { - n.Isddd = t.Isddd - } - args = list(args, a) - } - - return args -} - -/* - * Generate a wrapper function to convert from - * a receiver of type T to a receiver of type U. - * That is, - * - * func (t T) M() { - * ... - * } - * - * already exists; this function generates - * - * func (u U) M() { - * u.M() - * } - * - * where the types T and U are such that u.M() is valid - * and calls the T.M method. - * The resulting function is for use in method tables. - * - * rcvr - U - * method - M func (t T)(), a TFIELD type struct - * newnam - the eventual mangled name of this function - */ - -var genwrapper_linehistdone int = 0 - -func genwrapper(rcvr *Type, method *Type, newnam *Sym, iface int) { - if false && Debug['r'] != 0 { - fmt.Printf("genwrapper rcvrtype=%v method=%v newnam=%v\n", rcvr, method, newnam) - } - - lexlineno++ - lineno = lexlineno - if genwrapper_linehistdone == 0 { - // All the wrappers can share the same linehist entry. - linehistpush("") - - genwrapper_linehistdone = 1 - } - - dclcontext = PEXTERN - markdcl() - - this := Nod(ODCLFIELD, newname(Lookup(".this")), typenod(rcvr)) - this.Left.Ntype = this.Right - in := structargs(getinarg(method.Type), 1) - out := structargs(Getoutarg(method.Type), 0) - - t := Nod(OTFUNC, nil, nil) - l := list1(this) - if iface != 0 && rcvr.Width < Types[Tptr].Width { - // Building method for interface table and receiver - // is smaller than the single pointer-sized word - // that the interface call will pass in. - // Add a dummy padding argument after the - // receiver to make up the difference. - tpad := typ(TARRAY) - - tpad.Type = Types[TUINT8] - tpad.Bound = Types[Tptr].Width - rcvr.Width - pad := Nod(ODCLFIELD, newname(Lookup(".pad")), typenod(tpad)) - l = list(l, pad) - } - - t.List = concat(l, in) - t.Rlist = out - - fn := Nod(ODCLFUNC, nil, nil) - fn.Nname = newname(newnam) - fn.Nname.Defn = fn - fn.Nname.Ntype = t - declare(fn.Nname, PFUNC) - funchdr(fn) - - // arg list - var args *NodeList - - isddd := false - for l := in; l != nil; l = l.Next { - args = list(args, l.N.Left) - isddd = l.N.Left.Isddd - } - - methodrcvr := getthisx(method.Type).Type.Type - - // generate nil pointer check for better error - if Isptr[rcvr.Etype] && rcvr.Type == methodrcvr { - // generating wrapper from *T to T. - n := Nod(OIF, nil, nil) - - n.Ntest = Nod(OEQ, this.Left, nodnil()) - - // these strings are already in the reflect tables, - // so no space cost to use them here. - var l *NodeList - - var v Val - v.Ctype = CTSTR - v.U = rcvr.Type.Sym.Pkg.Name // package name - l = list(l, nodlit(v)) - v.U = rcvr.Type.Sym.Name // type name - l = list(l, nodlit(v)) - v.U = method.Sym.Name - l = list(l, nodlit(v)) // method name - call := Nod(OCALL, syslook("panicwrap", 0), nil) - call.List = l - n.Nbody = list1(call) - fn.Nbody = list(fn.Nbody, n) - } - - dot := adddot(Nod(OXDOT, this.Left, newname(method.Sym))) - - // generate call - if flag_race == 0 && Isptr[rcvr.Etype] && Isptr[methodrcvr.Etype] && method.Embedded != 0 && !isifacemethod(method.Type) { - // generate tail call: adjust pointer receiver and jump to embedded method. - dot = dot.Left // skip final .M - if !Isptr[dotlist[0].field.Type.Etype] { - dot = Nod(OADDR, dot, nil) - } - as := Nod(OAS, this.Left, Nod(OCONVNOP, dot, nil)) - as.Right.Type = rcvr - fn.Nbody = list(fn.Nbody, as) - n := Nod(ORETJMP, nil, nil) - n.Left = newname(methodsym(method.Sym, methodrcvr, 0)) - fn.Nbody = list(fn.Nbody, n) - } else { - fn.Func.Wrapper = true // ignore frame for panic+recover matching - call := Nod(OCALL, dot, nil) - call.List = args - call.Isddd = isddd - if method.Type.Outtuple > 0 { - n := Nod(ORETURN, nil, nil) - n.List = list1(call) - call = n - } - - fn.Nbody = list(fn.Nbody, call) - } - - if false && Debug['r'] != 0 { - dumplist("genwrapper body", fn.Nbody) - } - - funcbody(fn) - Curfn = fn - - // wrappers where T is anonymous (struct or interface) can be duplicated. - if rcvr.Etype == TSTRUCT || rcvr.Etype == TINTER || Isptr[rcvr.Etype] && rcvr.Type.Etype == TSTRUCT { - fn.Func.Dupok = true - } - typecheck(&fn, Etop) - typechecklist(fn.Nbody, Etop) - - // Set inl_nonlocal to whether we are calling a method on a - // type defined in a different package. Checked in inlvar. - if !methodrcvr.Local { - inl_nonlocal = 1 - } - - inlcalls(fn) - - inl_nonlocal = 0 - - Curfn = nil - funccompile(fn) -} - -func hashmem(t *Type) *Node { - sym := Pkglookup("memhash", Runtimepkg) - - n := newname(sym) - n.Class = PFUNC - tfn := Nod(OTFUNC, nil, nil) - tfn.List = list(tfn.List, Nod(ODCLFIELD, nil, typenod(Ptrto(t)))) - tfn.List = list(tfn.List, Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR]))) - tfn.List = list(tfn.List, Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR]))) - tfn.Rlist = list(tfn.Rlist, Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR]))) - typecheck(&tfn, Etype) - n.Type = tfn.Type - return n -} - -func hashfor(t *Type) *Node { - var sym *Sym - - a := algtype1(t, nil) - switch a { - case AMEM: - Fatal("hashfor with AMEM type") - - case AINTER: - sym = Pkglookup("interhash", Runtimepkg) - - case ANILINTER: - sym = Pkglookup("nilinterhash", Runtimepkg) - - case ASTRING: - sym = Pkglookup("strhash", Runtimepkg) - - case AFLOAT32: - sym = Pkglookup("f32hash", Runtimepkg) - - case AFLOAT64: - sym = Pkglookup("f64hash", Runtimepkg) - - case ACPLX64: - sym = Pkglookup("c64hash", Runtimepkg) - - case ACPLX128: - sym = Pkglookup("c128hash", Runtimepkg) - - default: - sym = typesymprefix(".hash", t) - } - - n := newname(sym) - n.Class = PFUNC - tfn := Nod(OTFUNC, nil, nil) - tfn.List = list(tfn.List, Nod(ODCLFIELD, nil, typenod(Ptrto(t)))) - tfn.List = list(tfn.List, Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR]))) - tfn.Rlist = list(tfn.Rlist, Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR]))) - typecheck(&tfn, Etype) - n.Type = tfn.Type - return n -} - -/* - * Generate a helper function to compute the hash of a value of type t. - */ -func genhash(sym *Sym, t *Type) { - if Debug['r'] != 0 { - fmt.Printf("genhash %v %v\n", sym, t) - } - - lineno = 1 // less confusing than end of input - dclcontext = PEXTERN - markdcl() - - // func sym(p *T, h uintptr) uintptr - fn := Nod(ODCLFUNC, nil, nil) - - fn.Nname = newname(sym) - fn.Nname.Class = PFUNC - tfn := Nod(OTFUNC, nil, nil) - fn.Nname.Ntype = tfn - - n := Nod(ODCLFIELD, newname(Lookup("p")), typenod(Ptrto(t))) - tfn.List = list(tfn.List, n) - np := n.Left - n = Nod(ODCLFIELD, newname(Lookup("h")), typenod(Types[TUINTPTR])) - tfn.List = list(tfn.List, n) - nh := n.Left - n = Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])) // return value - tfn.Rlist = list(tfn.Rlist, n) - - funchdr(fn) - typecheck(&fn.Nname.Ntype, Etype) - - // genhash is only called for types that have equality but - // cannot be handled by the standard algorithms, - // so t must be either an array or a struct. - switch t.Etype { - default: - Fatal("genhash %v", t) - - case TARRAY: - if Isslice(t) { - Fatal("genhash %v", t) - } - - // An array of pure memory would be handled by the - // standard algorithm, so the element type must not be - // pure memory. - hashel := hashfor(t.Type) - - n := Nod(ORANGE, nil, Nod(OIND, np, nil)) - ni := newname(Lookup("i")) - ni.Type = Types[TINT] - n.List = list1(ni) - n.Colas = true - colasdefn(n.List, n) - ni = n.List.N - - // TODO: with aeshash we don't need these shift/mul parts - - // h = h<<3 | h>>61 - n.Nbody = list(n.Nbody, Nod(OAS, nh, Nod(OOR, Nod(OLSH, nh, Nodintconst(3)), Nod(ORSH, nh, Nodintconst(int64(Widthptr)*8-3))))) - - // h *= mul - // Same multipliers as in runtime.memhash. - var mul int64 - if Widthptr == 4 { - mul = 3267000013 - } else { - mul = 23344194077549503 - } - n.Nbody = list(n.Nbody, Nod(OAS, nh, Nod(OMUL, nh, Nodintconst(mul)))) - - // h = hashel(&p[i], h) - call := Nod(OCALL, hashel, nil) - - nx := Nod(OINDEX, np, ni) - nx.Bounded = true - na := Nod(OADDR, nx, nil) - na.Etype = 1 // no escape to heap - call.List = list(call.List, na) - call.List = list(call.List, nh) - n.Nbody = list(n.Nbody, Nod(OAS, nh, call)) - - fn.Nbody = list(fn.Nbody, n) - - // Walk the struct using memhash for runs of AMEM - // and calling specific hash functions for the others. - case TSTRUCT: - var first *Type - - offend := int64(0) - var size int64 - var call *Node - var nx *Node - var na *Node - var hashel *Node - for t1 := t.Type; ; t1 = t1.Down { - if t1 != nil && algtype1(t1.Type, nil) == AMEM && !isblanksym(t1.Sym) { - offend = t1.Width + t1.Type.Width - if first == nil { - first = t1 - } - - // If it's a memory field but it's padded, stop here. - if ispaddedfield(t1, t.Width) { - t1 = t1.Down - } else { - continue - } - } - - // Run memhash for fields up to this one. - if first != nil { - size = offend - first.Width // first->width is offset - hashel = hashmem(first.Type) - - // h = hashel(&p.first, size, h) - call = Nod(OCALL, hashel, nil) - - nx = Nod(OXDOT, np, newname(first.Sym)) // TODO: fields from other packages? - na = Nod(OADDR, nx, nil) - na.Etype = 1 // no escape to heap - call.List = list(call.List, na) - call.List = list(call.List, nh) - call.List = list(call.List, Nodintconst(size)) - fn.Nbody = list(fn.Nbody, Nod(OAS, nh, call)) - - first = nil - } - - if t1 == nil { - break - } - if isblanksym(t1.Sym) { - continue - } - - // Run hash for this field. - if algtype1(t1.Type, nil) == AMEM { - hashel = hashmem(t1.Type) - - // h = memhash(&p.t1, h, size) - call = Nod(OCALL, hashel, nil) - - nx = Nod(OXDOT, np, newname(t1.Sym)) // TODO: fields from other packages? - na = Nod(OADDR, nx, nil) - na.Etype = 1 // no escape to heap - call.List = list(call.List, na) - call.List = list(call.List, nh) - call.List = list(call.List, Nodintconst(t1.Type.Width)) - fn.Nbody = list(fn.Nbody, Nod(OAS, nh, call)) - } else { - hashel = hashfor(t1.Type) - - // h = hashel(&p.t1, h) - call = Nod(OCALL, hashel, nil) - - nx = Nod(OXDOT, np, newname(t1.Sym)) // TODO: fields from other packages? - na = Nod(OADDR, nx, nil) - na.Etype = 1 // no escape to heap - call.List = list(call.List, na) - call.List = list(call.List, nh) - fn.Nbody = list(fn.Nbody, Nod(OAS, nh, call)) - } - } - } - - r := Nod(ORETURN, nil, nil) - r.List = list(r.List, nh) - fn.Nbody = list(fn.Nbody, r) - - if Debug['r'] != 0 { - dumplist("genhash body", fn.Nbody) - } - - funcbody(fn) - Curfn = fn - fn.Func.Dupok = true - typecheck(&fn, Etop) - typechecklist(fn.Nbody, Etop) - Curfn = nil - - // Disable safemode while compiling this code: the code we - // generate internally can refer to unsafe.Pointer. - // In this case it can happen if we need to generate an == - // for a struct containing a reflect.Value, which itself has - // an unexported field of type unsafe.Pointer. - old_safemode := safemode - - safemode = 0 - funccompile(fn) - safemode = old_safemode -} - -// Return node for -// if p.field != q.field { return false } -func eqfield(p *Node, q *Node, field *Node) *Node { - nx := Nod(OXDOT, p, field) - ny := Nod(OXDOT, q, field) - nif := Nod(OIF, nil, nil) - nif.Ntest = Nod(ONE, nx, ny) - r := Nod(ORETURN, nil, nil) - r.List = list(r.List, Nodbool(false)) - nif.Nbody = list(nif.Nbody, r) - return nif -} - -func eqmemfunc(size int64, type_ *Type, needsize *int) *Node { - var fn *Node - - switch size { - default: - fn = syslook("memequal", 1) - *needsize = 1 - - case 1, 2, 4, 8, 16: - buf := fmt.Sprintf("memequal%d", int(size)*8) - fn = syslook(buf, 1) - *needsize = 0 - } - - substArgTypes(fn, type_, type_) - return fn -} - -// Return node for -// if !memequal(&p.field, &q.field [, size]) { return false } -func eqmem(p *Node, q *Node, field *Node, size int64) *Node { - var needsize int - - nx := Nod(OADDR, Nod(OXDOT, p, field), nil) - nx.Etype = 1 // does not escape - ny := Nod(OADDR, Nod(OXDOT, q, field), nil) - ny.Etype = 1 // does not escape - typecheck(&nx, Erv) - typecheck(&ny, Erv) - - call := Nod(OCALL, eqmemfunc(size, nx.Type.Type, &needsize), nil) - call.List = list(call.List, nx) - call.List = list(call.List, ny) - if needsize != 0 { - call.List = list(call.List, Nodintconst(size)) - } - - nif := Nod(OIF, nil, nil) - nif.Ntest = Nod(ONOT, call, nil) - r := Nod(ORETURN, nil, nil) - r.List = list(r.List, Nodbool(false)) - nif.Nbody = list(nif.Nbody, r) - return nif -} - -/* - * Generate a helper function to check equality of two values of type t. - */ -func geneq(sym *Sym, t *Type) { - if Debug['r'] != 0 { - fmt.Printf("geneq %v %v\n", sym, t) - } - - lineno = 1 // less confusing than end of input - dclcontext = PEXTERN - markdcl() - - // func sym(p, q *T) bool - fn := Nod(ODCLFUNC, nil, nil) - - fn.Nname = newname(sym) - fn.Nname.Class = PFUNC - tfn := Nod(OTFUNC, nil, nil) - fn.Nname.Ntype = tfn - - n := Nod(ODCLFIELD, newname(Lookup("p")), typenod(Ptrto(t))) - tfn.List = list(tfn.List, n) - np := n.Left - n = Nod(ODCLFIELD, newname(Lookup("q")), typenod(Ptrto(t))) - tfn.List = list(tfn.List, n) - nq := n.Left - n = Nod(ODCLFIELD, nil, typenod(Types[TBOOL])) - tfn.Rlist = list(tfn.Rlist, n) - - funchdr(fn) - - // geneq is only called for types that have equality but - // cannot be handled by the standard algorithms, - // so t must be either an array or a struct. - switch t.Etype { - default: - Fatal("geneq %v", t) - - case TARRAY: - if Isslice(t) { - Fatal("geneq %v", t) - } - - // An array of pure memory would be handled by the - // standard memequal, so the element type must not be - // pure memory. Even if we unrolled the range loop, - // each iteration would be a function call, so don't bother - // unrolling. - nrange := Nod(ORANGE, nil, Nod(OIND, np, nil)) - - ni := newname(Lookup("i")) - ni.Type = Types[TINT] - nrange.List = list1(ni) - nrange.Colas = true - colasdefn(nrange.List, nrange) - ni = nrange.List.N - - // if p[i] != q[i] { return false } - nx := Nod(OINDEX, np, ni) - - nx.Bounded = true - ny := Nod(OINDEX, nq, ni) - ny.Bounded = true - - nif := Nod(OIF, nil, nil) - nif.Ntest = Nod(ONE, nx, ny) - r := Nod(ORETURN, nil, nil) - r.List = list(r.List, Nodbool(false)) - nif.Nbody = list(nif.Nbody, r) - nrange.Nbody = list(nrange.Nbody, nif) - fn.Nbody = list(fn.Nbody, nrange) - - // Walk the struct using memequal for runs of AMEM - // and calling specific equality tests for the others. - // Skip blank-named fields. - case TSTRUCT: - var first *Type - - offend := int64(0) - var size int64 - for t1 := t.Type; ; t1 = t1.Down { - if t1 != nil && algtype1(t1.Type, nil) == AMEM && !isblanksym(t1.Sym) { - offend = t1.Width + t1.Type.Width - if first == nil { - first = t1 - } - - // If it's a memory field but it's padded, stop here. - if ispaddedfield(t1, t.Width) { - t1 = t1.Down - } else { - continue - } - } - - // Run memequal for fields up to this one. - // TODO(rsc): All the calls to newname are wrong for - // cross-package unexported fields. - if first != nil { - if first.Down == t1 { - fn.Nbody = list(fn.Nbody, eqfield(np, nq, newname(first.Sym))) - } else if first.Down.Down == t1 { - fn.Nbody = list(fn.Nbody, eqfield(np, nq, newname(first.Sym))) - first = first.Down - if !isblanksym(first.Sym) { - fn.Nbody = list(fn.Nbody, eqfield(np, nq, newname(first.Sym))) - } - } else { - // More than two fields: use memequal. - size = offend - first.Width // first->width is offset - fn.Nbody = list(fn.Nbody, eqmem(np, nq, newname(first.Sym), size)) - } - - first = nil - } - - if t1 == nil { - break - } - if isblanksym(t1.Sym) { - continue - } - - // Check this field, which is not just memory. - fn.Nbody = list(fn.Nbody, eqfield(np, nq, newname(t1.Sym))) - } - } - - // return true - r := Nod(ORETURN, nil, nil) - - r.List = list(r.List, Nodbool(true)) - fn.Nbody = list(fn.Nbody, r) - - if Debug['r'] != 0 { - dumplist("geneq body", fn.Nbody) - } - - funcbody(fn) - Curfn = fn - fn.Func.Dupok = true - typecheck(&fn, Etop) - typechecklist(fn.Nbody, Etop) - Curfn = nil - - // Disable safemode while compiling this code: the code we - // generate internally can refer to unsafe.Pointer. - // In this case it can happen if we need to generate an == - // for a struct containing a reflect.Value, which itself has - // an unexported field of type unsafe.Pointer. - old_safemode := safemode - - safemode = 0 - funccompile(fn) - safemode = old_safemode -} - -func ifacelookdot(s *Sym, t *Type, followptr *int, ignorecase int) *Type { - *followptr = 0 - - if t == nil { - return nil - } - - var m *Type - var i int - var c int - for d := 0; d < len(dotlist); d++ { - c = adddot1(s, t, d, &m, ignorecase) - if c > 1 { - Yyerror("%v.%v is ambiguous", t, s) - return nil - } - - if c == 1 { - for i = 0; i < d; i++ { - if Isptr[dotlist[i].field.Type.Etype] { - *followptr = 1 - break - } - } - - if m.Type.Etype != TFUNC || m.Type.Thistuple == 0 { - Yyerror("%v.%v is a field, not a method", t, s) - return nil - } - - return m - } - } - - return nil -} - -func implements(t *Type, iface *Type, m **Type, samename **Type, ptr *int) bool { - t0 := t - if t == nil { - return false - } - - // if this is too slow, - // could sort these first - // and then do one loop. - - if t.Etype == TINTER { - var tm *Type - for im := iface.Type; im != nil; im = im.Down { - for tm = t.Type; tm != nil; tm = tm.Down { - if tm.Sym == im.Sym { - if Eqtype(tm.Type, im.Type) { - goto found - } - *m = im - *samename = tm - *ptr = 0 - return false - } - } - - *m = im - *samename = nil - *ptr = 0 - return false - found: - } - - return true - } - - t = methtype(t, 0) - if t != nil { - expandmeth(t) - } - var tm *Type - var imtype *Type - var followptr int - var rcvr *Type - for im := iface.Type; im != nil; im = im.Down { - imtype = methodfunc(im.Type, nil) - tm = ifacelookdot(im.Sym, t, &followptr, 0) - if tm == nil || tm.Nointerface || !Eqtype(methodfunc(tm.Type, nil), imtype) { - if tm == nil { - tm = ifacelookdot(im.Sym, t, &followptr, 1) - } - *m = im - *samename = tm - *ptr = 0 - return false - } - - // if pointer receiver in method, - // the method does not exist for value types. - rcvr = getthisx(tm.Type).Type.Type - - if Isptr[rcvr.Etype] && !Isptr[t0.Etype] && followptr == 0 && !isifacemethod(tm.Type) { - if false && Debug['r'] != 0 { - Yyerror("interface pointer mismatch") - } - - *m = im - *samename = nil - *ptr = 1 - return false - } - } - - return true -} - -/* - * even simpler simtype; get rid of ptr, bool. - * assuming that the front end has rejected - * all the invalid conversions (like ptr -> bool) - */ -func Simsimtype(t *Type) int { - if t == nil { - return 0 - } - - et := int(Simtype[t.Etype]) - switch et { - case TPTR32: - et = TUINT32 - - case TPTR64: - et = TUINT64 - - case TBOOL: - et = TUINT8 - } - - return et -} - -func listtreecopy(l *NodeList) *NodeList { - var out *NodeList - for ; l != nil; l = l.Next { - out = list(out, treecopy(l.N)) - } - return out -} - -func liststmt(l *NodeList) *Node { - n := Nod(OBLOCK, nil, nil) - n.List = l - if l != nil { - n.Lineno = l.N.Lineno - } - return n -} - -/* - * return nelem of list - */ -func structcount(t *Type) int { - var s Iter - - v := 0 - for t = Structfirst(&s, &t); t != nil; t = structnext(&s) { - v++ - } - return v -} - -/* - * return power of 2 of the constant - * operand. -1 if it is not a power of 2. - * 1000+ if it is a -(power of 2) - */ -func powtwo(n *Node) int { - if n == nil || n.Op != OLITERAL || n.Type == nil { - return -1 - } - if !Isint[n.Type.Etype] { - return -1 - } - - v := uint64(Mpgetfix(n.Val.U.(*Mpint))) - b := uint64(1) - for i := 0; i < 64; i++ { - if b == v { - return i - } - b = b << 1 - } - - if !Issigned[n.Type.Etype] { - return -1 - } - - v = -v - b = 1 - for i := 0; i < 64; i++ { - if b == v { - return i + 1000 - } - b = b << 1 - } - - return -1 -} - -/* - * return the unsigned type for - * a signed integer type. - * returns T if input is not a - * signed integer type. - */ -func tounsigned(t *Type) *Type { - // this is types[et+1], but not sure - // that this relation is immutable - switch t.Etype { - default: - fmt.Printf("tounsigned: unknown type %v\n", t) - t = nil - - case TINT: - t = Types[TUINT] - - case TINT8: - t = Types[TUINT8] - - case TINT16: - t = Types[TUINT16] - - case TINT32: - t = Types[TUINT32] - - case TINT64: - t = Types[TUINT64] - } - - return t -} - -/* - * magic number for signed division - * see hacker's delight chapter 10 - */ -func Smagic(m *Magic) { - var mask uint64 - - m.Bad = 0 - switch m.W { - default: - m.Bad = 1 - return - - case 8: - mask = 0xff - - case 16: - mask = 0xffff - - case 32: - mask = 0xffffffff - - case 64: - mask = 0xffffffffffffffff - } - - two31 := mask ^ (mask >> 1) - - p := m.W - 1 - ad := uint64(m.Sd) - if m.Sd < 0 { - ad = -uint64(m.Sd) - } - - // bad denominators - if ad == 0 || ad == 1 || ad == two31 { - m.Bad = 1 - return - } - - t := two31 - ad &= mask - - anc := t - 1 - t%ad - anc &= mask - - q1 := two31 / anc - r1 := two31 - q1*anc - q1 &= mask - r1 &= mask - - q2 := two31 / ad - r2 := two31 - q2*ad - q2 &= mask - r2 &= mask - - var delta uint64 - for { - p++ - q1 <<= 1 - r1 <<= 1 - q1 &= mask - r1 &= mask - if r1 >= anc { - q1++ - r1 -= anc - q1 &= mask - r1 &= mask - } - - q2 <<= 1 - r2 <<= 1 - q2 &= mask - r2 &= mask - if r2 >= ad { - q2++ - r2 -= ad - q2 &= mask - r2 &= mask - } - - delta = ad - r2 - delta &= mask - if q1 < delta || (q1 == delta && r1 == 0) { - continue - } - - break - } - - m.Sm = int64(q2 + 1) - if uint64(m.Sm)&two31 != 0 { - m.Sm |= ^int64(mask) - } - m.S = p - m.W -} - -/* - * magic number for unsigned division - * see hacker's delight chapter 10 - */ -func Umagic(m *Magic) { - var mask uint64 - - m.Bad = 0 - m.Ua = 0 - - switch m.W { - default: - m.Bad = 1 - return - - case 8: - mask = 0xff - - case 16: - mask = 0xffff - - case 32: - mask = 0xffffffff - - case 64: - mask = 0xffffffffffffffff - } - - two31 := mask ^ (mask >> 1) - - m.Ud &= mask - if m.Ud == 0 || m.Ud == two31 { - m.Bad = 1 - return - } - - nc := mask - (-m.Ud&mask)%m.Ud - p := m.W - 1 - - q1 := two31 / nc - r1 := two31 - q1*nc - q1 &= mask - r1 &= mask - - q2 := (two31 - 1) / m.Ud - r2 := (two31 - 1) - q2*m.Ud - q2 &= mask - r2 &= mask - - var delta uint64 - for { - p++ - if r1 >= nc-r1 { - q1 <<= 1 - q1++ - r1 <<= 1 - r1 -= nc - } else { - q1 <<= 1 - r1 <<= 1 - } - - q1 &= mask - r1 &= mask - if r2+1 >= m.Ud-r2 { - if q2 >= two31-1 { - m.Ua = 1 - } - - q2 <<= 1 - q2++ - r2 <<= 1 - r2++ - r2 -= m.Ud - } else { - if q2 >= two31 { - m.Ua = 1 - } - - q2 <<= 1 - r2 <<= 1 - r2++ - } - - q2 &= mask - r2 &= mask - - delta = m.Ud - 1 - r2 - delta &= mask - - if p < m.W+m.W { - if q1 < delta || (q1 == delta && r1 == 0) { - continue - } - } - - break - } - - m.Um = q2 + 1 - m.S = p - m.W -} - -func ngotype(n *Node) *Sym { - if n.Type != nil { - return typenamesym(n.Type) - } - return nil -} - -/* - * Convert raw string to the prefix that will be used in the symbol - * table. All control characters, space, '%' and '"', as well as - * non-7-bit clean bytes turn into %xx. The period needs escaping - * only in the last segment of the path, and it makes for happier - * users if we escape that as little as possible. - * - * If you edit this, edit ../ld/lib.c:/^pathtoprefix too. - * If you edit this, edit ../../debug/goobj/read.go:/importPathToPrefix too. - */ -func pathtoprefix(s string) string { - slash := strings.LastIndex(s, "/") - for i := 0; i < len(s); i++ { - c := s[i] - if c <= ' ' || i >= slash && c == '.' || c == '%' || c == '"' || c >= 0x7F { - var buf bytes.Buffer - for i := 0; i < len(s); i++ { - c := s[i] - if c <= ' ' || i >= slash && c == '.' || c == '%' || c == '"' || c >= 0x7F { - fmt.Fprintf(&buf, "%%%02x", c) - continue - } - buf.WriteByte(c) - } - return buf.String() - } - } - return s -} - -var pkgMap = make(map[string]*Pkg) -var pkgs []*Pkg - -func mkpkg(path string) *Pkg { - if p := pkgMap[path]; p != nil { - return p - } - - p := new(Pkg) - p.Path = path - p.Prefix = pathtoprefix(path) - p.Syms = make(map[string]*Sym) - pkgMap[path] = p - pkgs = append(pkgs, p) - return p -} - -func addinit(np **Node, init *NodeList) { - if init == nil { - return - } - - n := *np - switch n.Op { - // There may be multiple refs to this node; - // introduce OCONVNOP to hold init list. - case ONAME, OLITERAL: - n = Nod(OCONVNOP, n, nil) - - n.Type = n.Left.Type - n.Typecheck = 1 - *np = n - } - - n.Ninit = concat(init, n.Ninit) - n.Ullman = UINF -} - -var reservedimports = []string{ - "go", - "type", -} - -func isbadimport(path string) bool { - if strings.Contains(path, "\x00") { - Yyerror("import path contains NUL") - return true - } - - for i := 0; i < len(reservedimports); i++ { - if path == reservedimports[i] { - Yyerror("import path %q is reserved and cannot be used", path) - return true - } - } - - var s string - _ = s - var r uint - _ = r - for _, r := range path { - if r == utf8.RuneError { - Yyerror("import path contains invalid UTF-8 sequence: %q", path) - return true - } - - if r < 0x20 || r == 0x7f { - Yyerror("import path contains control character: %q", path) - return true - } - - if r == '\\' { - Yyerror("import path contains backslash; use slash: %q", path) - return true - } - - if unicode.IsSpace(rune(r)) { - Yyerror("import path contains space character: %q", path) - return true - } - - if strings.ContainsRune("!\"#$%&'()*,:;<=>?[]^`{|}", r) { - Yyerror("import path contains invalid character '%c': %q", r, path) - return true - } - } - - return false -} - -func checknil(x *Node, init **NodeList) { - if Isinter(x.Type) { - x = Nod(OITAB, x, nil) - typecheck(&x, Erv) - } - - n := Nod(OCHECKNIL, x, nil) - n.Typecheck = 1 - *init = list(*init, n) -} - -/* - * Can this type be stored directly in an interface word? - * Yes, if the representation is a single pointer. - */ -func isdirectiface(t *Type) bool { - switch t.Etype { - case TPTR32, - TPTR64, - TCHAN, - TMAP, - TFUNC, - TUNSAFEPTR: - return true - - // Array of 1 direct iface type can be direct. - case TARRAY: - return t.Bound == 1 && isdirectiface(t.Type) - - // Struct with 1 field of direct iface type can be direct. - case TSTRUCT: - return t.Type != nil && t.Type.Down == nil && isdirectiface(t.Type.Type) - } - - return false -} - -// type2IET returns "T" if t is a concrete type, -// "I" if t is an interface type, and "E" if t is an empty interface type. -// It is used to build calls to the conv* and assert* runtime routines. -func type2IET(t *Type) string { - if isnilinter(t) { - return "E" - } - if Isinter(t) { - return "I" - } - return "T" -} diff --git a/src/cmd/internal/gc/swt.go b/src/cmd/internal/gc/swt.go deleted file mode 100644 index e8f15a5ce5..0000000000 --- a/src/cmd/internal/gc/swt.go +++ /dev/null @@ -1,840 +0,0 @@ -// Copyright 2009 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 gc - -import ( - "cmd/internal/obj" - "fmt" - "sort" - "strconv" -) - -const ( - // expression switch - switchKindExpr = iota // switch a {...} or switch 5 {...} - switchKindTrue // switch true {...} or switch {...} - switchKindFalse // switch false {...} - - // type switch - switchKindType // switch a.(type) {...} -) - -const ( - caseKindDefault = iota // default: - - // expression switch - caseKindExprConst // case 5: - caseKindExprVar // case x: - - // type switch - caseKindTypeNil // case nil: - caseKindTypeConst // case time.Time: (concrete type, has type hash) - caseKindTypeVar // case io.Reader: (interface type) -) - -const binarySearchMin = 4 // minimum number of cases for binary search - -// An exprSwitch walks an expression switch. -type exprSwitch struct { - exprname *Node // node for the expression being switched on - kind int // kind of switch statement (switchKind*) -} - -// A typeSwitch walks a type switch. -type typeSwitch struct { - hashname *Node // node for the hash of the type of the variable being switched on - facename *Node // node for the concrete type of the variable being switched on - okname *Node // boolean node used for comma-ok type assertions -} - -// A caseClause is a single case clause in a switch statement. -type caseClause struct { - node *Node // points at case statement - ordinal int // position in switch - hash uint32 // hash of a type switch - typ uint8 // type of case -} - -// typecheckswitch typechecks a switch statement. -func typecheckswitch(n *Node) { - lno := int(lineno) - typechecklist(n.Ninit, Etop) - - var nilonly string - var top int - var t *Type - - if n.Ntest != nil && n.Ntest.Op == OTYPESW { - // type switch - top = Etype - typecheck(&n.Ntest.Right, Erv) - t = n.Ntest.Right.Type - if t != nil && t.Etype != TINTER { - Yyerror("cannot type switch on non-interface value %v", Nconv(n.Ntest.Right, obj.FmtLong)) - } - } else { - // expression switch - top = Erv - if n.Ntest != nil { - typecheck(&n.Ntest, Erv) - defaultlit(&n.Ntest, nil) - t = n.Ntest.Type - } else { - t = Types[TBOOL] - } - if t != nil { - var badtype *Type - switch { - case !okforeq[t.Etype]: - Yyerror("cannot switch on %v", Nconv(n.Ntest, obj.FmtLong)) - case t.Etype == TARRAY && !Isfixedarray(t): - nilonly = "slice" - case t.Etype == TARRAY && Isfixedarray(t) && algtype1(t, nil) == ANOEQ: - Yyerror("cannot switch on %v", Nconv(n.Ntest, obj.FmtLong)) - case t.Etype == TSTRUCT && algtype1(t, &badtype) == ANOEQ: - Yyerror("cannot switch on %v (struct containing %v cannot be compared)", Nconv(n.Ntest, obj.FmtLong), badtype) - case t.Etype == TFUNC: - nilonly = "func" - case t.Etype == TMAP: - nilonly = "map" - } - } - } - - n.Type = t - - var def *Node - var ll *NodeList - for l := n.List; l != nil; l = l.Next { - ncase := l.N - setlineno(n) - if ncase.List == nil { - // default - if def != nil { - Yyerror("multiple defaults in switch (first at %v)", def.Line()) - } else { - def = ncase - } - } else { - for ll = ncase.List; ll != nil; ll = ll.Next { - setlineno(ll.N) - typecheck(&ll.N, Erv|Etype) - if ll.N.Type == nil || t == nil { - continue - } - setlineno(ncase) - switch top { - // expression switch - case Erv: - defaultlit(&ll.N, t) - switch { - case ll.N.Op == OTYPE: - Yyerror("type %v is not an expression", ll.N.Type) - case ll.N.Type != nil && assignop(ll.N.Type, t, nil) == 0 && assignop(t, ll.N.Type, nil) == 0: - if n.Ntest != nil { - Yyerror("invalid case %v in switch on %v (mismatched types %v and %v)", ll.N, n.Ntest, ll.N.Type, t) - } else { - Yyerror("invalid case %v in switch (mismatched types %v and bool)", ll.N, ll.N.Type) - } - case nilonly != "" && !Isconst(ll.N, CTNIL): - Yyerror("invalid case %v in switch (can only compare %s %v to nil)", ll.N, nilonly, n.Ntest) - } - - // type switch - case Etype: - var missing, have *Type - var ptr int - switch { - case ll.N.Op == OLITERAL && Istype(ll.N.Type, TNIL): - case ll.N.Op != OTYPE && ll.N.Type != nil: // should this be ||? - Yyerror("%v is not a type", Nconv(ll.N, obj.FmtLong)) - // reset to original type - ll.N = n.Ntest.Right - case ll.N.Type.Etype != TINTER && t.Etype == TINTER && !implements(ll.N.Type, t, &missing, &have, &ptr): - if have != nil && missing.Broke == 0 && have.Broke == 0 { - Yyerror("impossible type switch case: %v cannot have dynamic type %v"+" (wrong type for %v method)\n\thave %v%v\n\twant %v%v", Nconv(n.Ntest.Right, obj.FmtLong), ll.N.Type, missing.Sym, have.Sym, Tconv(have.Type, obj.FmtShort), missing.Sym, Tconv(missing.Type, obj.FmtShort)) - } else if missing.Broke == 0 { - Yyerror("impossible type switch case: %v cannot have dynamic type %v"+" (missing %v method)", Nconv(n.Ntest.Right, obj.FmtLong), ll.N.Type, missing.Sym) - } - } - } - } - } - - if top == Etype && n.Type != nil { - ll = ncase.List - nvar := ncase.Nname - 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) - } else { - // multiple entry type switch or default - nvar.Ntype = typenod(n.Type) - } - - typecheck(&nvar, Erv|Easgn) - ncase.Nname = nvar - } - } - - typechecklist(ncase.Nbody, Etop) - } - - lineno = int32(lno) -} - -// walkswitch walks a switch statement. -func walkswitch(sw *Node) { - // convert switch {...} to switch true {...} - if sw.Ntest == nil { - sw.Ntest = Nodbool(true) - typecheck(&sw.Ntest, Erv) - } - - if sw.Ntest.Op == OTYPESW { - var s typeSwitch - s.walk(sw) - } else { - var s exprSwitch - s.walk(sw) - } - - // Discard old AST elements. They can confuse racewalk. - sw.Ntest = nil - sw.List = nil -} - -// walk generates an AST implementing sw. -// sw is an expression switch. -// The AST is generally of the form of a linear -// search using if..goto, although binary search -// is used with long runs of constants. -func (s *exprSwitch) walk(sw *Node) { - casebody(sw, nil) - - s.kind = switchKindExpr - if Isconst(sw.Ntest, CTBOOL) { - s.kind = switchKindTrue - if !sw.Ntest.Val.U.(bool) { - s.kind = switchKindFalse - } - } - - walkexpr(&sw.Ntest, &sw.Ninit) - t := sw.Type - if t == nil { - return - } - - // convert the switch into OIF statements - var cas *NodeList - if s.kind == switchKindTrue || s.kind == switchKindFalse { - s.exprname = Nodbool(s.kind == switchKindTrue) - } else if consttype(sw.Ntest) >= 0 { - // leave constants to enable dead code elimination (issue 9608) - s.exprname = sw.Ntest - } else { - s.exprname = temp(sw.Ntest.Type) - cas = list1(Nod(OAS, s.exprname, sw.Ntest)) - typechecklist(cas, Etop) - } - - // enumerate the cases, and lop off the default case - cc := caseClauses(sw, s.kind) - var def *Node - if len(cc) > 0 && cc[0].typ == caseKindDefault { - def = cc[0].node.Right - cc = cc[1:] - } else { - def = Nod(OBREAK, nil, nil) - } - - // handle the cases in order - for len(cc) > 0 { - // deal with expressions one at a time - if !okforcmp[t.Etype] || cc[0].typ != caseKindExprConst { - a := s.walkCases(cc[:1]) - cas = list(cas, a) - cc = cc[1:] - continue - } - - // do binary search on runs of constants - var run int - for run = 1; run < len(cc) && cc[run].typ == caseKindExprConst; run++ { - } - - // sort and compile constants - sort.Sort(caseClauseByExpr(cc[:run])) - a := s.walkCases(cc[:run]) - cas = list(cas, a) - cc = cc[run:] - } - - // handle default case - if nerrors == 0 { - cas = list(cas, def) - sw.Nbody = concat(cas, sw.Nbody) - sw.List = nil - walkstmtlist(sw.Nbody) - } -} - -// walkCases generates an AST implementing the cases in cc. -func (s *exprSwitch) walkCases(cc []*caseClause) *Node { - if len(cc) < binarySearchMin { - // linear search - var cas *NodeList - for _, c := range cc { - n := c.node - lno := int(setlineno(n)) - - a := Nod(OIF, nil, nil) - if (s.kind != switchKindTrue && s.kind != switchKindFalse) || assignop(n.Left.Type, s.exprname.Type, nil) == OCONVIFACE || assignop(s.exprname.Type, n.Left.Type, nil) == OCONVIFACE { - a.Ntest = Nod(OEQ, s.exprname, n.Left) // if name == val - typecheck(&a.Ntest, Erv) - } else if s.kind == switchKindTrue { - a.Ntest = n.Left // if val - } else { - // s.kind == switchKindFalse - a.Ntest = Nod(ONOT, n.Left, nil) // if !val - typecheck(&a.Ntest, Erv) - } - a.Nbody = list1(n.Right) // goto l - - cas = list(cas, a) - lineno = int32(lno) - } - return liststmt(cas) - } - - // find the middle and recur - half := len(cc) / 2 - a := Nod(OIF, nil, nil) - mid := cc[half-1].node.Left - le := Nod(OLE, s.exprname, mid) - if Isconst(mid, CTSTR) { - // Search by length and then by value; see exprcmp. - lenlt := Nod(OLT, Nod(OLEN, s.exprname, nil), Nod(OLEN, mid, nil)) - leneq := Nod(OEQ, Nod(OLEN, s.exprname, nil), Nod(OLEN, mid, nil)) - a.Ntest = Nod(OOROR, lenlt, Nod(OANDAND, leneq, le)) - } else { - a.Ntest = le - } - typecheck(&a.Ntest, Erv) - a.Nbody = list1(s.walkCases(cc[:half])) - a.Nelse = list1(s.walkCases(cc[half:])) - return a -} - -// casebody builds separate lists of statements and cases. -// It makes labels between cases and statements -// and deals with fallthrough, break, and unreachable statements. -func casebody(sw *Node, typeswvar *Node) { - if sw.List == nil { - return - } - - lno := setlineno(sw) - - var cas *NodeList // cases - var stat *NodeList // statements - var def *Node // defaults - br := Nod(OBREAK, nil, nil) - - for l := sw.List; l != nil; l = l.Next { - n := l.N - setlineno(n) - if n.Op != OXCASE { - Fatal("casebody %v", Oconv(int(n.Op), 0)) - } - n.Op = OCASE - needvar := count(n.List) != 1 || n.List.N.Op == OLITERAL - - jmp := Nod(OGOTO, newCaseLabel(), nil) - if n.List == nil { - if def != nil { - Yyerror("more than one default case") - } - // reuse original default case - n.Right = jmp - def = n - } - - if n.List != nil && n.List.Next == nil { - // one case -- reuse OCASE node - n.Left = n.List.N - n.Right = jmp - n.List = nil - cas = list(cas, n) - } else { - // expand multi-valued cases - for lc := n.List; lc != nil; lc = lc.Next { - cas = list(cas, Nod(OCASE, lc.N, jmp)) - } - } - - stat = list(stat, Nod(OLABEL, jmp.Left, nil)) - if typeswvar != nil && needvar && n.Nname != nil { - l := list1(Nod(ODCL, n.Nname, nil)) - l = list(l, Nod(OAS, n.Nname, typeswvar)) - typechecklist(l, Etop) - stat = concat(stat, l) - } - stat = concat(stat, n.Nbody) - - // botch - shouldn't fall thru declaration - last := stat.End.N - if last.Xoffset == n.Xoffset && last.Op == OXFALL { - if typeswvar != nil { - setlineno(last) - Yyerror("cannot fallthrough in type switch") - } - - if l.Next == nil { - setlineno(last) - Yyerror("cannot fallthrough final case in switch") - } - - last.Op = OFALL - } else { - stat = list(stat, br) - } - } - - stat = list(stat, br) - if def != nil { - cas = list(cas, def) - } - - sw.List = cas - sw.Nbody = stat - lineno = lno -} - -// nSwitchLabel is the number of switch labels generated. -// This should be per-function, but it is a global counter for now. -var nSwitchLabel int - -func newCaseLabel() *Node { - label := strconv.Itoa(nSwitchLabel) - nSwitchLabel++ - return newname(Lookup(label)) -} - -// caseClauses generates a slice of caseClauses -// corresponding to the clauses in the switch statement sw. -// Kind is the kind of switch statement. -func caseClauses(sw *Node, kind int) []*caseClause { - var cc []*caseClause - for l := sw.List; l != nil; l = l.Next { - n := l.N - c := new(caseClause) - cc = append(cc, c) - c.ordinal = len(cc) - c.node = n - - if n.Left == nil { - c.typ = caseKindDefault - continue - } - - if kind == switchKindType { - // type switch - switch { - case n.Left.Op == OLITERAL: - c.typ = caseKindTypeNil - case Istype(n.Left.Type, TINTER): - c.typ = caseKindTypeVar - default: - c.typ = caseKindTypeConst - c.hash = typehash(n.Left.Type) - } - } else { - // expression switch - switch consttype(n.Left) { - case CTFLT, CTINT, CTRUNE, CTSTR: - c.typ = caseKindExprConst - default: - c.typ = caseKindExprVar - } - } - } - - if cc == nil { - return nil - } - - // sort by value and diagnose duplicate cases - if kind == switchKindType { - // type switch - sort.Sort(caseClauseByType(cc)) - for i, c1 := range cc { - if c1.typ == caseKindTypeNil || c1.typ == caseKindDefault { - break - } - for _, c2 := range cc[i+1:] { - if c2.typ == caseKindTypeNil || c2.typ == caseKindDefault || c1.hash != c2.hash { - break - } - if Eqtype(c1.node.Left.Type, c2.node.Left.Type) { - yyerrorl(int(c2.node.Lineno), "duplicate case %v in type switch\n\tprevious case at %v", c2.node.Left.Type, c1.node.Line()) - } - } - } - } else { - // expression switch - sort.Sort(caseClauseByExpr(cc)) - for i, c1 := range cc { - if i+1 == len(cc) { - break - } - c2 := cc[i+1] - if exprcmp(c1, c2) != 0 { - continue - } - setlineno(c2.node) - Yyerror("duplicate case %v in switch\n\tprevious case at %v", c1.node.Left, c1.node.Line()) - } - } - - // put list back in processing order - sort.Sort(caseClauseByOrd(cc)) - return cc -} - -// walk generates an AST that implements sw, -// where sw is a type switch. -// The AST is generally of the form of a linear -// search using if..goto, although binary search -// is used with long runs of concrete types. -func (s *typeSwitch) walk(sw *Node) { - if sw.Ntest == nil { - return - } - if sw.Ntest.Right == nil { - setlineno(sw) - Yyerror("type switch must have an assignment") - return - } - - walkexpr(&sw.Ntest.Right, &sw.Ninit) - if !Istype(sw.Ntest.Right.Type, TINTER) { - Yyerror("type switch must be on an interface") - return - } - - var cas *NodeList - - // predeclare temporary variables and the boolean var - s.facename = temp(sw.Ntest.Right.Type) - - a := Nod(OAS, s.facename, sw.Ntest.Right) - typecheck(&a, Etop) - cas = list(cas, a) - - s.okname = temp(Types[TBOOL]) - typecheck(&s.okname, Erv) - - s.hashname = temp(Types[TUINT32]) - typecheck(&s.hashname, Erv) - - // set up labels and jumps - casebody(sw, s.facename) - - // calculate type hash - t := sw.Ntest.Right.Type - if isnilinter(t) { - a = syslook("efacethash", 1) - } else { - a = syslook("ifacethash", 1) - } - substArgTypes(a, t) - a = Nod(OCALL, a, nil) - a.List = list1(s.facename) - a = Nod(OAS, s.hashname, a) - typecheck(&a, Etop) - cas = list(cas, a) - - cc := caseClauses(sw, switchKindType) - var def *Node - if len(cc) > 0 && cc[0].typ == caseKindDefault { - def = cc[0].node.Right - cc = cc[1:] - } else { - def = Nod(OBREAK, nil, nil) - } - - // insert type equality check into each case block - for _, c := range cc { - n := c.node - switch c.typ { - case caseKindTypeNil: - var v Val - v.Ctype = CTNIL - a = Nod(OIF, nil, nil) - a.Ntest = Nod(OEQ, s.facename, nodlit(v)) - typecheck(&a.Ntest, Erv) - a.Nbody = list1(n.Right) // if i==nil { goto l } - n.Right = a - - case caseKindTypeVar, caseKindTypeConst: - n.Right = s.typeone(n) - } - } - - // generate list of if statements, binary search for constant sequences - for len(cc) > 0 { - if cc[0].typ != caseKindTypeConst { - n := cc[0].node - cas = list(cas, n.Right) - cc = cc[1:] - continue - } - - // identify run of constants - var run int - for run = 1; run < len(cc) && cc[run].typ == caseKindTypeConst; run++ { - } - - // sort by hash - sort.Sort(caseClauseByType(cc[:run])) - - // for debugging: linear search - if false { - for i := 0; i < run; i++ { - n := cc[i].node - cas = list(cas, n.Right) - } - continue - } - - // combine adjacent cases with the same hash - ncase := 0 - for i := 0; i < run; i++ { - ncase++ - hash := list1(cc[i].node.Right) - for j := i + 1; j < run && cc[i].hash == cc[j].hash; j++ { - hash = list(hash, cc[j].node.Right) - } - cc[i].node.Right = liststmt(hash) - } - - // binary search among cases to narrow by hash - cas = list(cas, s.walkCases(cc[:ncase])) - cc = cc[ncase:] - } - - // handle default case - if nerrors == 0 { - cas = list(cas, def) - sw.Nbody = concat(cas, sw.Nbody) - sw.List = nil - walkstmtlist(sw.Nbody) - } -} - -// typeone generates an AST that jumps to the -// case body if the variable is of type t. -func (s *typeSwitch) typeone(t *Node) *Node { - name := t.Nname - var init *NodeList - if name == nil { - typecheck(&nblank, Erv|Easgn) - name = nblank - } else { - init = list1(Nod(ODCL, name, nil)) - } - - a := Nod(OAS2, nil, nil) - a.List = list(list1(name), s.okname) // name, ok = - b := Nod(ODOTTYPE, s.facename, nil) - b.Type = t.Left.Type // interface.(type) - a.Rlist = list1(b) - typecheck(&a, Etop) - init = list(init, a) - - c := Nod(OIF, nil, nil) - c.Ntest = s.okname - c.Nbody = list1(t.Right) // if ok { goto l } - - return liststmt(list(init, c)) -} - -// walkCases generates an AST implementing the cases in cc. -func (s *typeSwitch) walkCases(cc []*caseClause) *Node { - if len(cc) < binarySearchMin { - var cas *NodeList - for _, c := range cc { - n := c.node - if c.typ != caseKindTypeConst { - Fatal("typeSwitch walkCases") - } - a := Nod(OIF, nil, nil) - a.Ntest = Nod(OEQ, s.hashname, Nodintconst(int64(c.hash))) - typecheck(&a.Ntest, Erv) - a.Nbody = list1(n.Right) - cas = list(cas, a) - } - return liststmt(cas) - } - - // find the middle and recur - half := len(cc) / 2 - a := Nod(OIF, nil, nil) - a.Ntest = Nod(OLE, s.hashname, Nodintconst(int64(cc[half-1].hash))) - typecheck(&a.Ntest, Erv) - a.Nbody = list1(s.walkCases(cc[:half])) - a.Nelse = list1(s.walkCases(cc[half:])) - return a -} - -type caseClauseByOrd []*caseClause - -func (x caseClauseByOrd) Len() int { return len(x) } -func (x caseClauseByOrd) Swap(i, j int) { x[i], x[j] = x[j], x[i] } -func (x caseClauseByOrd) Less(i, j int) bool { - c1, c2 := x[i], x[j] - switch { - // sort default first - case c1.typ == caseKindDefault: - return true - case c2.typ == caseKindDefault: - return false - - // sort nil second - case c1.typ == caseKindTypeNil: - return true - case c2.typ == caseKindTypeNil: - return false - } - - // sort by ordinal - return c1.ordinal < c2.ordinal -} - -type caseClauseByExpr []*caseClause - -func (x caseClauseByExpr) Len() int { return len(x) } -func (x caseClauseByExpr) Swap(i, j int) { x[i], x[j] = x[j], x[i] } -func (x caseClauseByExpr) Less(i, j int) bool { - return exprcmp(x[i], x[j]) < 0 -} - -func exprcmp(c1, c2 *caseClause) int { - // sort non-constants last - if c1.typ != caseKindExprConst { - return +1 - } - if c2.typ != caseKindExprConst { - return -1 - } - - n1 := c1.node.Left - n2 := c2.node.Left - - // sort by type (for switches on interface) - ct := int(n1.Val.Ctype) - if ct > int(n2.Val.Ctype) { - return +1 - } - if ct < int(n2.Val.Ctype) { - return -1 - } - if !Eqtype(n1.Type, n2.Type) { - if n1.Type.Vargen > n2.Type.Vargen { - return +1 - } else { - return -1 - } - } - - // sort by constant value to enable binary search - switch ct { - case CTFLT: - return mpcmpfltflt(n1.Val.U.(*Mpflt), n2.Val.U.(*Mpflt)) - case CTINT, CTRUNE: - 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.(string) - b := n2.Val.U.(string) - if len(a) < len(b) { - return -1 - } - if len(a) > len(b) { - return +1 - } - return stringsCompare(a, b) - } - - return 0 -} - -type caseClauseByType []*caseClause - -func (x caseClauseByType) Len() int { return len(x) } -func (x caseClauseByType) Swap(i, j int) { x[i], x[j] = x[j], x[i] } -func (x caseClauseByType) Less(i, j int) bool { - c1, c2 := x[i], x[j] - switch { - // sort non-constants last - case c1.typ != caseKindTypeConst: - return false - case c2.typ != caseKindTypeConst: - return true - - // sort by hash code - case c1.hash != c2.hash: - return c1.hash < c2.hash - } - - // sort by ordinal - return c1.ordinal < c2.ordinal -} - -func dumpcase(cc []*caseClause) { - for _, c := range cc { - switch c.typ { - case caseKindDefault: - fmt.Printf("case-default\n") - fmt.Printf("\tord=%d\n", c.ordinal) - - case caseKindExprConst: - fmt.Printf("case-exprconst\n") - fmt.Printf("\tord=%d\n", c.ordinal) - - case caseKindExprVar: - fmt.Printf("case-exprvar\n") - fmt.Printf("\tord=%d\n", c.ordinal) - fmt.Printf("\top=%v\n", Oconv(int(c.node.Left.Op), 0)) - - case caseKindTypeNil: - fmt.Printf("case-typenil\n") - fmt.Printf("\tord=%d\n", c.ordinal) - - case caseKindTypeConst: - fmt.Printf("case-typeconst\n") - fmt.Printf("\tord=%d\n", c.ordinal) - fmt.Printf("\thash=%x\n", c.hash) - - case caseKindTypeVar: - fmt.Printf("case-typevar\n") - fmt.Printf("\tord=%d\n", c.ordinal) - - default: - fmt.Printf("case-???\n") - fmt.Printf("\tord=%d\n", c.ordinal) - fmt.Printf("\top=%v\n", Oconv(int(c.node.Left.Op), 0)) - fmt.Printf("\thash=%x\n", c.hash) - } - } - - fmt.Printf("\n") -} diff --git a/src/cmd/internal/gc/syntax.go b/src/cmd/internal/gc/syntax.go deleted file mode 100644 index 69348d1c2f..0000000000 --- a/src/cmd/internal/gc/syntax.go +++ /dev/null @@ -1,469 +0,0 @@ -// Copyright 2009 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. - -// “Abstract” syntax representation. - -package gc - -// A Node is a single node in the syntax tree. -// Actually the syntax tree is a syntax DAG, because there is only one -// node with Op=ONAME for a given instance of a variable x. -// The same is true for Op=OTYPE and Op=OLITERAL. -type Node struct { - // Tree structure. - // Generic recursive walks should follow these fields. - Left *Node - Right *Node - Ntest *Node - Nincr *Node - Ninit *NodeList - Nbody *NodeList - Nelse *NodeList - 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 - Addable bool // addressable - 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 - Colas bool // OAS resulting from := - Diag uint8 // already printed error about this - Noescape bool // func arguments do not escape; TODO(rsc): move Noescape to Func struct (see CL 7360) - Walkdef uint8 - Typecheck uint8 - Local bool - Dodata uint8 - Initorder uint8 - 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 -} - -// 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 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 - Byval bool // is the variable captured by value or by reference - 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 - Enter *NodeList - Exit *NodeList - Cvars *NodeList // closure params - Dcl *NodeList // autodcl for this func/closure - Inldcl *NodeList // copy of dcl for use in inlining - Closgen int - Outerfunc *Node - - Inl *NodeList // copy of the body for use in inlining - InlCost int32 - - Endlineno int32 - - Nosplit bool // func should not execute on separate stack - Nowritebarrier bool // emit compiler error instead of write barrier - Dupok bool // duplicate definitions ok - Wrapper bool // is method wrapper - Needctxt bool // function uses context register (has closure variables) -} - -// Node ops. -const ( - OXXX = iota - - // names - ONAME // var, const or func name - ONONAME // unnamed arg or return value: f(int, string) (int, error) { etc } - OTYPE // type name - OPACK // import - OLITERAL // literal - - // expressions - OADD // x + y - OSUB // x - y - OOR // x | y - OXOR // x ^ y - OADDSTR // s + "foo" - OADDR // &x - OANDAND // b0 && b1 - OAPPEND // append - OARRAYBYTESTR // string(bytes) - OARRAYBYTESTRTMP // string(bytes) ephemeral - OARRAYRUNESTR // string(runes) - OSTRARRAYBYTE // []byte(s) - OSTRARRAYBYTETMP // []byte(s) ephemeral - OSTRARRAYRUNE // []rune(s) - OAS // x = y or x := y - OAS2 // x, y, z = xx, yy, zz - OAS2FUNC // x, y = f() - OAS2RECV // x, ok = <-c - OAS2MAPR // x, ok = m["foo"] - OAS2DOTTYPE // x, ok = I.(int) - OASOP // x += y - OASWB // OAS but with write barrier - OCALL // function call, method call or type conversion, possibly preceded by defer or go. - OCALLFUNC // f() - OCALLMETH // t.Method() - OCALLINTER // err.Error() - OCALLPART // t.Method (without ()) - OCAP // cap - OCLOSE // close - OCLOSURE // f = func() { etc } - OCMPIFACE // err1 == err2 - OCMPSTR // s1 == s2 - OCOMPLIT // composite literal, typechecking may convert to a more specific OXXXLIT. - OMAPLIT // M{"foo":3, "bar":4} - OSTRUCTLIT // T{x:3, y:4} - OARRAYLIT // [2]int{3, 4} - OPTRLIT // &T{x:3, y:4} - OCONV // var i int; var u uint; i = int(u) - OCONVIFACE // I(t) - OCONVNOP // type Int int; var i int; var j Int; i = int(j) - OCOPY // copy - ODCL // var x int - ODCLFUNC // func f() or func (r) f() - ODCLFIELD // struct field, interface field, or func/method argument/return value. - ODCLCONST // const pi = 3.14 - ODCLTYPE // type Int int - ODELETE // delete - ODOT // t.x - ODOTPTR // p.x that is implicitly (*p).x - ODOTMETH // t.Method - ODOTINTER // err.Error - OXDOT // t.x, typechecking may convert to a more specific ODOTXXX. - ODOTTYPE // e = err.(MyErr) - ODOTTYPE2 // e, ok = err.(MyErr) - OEQ // x == y - ONE // x != y - OLT // x < y - OLE // x <= y - OGE // x >= y - OGT // x > y - OIND // *p - OINDEX // a[i] - OINDEXMAP // m[s] - OKEY // The x:3 in t{x:3, y:4}, the 1:2 in a[1:2], the 2:20 in [3]int{2:20}, etc. - OPARAM // The on-stack copy of a parameter or return value that escapes. - OLEN // len - OMAKE // make, typechecking may convert to a more specific OMAKEXXX. - OMAKECHAN // make(chan int) - OMAKEMAP // make(map[string]int) - OMAKESLICE // make([]int, 0) - OMUL // * - ODIV // x / y - OMOD // x % y - OLSH // x << u - ORSH // x >> u - OAND // x & y - OANDNOT // x &^ y - ONEW // new - ONOT // !b - OCOM // ^x - OPLUS // +x - OMINUS // -y - OOROR // b1 || b2 - OPANIC // panic - OPRINT // print - OPRINTN // println - OPAREN // (x) - OSEND // c <- x - OSLICE // v[1:2], typechecking may convert to a more specific OSLICEXXX. - OSLICEARR // a[1:2] - OSLICESTR // s[1:2] - OSLICE3 // v[1:2:3], typechecking may convert to OSLICE3ARR. - OSLICE3ARR // a[1:2:3] - ORECOVER // recover - ORECV // <-c - ORUNESTR // string(i) - OSELRECV // case x = <-c: - OSELRECV2 // case x, ok = <-c: - OIOTA // iota - OREAL // real - OIMAG // imag - OCOMPLEX // complex - - // statements - OBLOCK // block of code - OBREAK // break - OCASE // case, after being verified by swt.c's casebody. - OXCASE // case, before verification. - OCONTINUE // continue - ODEFER // defer - OEMPTY // no-op - OFALL // fallthrough, after being verified by swt.c's casebody. - OXFALL // fallthrough, before verification. - OFOR // for - OGOTO // goto - OIF // if - OLABEL // label: - OPROC // go - ORANGE // range - ORETURN // return - OSELECT // select - OSWITCH // switch x - OTYPESW // switch err.(type) - - // types - OTCHAN // chan int - OTMAP // map[string]int - OTSTRUCT // struct{} - OTINTER // interface{} - OTFUNC // func() - OTARRAY // []int, [8]int, [N]int or [...]int - - // misc - ODDD // func f(args ...int) or f(l...) or var a = [...]int{0, 1, 2}. - ODDDARG // func f(args ...int), introduced by escape analysis. - OINLCALL // intermediary representation of an inlined call. - OEFACE // itable and data words of an empty-interface value. - OITAB // itable word of an interface value. - OSPTR // base pointer of a slice or string. - OCLOSUREVAR // variable reference at beginning of closure function - OCFUNC // reference to c function pointer (not go func value) - OCHECKNIL // emit code to ensure pointer/interface not nil - OVARKILL // variable is dead - - // thearch-specific registers - OREGISTER // a register, such as AX. - OINDREG // offset plus indirect of a register, such as 8(SP). - - // arch-specific opcodes - OCMP // compare: ACMP. - ODEC // decrement: ADEC. - OINC // increment: AINC. - OEXTEND // extend: ACWD/ACDQ/ACQO. - OHMUL // high mul: AMUL/AIMUL for unsigned/signed (OMUL uses AIMUL for both). - OLROT // left rotate: AROL. - ORROTC // right rotate-carry: ARCR. - ORETJMP // return to other function - OPS // compare parity set (for x86 NaN check) - OPC // compare parity clear (for x86 NaN check) - OSQRT // sqrt(float64), on systems that have hw support - OGETG // runtime.getg() (read g pointer) - - OEND -) - -/* - * Every node has a walkgen field. - * If you want to do a traversal of a node graph that - * might contain duplicates and want to avoid - * visiting the same nodes twice, increment walkgen - * before starting. Then before processing a node, do - * - * if(n->walkgen == walkgen) - * return; - * n->walkgen = walkgen; - * - * Such a walk cannot call another such walk recursively, - * because of the use of the global walkgen. - */ -var walkgen uint32 - -// A NodeList is a linked list of nodes. -// TODO(rsc): Some uses of NodeList should be made into slices. -// The remaining ones probably just need a simple linked list, -// not one with concatenation support. -type NodeList struct { - N *Node - Next *NodeList - End *NodeList -} - -// concat returns the concatenation of the lists a and b. -// The storage taken by both is reused for the result. -func concat(a *NodeList, b *NodeList) *NodeList { - if a == nil { - return b - } - if b == nil { - return a - } - - a.End.Next = b - a.End = b.End - b.End = nil - return a -} - -// list1 returns a one-element list containing n. -func list1(n *Node) *NodeList { - if n == nil { - return nil - } - if n.Op == OBLOCK && n.Ninit == nil { - // Flatten list and steal storage. - // Poison pointer to catch errant uses. - l := n.List - - n.List = nil - return l - } - - l := new(NodeList) - l.N = n - l.End = l - return l -} - -// list returns the result of appending n to l. -func list(l *NodeList, n *Node) *NodeList { - return concat(l, list1(n)) -} - -// listsort sorts *l in place according to the 3-way comparison function f. -// The algorithm is mergesort, so it is guaranteed to be O(n log n). -func listsort(l **NodeList, f func(*Node, *Node) int) { - if *l == nil || (*l).Next == nil { - return - } - - l1 := *l - l2 := *l - for { - l2 = l2.Next - if l2 == nil { - break - } - l2 = l2.Next - if l2 == nil { - break - } - l1 = l1.Next - } - - l2 = l1.Next - l1.Next = nil - l2.End = (*l).End - (*l).End = l1 - - l1 = *l - listsort(&l1, f) - listsort(&l2, f) - - if f(l1.N, l2.N) < 0 { - *l = l1 - } else { - *l = l2 - l2 = l1 - l1 = *l - } - - // now l1 == *l; and l1 < l2 - - var le *NodeList - for (l1 != nil) && (l2 != nil) { - for (l1.Next != nil) && f(l1.Next.N, l2.N) < 0 { - l1 = l1.Next - } - - // l1 is last one from l1 that is < l2 - le = l1.Next // le is the rest of l1, first one that is >= l2 - if le != nil { - le.End = (*l).End - } - - (*l).End = l1 // cut *l at l1 - *l = concat(*l, l2) // glue l2 to *l's tail - - l1 = l2 // l1 is the first element of *l that is < the new l2 - l2 = le // ... because l2 now is the old tail of l1 - } - - *l = concat(*l, l2) // any remainder -} - -// count returns the length of the list l. -func count(l *NodeList) int { - n := int64(0) - for ; l != nil; l = l.Next { - n++ - } - if int64(int(n)) != n { // Overflow. - Yyerror("too many elements in list") - } - return int(n) -} diff --git a/src/cmd/internal/gc/typecheck.go b/src/cmd/internal/gc/typecheck.go deleted file mode 100644 index 0395ec5f5b..0000000000 --- a/src/cmd/internal/gc/typecheck.go +++ /dev/null @@ -1,4069 +0,0 @@ -// Copyright 2009 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 gc - -import ( - "cmd/internal/obj" - "fmt" - "math" - "strings" -) - -/* - * type check the whole tree of an expression. - * calculates expression types. - * evaluates compile time constants. - * marks variables that escape the local frame. - * rewrites n->op to be more specific in some cases. - */ -var typecheckdefstack *NodeList - -/* - * resolve ONONAME to definition, if any. - */ -func resolve(n *Node) *Node { - if n != nil && n.Op == ONONAME && n.Sym != nil { - r := n.Sym.Def - if r != nil { - if r.Op != OIOTA { - n = r - } else if n.Iota >= 0 { - n = Nodintconst(int64(n.Iota)) - } - } - } - - return n -} - -func typechecklist(l *NodeList, top int) { - for ; l != nil; l = l.Next { - typecheck(&l.N, top) - } -} - -var _typekind = []string{ - TINT: "int", - TUINT: "uint", - TINT8: "int8", - TUINT8: "uint8", - TINT16: "int16", - TUINT16: "uint16", - TINT32: "int32", - TUINT32: "uint32", - TINT64: "int64", - TUINT64: "uint64", - TUINTPTR: "uintptr", - TCOMPLEX64: "complex64", - TCOMPLEX128: "complex128", - TFLOAT32: "float32", - TFLOAT64: "float64", - TBOOL: "bool", - TSTRING: "string", - TPTR32: "pointer", - TPTR64: "pointer", - TUNSAFEPTR: "unsafe.Pointer", - TSTRUCT: "struct", - TINTER: "interface", - TCHAN: "chan", - TMAP: "map", - TARRAY: "array", - TFUNC: "func", - TNIL: "nil", - TIDEAL: "untyped number", -} - -func typekind(t *Type) string { - if Isslice(t) { - return "slice" - } - et := int(t.Etype) - if 0 <= et && et < len(_typekind) { - s := _typekind[et] - if s != "" { - return s - } - } - return fmt.Sprintf("etype=%d", et) -} - -/* - * sprint_depchain prints a dependency chain - * of nodes into fmt. - * It is used by typecheck in the case of OLITERAL nodes - * to print constant definition loops. - */ -func sprint_depchain(fmt_ *string, stack *NodeList, cur *Node, first *Node) { - for l := stack; l != nil; l = l.Next { - if l.N.Op == cur.Op { - if l.N != first { - sprint_depchain(fmt_, l.Next, l.N, first) - } - *fmt_ += fmt.Sprintf("\n\t%v: %v uses %v", l.N.Line(), l.N, cur) - return - } - } -} - -/* - * type check node *np. - * replaces *np with a new pointer in some cases. - * returns the final value of *np as a convenience. - */ - -var typecheck_tcstack *NodeList -var typecheck_tcfree *NodeList - -func typecheck(np **Node, top int) *Node { - // cannot type check until all the source has been parsed - if typecheckok == 0 { - Fatal("early typecheck") - } - - n := *np - if n == nil { - return nil - } - - lno := int(setlineno(n)) - - // Skip over parens. - for n.Op == OPAREN { - n = n.Left - } - - // Resolve definition of name and value of iota lazily. - n = resolve(n) - - *np = n - - // Skip typecheck if already done. - // But re-typecheck ONAME/OTYPE/OLITERAL/OPACK node in case context has changed. - if n.Typecheck == 1 { - switch n.Op { - case ONAME, OTYPE, OLITERAL, OPACK: - break - - default: - lineno = int32(lno) - return n - } - } - - if n.Typecheck == 2 { - // Typechecking loop. Trying printing a meaningful message, - // otherwise a stack trace of typechecking. - var fmt_ string - switch n.Op { - // We can already diagnose variables used as types. - case ONAME: - if top&(Erv|Etype) == Etype { - Yyerror("%v is not a type", n) - } - - case OLITERAL: - if top&(Erv|Etype) == Etype { - Yyerror("%v is not a type", n) - break - } - - fmt_ = "" - sprint_depchain(&fmt_, typecheck_tcstack, n, n) - yyerrorl(int(n.Lineno), "constant definition loop%s", fmt_) - } - - if nsavederrors+nerrors == 0 { - fmt_ = "" - for l := typecheck_tcstack; l != nil; l = l.Next { - fmt_ += fmt.Sprintf("\n\t%v %v", l.N.Line(), l.N) - } - Yyerror("typechecking loop involving %v%s", n, fmt_) - } - - lineno = int32(lno) - return n - } - - n.Typecheck = 2 - - var l *NodeList - if typecheck_tcfree != nil { - l = typecheck_tcfree - typecheck_tcfree = l.Next - } else { - l = new(NodeList) - } - l.Next = typecheck_tcstack - l.N = n - typecheck_tcstack = l - - typecheck1(&n, top) - *np = n - n.Typecheck = 1 - - if typecheck_tcstack != l { - Fatal("typecheck stack out of sync") - } - typecheck_tcstack = l.Next - l.Next = typecheck_tcfree - typecheck_tcfree = l - - lineno = int32(lno) - return n -} - -/* - * does n contain a call or receive operation? - */ -func callrecv(n *Node) bool { - if n == nil { - return false - } - - switch n.Op { - case OCALL, - OCALLMETH, - OCALLINTER, - OCALLFUNC, - ORECV, - OCAP, - OLEN, - OCOPY, - ONEW, - OAPPEND, - ODELETE: - return true - } - - return callrecv(n.Left) || callrecv(n.Right) || callrecv(n.Ntest) || callrecv(n.Nincr) || callrecvlist(n.Ninit) || callrecvlist(n.Nbody) || callrecvlist(n.Nelse) || callrecvlist(n.List) || callrecvlist(n.Rlist) -} - -func callrecvlist(l *NodeList) bool { - for ; l != nil; l = l.Next { - if callrecv(l.N) { - return true - } - } - return false -} - -// indexlit implements typechecking of untyped values as -// array/slice indexes. It is equivalent to defaultlit -// except for constants of numerical kind, which are acceptable -// whenever they can be represented by a value of type int. -func indexlit(np **Node) { - n := *np - if n == nil || !isideal(n.Type) { - return - } - switch consttype(n) { - case CTINT, CTRUNE, CTFLT, CTCPLX: - defaultlit(np, Types[TINT]) - } - - defaultlit(np, nil) -} - -func typecheck1(np **Node, top int) { - n := *np - defer func() { - *np = n - }() - - if n.Sym != nil { - if n.Op == ONAME && n.Etype != 0 && top&Ecall == 0 { - Yyerror("use of builtin %v not in function call", n.Sym) - n.Type = nil - return - } - - typecheckdef(n) - if n.Op == ONONAME { - n.Type = nil - return - } - } - - ok := 0 -OpSwitch: - switch n.Op { - // until typecheck is complete, do nothing. - default: - Dump("typecheck", n) - - Fatal("typecheck %v", Oconv(int(n.Op), 0)) - - /* - * names - */ - case OLITERAL: - ok |= Erv - - if n.Type == nil && n.Val.Ctype == CTSTR { - n.Type = idealstring - } - break OpSwitch - - case ONONAME: - ok |= Erv - break OpSwitch - - case ONAME: - if n.Name.Decldepth == 0 { - n.Name.Decldepth = decldepth - } - if n.Etype != 0 { - ok |= Ecall - break OpSwitch - } - - if top&Easgn == 0 { - // not a write to the variable - if isblank(n) { - Yyerror("cannot use _ as value") - n.Type = nil - return - } - - n.Used = true - } - - if top&Ecall == 0 && isunsafebuiltin(n) { - Yyerror("%v is not an expression, must be called", n) - n.Type = nil - return - } - - ok |= Erv - break OpSwitch - - case OPACK: - Yyerror("use of package %v without selector", n.Sym) - n.Type = nil - return - - case ODDD: - break - - /* - * types (OIND is with exprs) - */ - case OTYPE: - ok |= Etype - - if n.Type == nil { - n.Type = nil - return - } - - case OTARRAY: - ok |= Etype - t := typ(TARRAY) - l := n.Left - r := n.Right - if l == nil { - t.Bound = -1 // slice - } else if l.Op == ODDD { - t.Bound = -100 // to be filled in - if top&Ecomplit == 0 && n.Diag == 0 { - t.Broke = 1 - n.Diag = 1 - Yyerror("use of [...] array outside of array literal") - } - } else { - l := typecheck(&n.Left, Erv) - var v Val - switch consttype(l) { - case CTINT, CTRUNE: - v = l.Val - - case CTFLT: - v = toint(l.Val) - - default: - if l.Type != nil && Isint[l.Type.Etype] && l.Op != OLITERAL { - Yyerror("non-constant array bound %v", l) - } else { - Yyerror("invalid array bound %v", l) - } - n.Type = nil - return - } - - t.Bound = Mpgetfix(v.U.(*Mpint)) - if doesoverflow(v, Types[TINT]) { - Yyerror("array bound is too large") - n.Type = nil - return - } else if t.Bound < 0 { - Yyerror("array bound must be non-negative") - n.Type = nil - return - } - } - - typecheck(&r, Etype) - if r.Type == nil { - n.Type = nil - return - } - t.Type = r.Type - n.Op = OTYPE - n.Type = t - n.Left = nil - n.Right = nil - if t.Bound != -100 { - checkwidth(t) - } - - case OTMAP: - ok |= Etype - l := typecheck(&n.Left, Etype) - r := typecheck(&n.Right, Etype) - if l.Type == nil || r.Type == nil { - n.Type = nil - return - } - n.Op = OTYPE - n.Type = maptype(l.Type, r.Type) - n.Left = nil - n.Right = nil - - case OTCHAN: - ok |= Etype - l := typecheck(&n.Left, Etype) - if l.Type == nil { - n.Type = nil - return - } - t := typ(TCHAN) - t.Type = l.Type - t.Chan = n.Etype - n.Op = OTYPE - n.Type = t - n.Left = nil - n.Etype = 0 - - case OTSTRUCT: - ok |= Etype - n.Op = OTYPE - n.Type = tostruct(n.List) - if n.Type == nil || n.Type.Broke != 0 { - n.Type = nil - return - } - n.List = nil - - case OTINTER: - ok |= Etype - n.Op = OTYPE - n.Type = tointerface(n.List) - if n.Type == nil { - n.Type = nil - return - } - - case OTFUNC: - ok |= Etype - n.Op = OTYPE - n.Type = functype(n.Left, n.List, n.Rlist) - if n.Type == nil { - n.Type = nil - return - } - - /* - * type or expr - */ - case OIND: - ntop := Erv | Etype - - if top&Eaddr == 0 { // The *x in &*x is not an indirect. - ntop |= Eindir - } - ntop |= top & Ecomplit - l := typecheck(&n.Left, ntop) - t := l.Type - if t == nil { - n.Type = nil - return - } - if l.Op == OTYPE { - ok |= Etype - n.Op = OTYPE - n.Type = Ptrto(l.Type) - n.Left = nil - break OpSwitch - } - - if !Isptr[t.Etype] { - if top&(Erv|Etop) != 0 { - Yyerror("invalid indirect of %v", Nconv(n.Left, obj.FmtLong)) - n.Type = nil - return - } - - break OpSwitch - } - - ok |= Erv - n.Type = t.Type - break OpSwitch - - /* - * arithmetic exprs - */ - case OASOP, - OADD, - OAND, - OANDAND, - OANDNOT, - ODIV, - OEQ, - OGE, - OGT, - OLE, - OLT, - OLSH, - ORSH, - OMOD, - OMUL, - ONE, - OOR, - OOROR, - OSUB, - OXOR: - var l *Node - var op int - var r *Node - if n.Op == OASOP { - ok |= Etop - l = typecheck(&n.Left, Erv) - r = typecheck(&n.Right, Erv) - checkassign(n, n.Left) - if l.Type == nil || r.Type == nil { - n.Type = nil - return - } - op = int(n.Etype) - } else { - ok |= Erv - l = typecheck(&n.Left, Erv|top&Eiota) - r = typecheck(&n.Right, Erv|top&Eiota) - if l.Type == nil || r.Type == nil { - n.Type = nil - return - } - op = int(n.Op) - } - if op == OLSH || op == ORSH { - defaultlit(&r, Types[TUINT]) - n.Right = r - t := r.Type - if !Isint[t.Etype] || Issigned[t.Etype] { - Yyerror("invalid operation: %v (shift count type %v, must be unsigned integer)", n, r.Type) - n.Type = nil - return - } - - t = l.Type - if t != nil && t.Etype != TIDEAL && !Isint[t.Etype] { - Yyerror("invalid operation: %v (shift of type %v)", n, t) - n.Type = nil - return - } - - // no defaultlit for left - // the outer context gives the type - n.Type = l.Type - - break OpSwitch - } - - // ideal mixed with non-ideal - defaultlit2(&l, &r, 0) - - n.Left = l - n.Right = r - if l.Type == nil || r.Type == nil { - n.Type = nil - return - } - t := l.Type - if t.Etype == TIDEAL { - t = r.Type - } - et := int(t.Etype) - if et == TIDEAL { - et = TINT - } - aop := 0 - if iscmp[n.Op] && t.Etype != TIDEAL && !Eqtype(l.Type, r.Type) { - // comparison is okay as long as one side is - // assignable to the other. convert so they have - // the same type. - // - // the only conversion that isn't a no-op is concrete == interface. - // in that case, check comparability of the concrete type. - // The conversion allocates, so only do it if the concrete type is huge. - if r.Type.Etype != TBLANK { - aop = assignop(l.Type, r.Type, nil) - if aop != 0 { - if Isinter(r.Type) && !Isinter(l.Type) && algtype1(l.Type, nil) == ANOEQ { - Yyerror("invalid operation: %v (operator %v not defined on %s)", n, Oconv(int(op), 0), typekind(l.Type)) - n.Type = nil - return - } - - dowidth(l.Type) - if Isinter(r.Type) == Isinter(l.Type) || l.Type.Width >= 1<<16 { - l = Nod(aop, l, nil) - l.Type = r.Type - l.Typecheck = 1 - n.Left = l - } - - t = r.Type - goto converted - } - } - - if l.Type.Etype != TBLANK { - aop = assignop(r.Type, l.Type, nil) - if aop != 0 { - if Isinter(l.Type) && !Isinter(r.Type) && algtype1(r.Type, nil) == ANOEQ { - Yyerror("invalid operation: %v (operator %v not defined on %s)", n, Oconv(int(op), 0), typekind(r.Type)) - n.Type = nil - return - } - - dowidth(r.Type) - if Isinter(r.Type) == Isinter(l.Type) || r.Type.Width >= 1<<16 { - r = Nod(aop, r, nil) - r.Type = l.Type - r.Typecheck = 1 - n.Right = r - } - - t = l.Type - } - } - - converted: - et = int(t.Etype) - } - - if t.Etype != TIDEAL && !Eqtype(l.Type, r.Type) { - defaultlit2(&l, &r, 1) - if n.Op == OASOP && n.Implicit { - Yyerror("invalid operation: %v (non-numeric type %v)", n, l.Type) - n.Type = nil - return - } - - if Isinter(r.Type) == Isinter(l.Type) || aop == 0 { - Yyerror("invalid operation: %v (mismatched types %v and %v)", n, l.Type, r.Type) - n.Type = nil - return - } - } - - if !okfor[op][et] { - Yyerror("invalid operation: %v (operator %v not defined on %s)", n, Oconv(int(op), 0), typekind(t)) - n.Type = nil - return - } - - // okfor allows any array == array, map == map, func == func. - // restrict to slice/map/func == nil and nil == slice/map/func. - if Isfixedarray(l.Type) && algtype1(l.Type, nil) == ANOEQ { - Yyerror("invalid operation: %v (%v cannot be compared)", n, l.Type) - n.Type = nil - return - } - - if Isslice(l.Type) && !isnil(l) && !isnil(r) { - Yyerror("invalid operation: %v (slice can only be compared to nil)", n) - n.Type = nil - return - } - - if l.Type.Etype == TMAP && !isnil(l) && !isnil(r) { - Yyerror("invalid operation: %v (map can only be compared to nil)", n) - n.Type = nil - return - } - - if l.Type.Etype == TFUNC && !isnil(l) && !isnil(r) { - Yyerror("invalid operation: %v (func can only be compared to nil)", n) - n.Type = nil - return - } - - var badtype *Type - if l.Type.Etype == TSTRUCT && algtype1(l.Type, &badtype) == ANOEQ { - Yyerror("invalid operation: %v (struct containing %v cannot be compared)", n, badtype) - n.Type = nil - return - } - - t = l.Type - if iscmp[n.Op] { - evconst(n) - t = idealbool - if n.Op != OLITERAL { - defaultlit2(&l, &r, 1) - n.Left = l - n.Right = r - } - } else if n.Op == OANDAND || n.Op == OOROR { - if l.Type == r.Type { - t = l.Type - } else if l.Type == idealbool { - t = r.Type - } else if r.Type == idealbool { - t = l.Type - } - } else - // non-comparison operators on ideal bools should make them lose their ideal-ness - if t == idealbool { - t = Types[TBOOL] - } - - if et == TSTRING { - if iscmp[n.Op] { - n.Etype = n.Op - n.Op = OCMPSTR - } else if n.Op == OADD { - // create OADDSTR node with list of strings in x + y + z + (w + v) + ... - n.Op = OADDSTR - - if l.Op == OADDSTR { - n.List = l.List - } else { - n.List = list1(l) - } - if r.Op == OADDSTR { - n.List = concat(n.List, r.List) - } else { - n.List = list(n.List, r) - } - n.Left = nil - n.Right = nil - } - } - - if et == TINTER { - if l.Op == OLITERAL && l.Val.Ctype == CTNIL { - // swap for back end - n.Left = r - - n.Right = l - } else if r.Op == OLITERAL && r.Val.Ctype == CTNIL { - } else // leave alone for back end - if Isinter(r.Type) == Isinter(l.Type) { - n.Etype = n.Op - n.Op = OCMPIFACE - } - } - - if (op == ODIV || op == OMOD) && Isconst(r, CTINT) { - if mpcmpfixc(r.Val.U.(*Mpint), 0) == 0 { - Yyerror("division by zero") - n.Type = nil - return - } - } - - n.Type = t - break OpSwitch - - case OCOM, OMINUS, ONOT, OPLUS: - ok |= Erv - l := typecheck(&n.Left, Erv|top&Eiota) - t := l.Type - if t == nil { - n.Type = nil - return - } - if !okfor[n.Op][t.Etype] { - Yyerror("invalid operation: %v %v", Oconv(int(n.Op), 0), t) - n.Type = nil - return - } - - n.Type = t - break OpSwitch - - /* - * exprs - */ - case OADDR: - ok |= Erv - - typecheck(&n.Left, Erv|Eaddr) - if n.Left.Type == nil { - n.Type = nil - return - } - checklvalue(n.Left, "take the address of") - r := outervalue(n.Left) - 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.Orig != l && l.Op == ONAME { - Fatal("found non-orig name node %v", l) - } - l.Addrtaken = true - if l.Param != nil && l.Closure != nil { - l.Closure.Addrtaken = true - } - defaultlit(&n.Left, nil) - l = n.Left - t := l.Type - if t == nil { - n.Type = nil - return - } - n.Type = Ptrto(t) - break OpSwitch - - case OCOMPLIT: - ok |= Erv - typecheckcomplit(&n) - if n.Type == nil { - n.Type = nil - return - } - break OpSwitch - - case OXDOT, ODOT: - if n.Op == OXDOT { - n = adddot(n) - n.Op = ODOT - if n.Left == nil { - n.Type = nil - return - } - } - - typecheck(&n.Left, Erv|Etype) - - defaultlit(&n.Left, nil) - if n.Right.Op != ONAME { - Yyerror("rhs of . must be a name") // impossible - n.Type = nil - return - } - - t := n.Left.Type - if t == nil { - adderrorname(n) - n.Type = nil - return - } - - r := n.Right - - if n.Left.Op == OTYPE { - if !looktypedot(n, t, 0) { - if looktypedot(n, t, 1) { - Yyerror("%v undefined (cannot refer to unexported method %v)", n, n.Right.Sym) - } else { - Yyerror("%v undefined (type %v has no method %v)", n, t, n.Right.Sym) - } - n.Type = nil - return - } - - if n.Type.Etype != TFUNC || n.Type.Thistuple != 1 { - Yyerror("type %v has no method %v", n.Left.Type, Sconv(n.Right.Sym, obj.FmtShort)) - n.Type = nil - n.Type = nil - return - } - - 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 - n.Class = PFUNC - ok = Erv - break OpSwitch - } - - if Isptr[t.Etype] && t.Type.Etype != TINTER { - t = t.Type - if t == nil { - n.Type = nil - return - } - n.Op = ODOTPTR - checkwidth(t) - } - - if isblank(n.Right) { - Yyerror("cannot refer to blank field or method") - n.Type = nil - return - } - - if lookdot(n, t, 0) == nil { - // Legitimate field or method lookup failed, try to explain the error - switch { - case isnilinter(t): - Yyerror("%v undefined (type %v is interface with no methods)", n, n.Left.Type) - - case Isptr[t.Etype] && Isinter(t.Type): - // Pointer to interface is almost always a mistake. - Yyerror("%v undefined (type %v is pointer to interface, not interface)", n, n.Left.Type) - - case lookdot(n, t, 1) != nil: - // Field or method matches by name, but it is not exported. - Yyerror("%v undefined (cannot refer to unexported field or method %v)", n, n.Right.Sym) - - default: - if mt := lookdot(n, t, 2); mt != nil { // Case-insensitive lookup. - Yyerror("%v undefined (type %v has no field or method %v, but does have %v)", n, n.Left.Type, n.Right.Sym, mt.Sym) - } else { - Yyerror("%v undefined (type %v has no field or method %v)", n, n.Left.Type, n.Right.Sym) - } - } - n.Type = nil - return - } - - switch n.Op { - case ODOTINTER, ODOTMETH: - if top&Ecall != 0 { - ok |= Ecall - } else { - typecheckpartialcall(n, r) - ok |= Erv - } - - default: - ok |= Erv - } - - break OpSwitch - - case ODOTTYPE: - ok |= Erv - typecheck(&n.Left, Erv) - defaultlit(&n.Left, nil) - l := n.Left - t := l.Type - if t == nil { - n.Type = nil - return - } - if !Isinter(t) { - Yyerror("invalid type assertion: %v (non-interface type %v on left)", n, t) - n.Type = nil - return - } - - if n.Right != nil { - typecheck(&n.Right, Etype) - n.Type = n.Right.Type - n.Right = nil - if n.Type == nil { - n.Type = nil - return - } - } - - if n.Type != nil && n.Type.Etype != TINTER { - var have *Type - var missing *Type - var ptr int - if !implements(n.Type, t, &missing, &have, &ptr) { - if have != nil && have.Sym == missing.Sym { - Yyerror("impossible type assertion:\n\t%v does not implement %v (wrong type for %v method)\n"+"\t\thave %v%v\n\t\twant %v%v", n.Type, t, missing.Sym, have.Sym, Tconv(have.Type, obj.FmtShort|obj.FmtByte), missing.Sym, Tconv(missing.Type, obj.FmtShort|obj.FmtByte)) - } else if ptr != 0 { - Yyerror("impossible type assertion:\n\t%v does not implement %v (%v method has pointer receiver)", n.Type, t, missing.Sym) - } else if have != nil { - Yyerror("impossible type assertion:\n\t%v does not implement %v (missing %v method)\n"+"\t\thave %v%v\n\t\twant %v%v", n.Type, t, missing.Sym, have.Sym, Tconv(have.Type, obj.FmtShort|obj.FmtByte), missing.Sym, Tconv(missing.Type, obj.FmtShort|obj.FmtByte)) - } else { - Yyerror("impossible type assertion:\n\t%v does not implement %v (missing %v method)", n.Type, t, missing.Sym) - } - n.Type = nil - return - } - } - - break OpSwitch - - case OINDEX: - ok |= Erv - typecheck(&n.Left, Erv) - defaultlit(&n.Left, nil) - implicitstar(&n.Left) - l := n.Left - typecheck(&n.Right, Erv) - r := n.Right - t := l.Type - if t == nil || r.Type == nil { - n.Type = nil - return - } - switch t.Etype { - default: - Yyerror("invalid operation: %v (type %v does not support indexing)", n, t) - n.Type = nil - return - - case TSTRING, TARRAY: - indexlit(&n.Right) - if t.Etype == TSTRING { - n.Type = bytetype - } else { - n.Type = t.Type - } - why := "string" - if t.Etype == TARRAY { - if Isfixedarray(t) { - why = "array" - } else { - why = "slice" - } - } - - if n.Right.Type != nil && !Isint[n.Right.Type.Etype] { - Yyerror("non-integer %s index %v", why, n.Right) - break - } - - if Isconst(n.Right, CTINT) { - 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.(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) - } - } - - case TMAP: - n.Etype = 0 - defaultlit(&n.Right, t.Down) - if n.Right.Type != nil { - n.Right = assignconv(n.Right, t.Down, "map index") - } - n.Type = t.Type - n.Op = OINDEXMAP - } - - break OpSwitch - - case ORECV: - ok |= Etop | Erv - typecheck(&n.Left, Erv) - defaultlit(&n.Left, nil) - l := n.Left - t := l.Type - if t == nil { - n.Type = nil - return - } - if t.Etype != TCHAN { - Yyerror("invalid operation: %v (receive from non-chan type %v)", n, t) - n.Type = nil - return - } - - if t.Chan&Crecv == 0 { - Yyerror("invalid operation: %v (receive from send-only type %v)", n, t) - n.Type = nil - return - } - - n.Type = t.Type - break OpSwitch - - case OSEND: - ok |= Etop - l := typecheck(&n.Left, Erv) - typecheck(&n.Right, Erv) - defaultlit(&n.Left, nil) - l = n.Left - t := l.Type - if t == nil { - n.Type = nil - return - } - if t.Etype != TCHAN { - Yyerror("invalid operation: %v (send to non-chan type %v)", n, t) - n.Type = nil - return - } - - if t.Chan&Csend == 0 { - Yyerror("invalid operation: %v (send to receive-only type %v)", n, t) - n.Type = nil - return - } - - defaultlit(&n.Right, t.Type) - r := n.Right - if r.Type == nil { - n.Type = nil - return - } - n.Right = assignconv(r, l.Type.Type, "send") - - // TODO: more aggressive - n.Etype = 0 - - n.Type = nil - break OpSwitch - - case OSLICE: - ok |= Erv - typecheck(&n.Left, top) - typecheck(&n.Right.Left, Erv) - typecheck(&n.Right.Right, Erv) - defaultlit(&n.Left, nil) - indexlit(&n.Right.Left) - indexlit(&n.Right.Right) - l := n.Left - if Isfixedarray(l.Type) { - if !islvalue(n.Left) { - Yyerror("invalid operation %v (slice of unaddressable value)", n) - n.Type = nil - return - } - - n.Left = Nod(OADDR, n.Left, nil) - n.Left.Implicit = true - typecheck(&n.Left, Erv) - l = n.Left - } - - t := l.Type - if t == nil { - n.Type = nil - return - } - var tp *Type - if Istype(t, TSTRING) { - n.Type = t - n.Op = OSLICESTR - } else if Isptr[t.Etype] && Isfixedarray(t.Type) { - tp = t.Type - n.Type = typ(TARRAY) - n.Type.Type = tp.Type - n.Type.Bound = -1 - dowidth(n.Type) - n.Op = OSLICEARR - } else if Isslice(t) { - n.Type = t - } else { - Yyerror("cannot slice %v (type %v)", l, t) - n.Type = nil - return - } - - lo := n.Right.Left - if lo != nil && checksliceindex(l, lo, tp) < 0 { - n.Type = nil - return - } - hi := n.Right.Right - if hi != nil && checksliceindex(l, hi, tp) < 0 { - n.Type = nil - return - } - if checksliceconst(lo, hi) < 0 { - n.Type = nil - return - } - break OpSwitch - - case OSLICE3: - ok |= Erv - typecheck(&n.Left, top) - typecheck(&n.Right.Left, Erv) - typecheck(&n.Right.Right.Left, Erv) - typecheck(&n.Right.Right.Right, Erv) - defaultlit(&n.Left, nil) - indexlit(&n.Right.Left) - indexlit(&n.Right.Right.Left) - indexlit(&n.Right.Right.Right) - l := n.Left - if Isfixedarray(l.Type) { - if !islvalue(n.Left) { - Yyerror("invalid operation %v (slice of unaddressable value)", n) - n.Type = nil - return - } - - n.Left = Nod(OADDR, n.Left, nil) - n.Left.Implicit = true - typecheck(&n.Left, Erv) - l = n.Left - } - - t := l.Type - if t == nil { - n.Type = nil - return - } - if Istype(t, TSTRING) { - Yyerror("invalid operation %v (3-index slice of string)", n) - n.Type = nil - return - } - - var tp *Type - if Isptr[t.Etype] && Isfixedarray(t.Type) { - tp = t.Type - n.Type = typ(TARRAY) - n.Type.Type = tp.Type - n.Type.Bound = -1 - dowidth(n.Type) - n.Op = OSLICE3ARR - } else if Isslice(t) { - n.Type = t - } else { - Yyerror("cannot slice %v (type %v)", l, t) - n.Type = nil - return - } - - lo := n.Right.Left - if lo != nil && checksliceindex(l, lo, tp) < 0 { - n.Type = nil - return - } - mid := n.Right.Right.Left - if mid != nil && checksliceindex(l, mid, tp) < 0 { - n.Type = nil - return - } - hi := n.Right.Right.Right - if hi != nil && checksliceindex(l, hi, tp) < 0 { - n.Type = nil - return - } - if checksliceconst(lo, hi) < 0 || checksliceconst(lo, mid) < 0 || checksliceconst(mid, hi) < 0 { - n.Type = nil - return - } - break OpSwitch - - /* - * call and call like - */ - case OCALL: - l := n.Left - - if l.Op == ONAME { - r := unsafenmagic(n) - if r != nil { - if n.Isddd { - Yyerror("invalid use of ... with builtin %v", l) - } - n = r - typecheck1(&n, top) - return - } - } - - typecheck(&n.Left, Erv|Etype|Ecall|top&Eproc) - n.Diag |= n.Left.Diag - l = n.Left - if l.Op == ONAME && l.Etype != 0 { - if n.Isddd && l.Etype != OAPPEND { - Yyerror("invalid use of ... with builtin %v", l) - } - - // builtin: OLEN, OCAP, etc. - n.Op = l.Etype - - n.Left = n.Right - n.Right = nil - typecheck1(&n, top) - return - } - - defaultlit(&n.Left, nil) - l = n.Left - if l.Op == OTYPE { - if n.Isddd || l.Type.Bound == -100 { - if l.Type.Broke == 0 { - Yyerror("invalid use of ... in type conversion to %v", l.Type) - } - n.Diag = 1 - } - - // pick off before type-checking arguments - ok |= Erv - - // turn CALL(type, arg) into CONV(arg) w/ type - n.Left = nil - - n.Op = OCONV - n.Type = l.Type - if onearg(n, "conversion to %v", l.Type) < 0 { - n.Type = nil - return - } - typecheck1(&n, top) - return - } - - if count(n.List) == 1 && !n.Isddd { - typecheck(&n.List.N, Erv|Efnstruct) - } else { - typechecklist(n.List, Erv) - } - t := l.Type - if t == nil { - n.Type = nil - return - } - checkwidth(t) - - switch l.Op { - case ODOTINTER: - n.Op = OCALLINTER - - case ODOTMETH: - n.Op = OCALLMETH - - // typecheckaste was used here but there wasn't enough - // information further down the call chain to know if we - // were testing a method receiver for unexported fields. - // It isn't necessary, so just do a sanity check. - tp := getthisx(t).Type.Type - - if l.Left == nil || !Eqtype(l.Left.Type, tp) { - Fatal("method receiver") - } - - default: - n.Op = OCALLFUNC - if t.Etype != TFUNC { - Yyerror("cannot call non-function %v (type %v)", l, t) - n.Type = nil - return - } - } - - typecheckaste(OCALL, n.Left, n.Isddd, getinargx(t), n.List, func() string { return fmt.Sprintf("argument to %v", n.Left) }) - ok |= Etop - if t.Outtuple == 0 { - break OpSwitch - } - ok |= Erv - if t.Outtuple == 1 { - t := getoutargx(l.Type).Type - if t == nil { - n.Type = nil - return - } - if t.Etype == TFIELD { - t = t.Type - } - n.Type = t - - if n.Op == OCALLFUNC && n.Left.Op == ONAME && (compiling_runtime != 0 || n.Left.Sym.Pkg == Runtimepkg) && n.Left.Sym.Name == "getg" { - // Emit code for runtime.getg() directly instead of calling function. - // Most such rewrites (for example the similar one for math.Sqrt) should be done in walk, - // so that the ordering pass can make sure to preserve the semantics of the original code - // (in particular, the exact time of the function call) by introducing temporaries. - // In this case, we know getg() always returns the same result within a given function - // and we want to avoid the temporaries, so we do the rewrite earlier than is typical. - n.Op = OGETG - } - - break OpSwitch - } - - // multiple return - if top&(Efnstruct|Etop) == 0 { - Yyerror("multiple-value %v() in single-value context", l) - break OpSwitch - } - - n.Type = getoutargx(l.Type) - - break OpSwitch - - case OCAP, OLEN, OREAL, OIMAG: - ok |= Erv - if onearg(n, "%v", Oconv(int(n.Op), 0)) < 0 { - n.Type = nil - return - } - typecheck(&n.Left, Erv) - defaultlit(&n.Left, nil) - implicitstar(&n.Left) - l := n.Left - t := l.Type - if t == nil { - n.Type = nil - return - } - switch n.Op { - case OCAP: - if !okforcap[t.Etype] { - goto badcall1 - } - - case OLEN: - if !okforlen[t.Etype] { - goto badcall1 - } - - case OREAL, OIMAG: - if !Iscomplex[t.Etype] { - goto badcall1 - } - if Isconst(l, CTCPLX) { - r := n - if n.Op == OREAL { - n = nodfltconst(&l.Val.U.(*Mpcplx).Real) - } else { - n = nodfltconst(&l.Val.U.(*Mpcplx).Imag) - } - n.Orig = r - } - - n.Type = Types[cplxsubtype(int(t.Etype))] - break OpSwitch - } - - // might be constant - switch t.Etype { - case TSTRING: - if Isconst(l, CTSTR) { - r := Nod(OXXX, nil, nil) - Nodconst(r, Types[TINT], int64(len(l.Val.U.(string)))) - r.Orig = n - n = r - } - - case TARRAY: - if t.Bound < 0 { // slice - break - } - if callrecv(l) { // has call or receive - break - } - r := Nod(OXXX, nil, nil) - Nodconst(r, Types[TINT], t.Bound) - r.Orig = n - n = r - } - - n.Type = Types[TINT] - break OpSwitch - - badcall1: - Yyerror("invalid argument %v for %v", Nconv(n.Left, obj.FmtLong), Oconv(int(n.Op), 0)) - n.Type = nil - return - - case OCOMPLEX: - ok |= Erv - var r *Node - var l *Node - if count(n.List) == 1 { - typechecklist(n.List, Efnstruct) - if n.List.N.Op != OCALLFUNC && n.List.N.Op != OCALLMETH { - Yyerror("invalid operation: complex expects two arguments") - n.Type = nil - return - } - - t := n.List.N.Left.Type - if t.Outtuple != 2 { - Yyerror("invalid operation: complex expects two arguments, %v returns %d results", n.List.N, t.Outtuple) - n.Type = nil - return - } - - t = n.List.N.Type.Type - l = t.Nname - r = t.Down.Nname - } else { - if twoarg(n) < 0 { - n.Type = nil - return - } - l = typecheck(&n.Left, Erv|top&Eiota) - r = typecheck(&n.Right, Erv|top&Eiota) - if l.Type == nil || r.Type == nil { - n.Type = nil - return - } - defaultlit2(&l, &r, 0) - if l.Type == nil || r.Type == nil { - n.Type = nil - return - } - n.Left = l - n.Right = r - } - - if !Eqtype(l.Type, r.Type) { - Yyerror("invalid operation: %v (mismatched types %v and %v)", n, l.Type, r.Type) - n.Type = nil - return - } - - var t *Type - switch l.Type.Etype { - default: - Yyerror("invalid operation: %v (arguments have type %v, expected floating-point)", n, l.Type) - n.Type = nil - return - - case TIDEAL: - t = Types[TIDEAL] - - case TFLOAT32: - t = Types[TCOMPLEX64] - - case TFLOAT64: - t = Types[TCOMPLEX128] - } - - if l.Op == OLITERAL && r.Op == OLITERAL { - // make it a complex literal - r = nodcplxlit(l.Val, r.Val) - - r.Orig = n - n = r - } - - n.Type = t - break OpSwitch - - case OCLOSE: - if onearg(n, "%v", Oconv(int(n.Op), 0)) < 0 { - n.Type = nil - return - } - typecheck(&n.Left, Erv) - defaultlit(&n.Left, nil) - l := n.Left - t := l.Type - if t == nil { - n.Type = nil - return - } - if t.Etype != TCHAN { - Yyerror("invalid operation: %v (non-chan type %v)", n, t) - n.Type = nil - return - } - - if t.Chan&Csend == 0 { - Yyerror("invalid operation: %v (cannot close receive-only channel)", n) - n.Type = nil - return - } - - ok |= Etop - break OpSwitch - - case ODELETE: - args := n.List - if args == nil { - Yyerror("missing arguments to delete") - n.Type = nil - return - } - - if args.Next == nil { - Yyerror("missing second (key) argument to delete") - n.Type = nil - return - } - - if args.Next.Next != nil { - Yyerror("too many arguments to delete") - n.Type = nil - return - } - - ok |= Etop - typechecklist(args, Erv) - l := args.N - r := args.Next.N - if l.Type != nil && l.Type.Etype != TMAP { - Yyerror("first argument to delete must be map; have %v", Tconv(l.Type, obj.FmtLong)) - n.Type = nil - return - } - - args.Next.N = assignconv(r, l.Type.Down, "delete") - break OpSwitch - - case OAPPEND: - ok |= Erv - args := n.List - if args == nil { - Yyerror("missing arguments to append") - n.Type = nil - return - } - - if count(args) == 1 && !n.Isddd { - typecheck(&args.N, Erv|Efnstruct) - } else { - typechecklist(args, Erv) - } - - t := args.N.Type - if t == nil { - n.Type = nil - return - } - - // Unpack multiple-return result before type-checking. - var funarg *Type - if Istype(t, TSTRUCT) && t.Funarg != 0 { - funarg = t - t = t.Type.Type - } - - n.Type = t - if !Isslice(t) { - if Isconst(args.N, CTNIL) { - Yyerror("first argument to append must be typed slice; have untyped nil") - n.Type = nil - return - } - - Yyerror("first argument to append must be slice; have %v", Tconv(t, obj.FmtLong)) - n.Type = nil - return - } - - if n.Isddd { - if args.Next == nil { - Yyerror("cannot use ... on first argument to append") - n.Type = nil - return - } - - if args.Next.Next != nil { - Yyerror("too many arguments to append") - n.Type = nil - return - } - - if Istype(t.Type, TUINT8) && Istype(args.Next.N.Type, TSTRING) { - defaultlit(&args.Next.N, Types[TSTRING]) - break OpSwitch - } - - args.Next.N = assignconv(args.Next.N, t.Orig, "append") - break OpSwitch - } - - 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") - } - } - - break OpSwitch - - case OCOPY: - ok |= Etop | Erv - args := n.List - if args == nil || args.Next == nil { - Yyerror("missing arguments to copy") - n.Type = nil - return - } - - if args.Next.Next != nil { - Yyerror("too many arguments to copy") - n.Type = nil - return - } - - n.Left = args.N - n.Right = args.Next.N - n.List = nil - n.Type = Types[TINT] - typecheck(&n.Left, Erv) - typecheck(&n.Right, Erv) - if n.Left.Type == nil || n.Right.Type == nil { - n.Type = nil - return - } - defaultlit(&n.Left, nil) - defaultlit(&n.Right, nil) - if n.Left.Type == nil || n.Right.Type == nil { - n.Type = nil - return - } - - // copy([]byte, string) - if Isslice(n.Left.Type) && n.Right.Type.Etype == TSTRING { - if Eqtype(n.Left.Type.Type, bytetype) { - break OpSwitch - } - Yyerror("arguments to copy have different element types: %v and string", Tconv(n.Left.Type, obj.FmtLong)) - n.Type = nil - return - } - - if !Isslice(n.Left.Type) || !Isslice(n.Right.Type) { - if !Isslice(n.Left.Type) && !Isslice(n.Right.Type) { - Yyerror("arguments to copy must be slices; have %v, %v", Tconv(n.Left.Type, obj.FmtLong), Tconv(n.Right.Type, obj.FmtLong)) - } else if !Isslice(n.Left.Type) { - Yyerror("first argument to copy should be slice; have %v", Tconv(n.Left.Type, obj.FmtLong)) - } else { - Yyerror("second argument to copy should be slice or string; have %v", Tconv(n.Right.Type, obj.FmtLong)) - } - n.Type = nil - return - } - - if !Eqtype(n.Left.Type.Type, n.Right.Type.Type) { - Yyerror("arguments to copy have different element types: %v and %v", Tconv(n.Left.Type, obj.FmtLong), Tconv(n.Right.Type, obj.FmtLong)) - n.Type = nil - return - } - - break OpSwitch - - case OCONV: - ok |= Erv - saveorignode(n) - typecheck(&n.Left, Erv|top&(Eindir|Eiota)) - convlit1(&n.Left, n.Type, true) - t := n.Left.Type - if t == nil || n.Type == nil { - n.Type = nil - return - } - var why string - n.Op = uint8(convertop(t, n.Type, &why)) - if (n.Op) == 0 { - if n.Diag == 0 && n.Type.Broke == 0 { - Yyerror("cannot convert %v to type %v%s", Nconv(n.Left, obj.FmtLong), n.Type, why) - n.Diag = 1 - } - - n.Op = OCONV - } - - switch n.Op { - case OCONVNOP: - if n.Left.Op == OLITERAL && n.Type != Types[TBOOL] { - r := Nod(OXXX, nil, nil) - n.Op = OCONV - n.Orig = r - *r = *n - n.Op = OLITERAL - n.Val = n.Left.Val - } - - // do not use stringtoarraylit. - // generated code and compiler memory footprint is better without it. - case OSTRARRAYBYTE: - break - - case OSTRARRAYRUNE: - if n.Left.Op == OLITERAL { - stringtoarraylit(&n) - } - } - - break OpSwitch - - case OMAKE: - ok |= Erv - args := n.List - if args == nil { - Yyerror("missing argument to make") - n.Type = nil - return - } - - n.List = nil - l := args.N - args = args.Next - typecheck(&l, Etype) - t := l.Type - if t == nil { - n.Type = nil - return - } - - switch t.Etype { - default: - Yyerror("cannot make type %v", t) - n.Type = nil - return - - case TARRAY: - if !Isslice(t) { - Yyerror("cannot make type %v", t) - n.Type = nil - return - } - - if args == nil { - Yyerror("missing len argument to make(%v)", t) - n.Type = nil - return - } - - l = args.N - args = args.Next - typecheck(&l, Erv) - var r *Node - if args != nil { - r = args.N - args = args.Next - typecheck(&r, Erv) - } - - if l.Type == nil || (r != nil && r.Type == nil) { - n.Type = nil - return - } - et := obj.Bool2int(checkmake(t, "len", l) < 0) - et |= obj.Bool2int(r != nil && checkmake(t, "cap", r) < 0) - if et != 0 { - n.Type = nil - return - } - 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 - } - - n.Left = l - n.Right = r - n.Op = OMAKESLICE - - case TMAP: - if args != nil { - l = args.N - args = args.Next - typecheck(&l, Erv) - defaultlit(&l, Types[TINT]) - if l.Type == nil { - n.Type = nil - return - } - if checkmake(t, "size", l) < 0 { - n.Type = nil - return - } - n.Left = l - } else { - n.Left = Nodintconst(0) - } - n.Op = OMAKEMAP - - case TCHAN: - l = nil - if args != nil { - l = args.N - args = args.Next - typecheck(&l, Erv) - defaultlit(&l, Types[TINT]) - if l.Type == nil { - n.Type = nil - return - } - if checkmake(t, "buffer", l) < 0 { - n.Type = nil - return - } - n.Left = l - } else { - n.Left = Nodintconst(0) - } - n.Op = OMAKECHAN - } - - if args != nil { - Yyerror("too many arguments to make(%v)", t) - n.Op = OMAKE - n.Type = nil - return - } - - n.Type = t - break OpSwitch - - case ONEW: - ok |= Erv - args := n.List - if args == nil { - Yyerror("missing argument to new") - n.Type = nil - return - } - - l := args.N - typecheck(&l, Etype) - t := l.Type - if t == nil { - n.Type = nil - return - } - if args.Next != nil { - Yyerror("too many arguments to new(%v)", t) - n.Type = nil - return - } - - n.Left = l - n.Type = Ptrto(t) - break OpSwitch - - case OPRINT, OPRINTN: - ok |= Etop - typechecklist(n.List, Erv|Eindir) // Eindir: address does not escape - for args := n.List; args != nil; args = args.Next { - // Special case for print: int constant is int64, not int. - if Isconst(args.N, CTINT) { - defaultlit(&args.N, Types[TINT64]) - } else { - defaultlit(&args.N, nil) - } - } - - break OpSwitch - - case OPANIC: - ok |= Etop - if onearg(n, "panic") < 0 { - n.Type = nil - return - } - typecheck(&n.Left, Erv) - defaultlit(&n.Left, Types[TINTER]) - if n.Left.Type == nil { - n.Type = nil - return - } - break OpSwitch - - case ORECOVER: - ok |= Erv | Etop - if n.List != nil { - Yyerror("too many arguments to recover") - n.Type = nil - return - } - - n.Type = Types[TINTER] - break OpSwitch - - case OCLOSURE: - ok |= Erv - typecheckclosure(n, top) - if n.Type == nil { - n.Type = nil - return - } - break OpSwitch - - case OITAB: - ok |= Erv - typecheck(&n.Left, Erv) - t := n.Left.Type - if t == nil { - n.Type = nil - return - } - if t.Etype != TINTER { - Fatal("OITAB of %v", t) - } - n.Type = Ptrto(Types[TUINTPTR]) - break OpSwitch - - case OSPTR: - ok |= Erv - typecheck(&n.Left, Erv) - t := n.Left.Type - if t == nil { - n.Type = nil - return - } - if !Isslice(t) && t.Etype != TSTRING { - Fatal("OSPTR of %v", t) - } - if t.Etype == TSTRING { - n.Type = Ptrto(Types[TUINT8]) - } else { - n.Type = Ptrto(t.Type) - } - break OpSwitch - - case OCLOSUREVAR: - ok |= Erv - break OpSwitch - - case OCFUNC: - ok |= Erv - typecheck(&n.Left, Erv) - n.Type = Types[TUINTPTR] - break OpSwitch - - case OCONVNOP: - ok |= Erv - typecheck(&n.Left, Erv) - break OpSwitch - - /* - * statements - */ - case OAS: - ok |= Etop - - typecheckas(n) - - // Code that creates temps does not bother to set defn, so do it here. - if n.Left.Op == ONAME && strings.HasPrefix(n.Left.Sym.Name, "autotmp_") { - n.Left.Defn = n - } - break OpSwitch - - case OAS2: - ok |= Etop - typecheckas2(n) - break OpSwitch - - case OBREAK, - OCONTINUE, - ODCL, - OEMPTY, - OGOTO, - OXFALL, - OVARKILL: - ok |= Etop - break OpSwitch - - case OLABEL: - ok |= Etop - decldepth++ - break OpSwitch - - case ODEFER: - ok |= Etop - typecheck(&n.Left, Etop|Erv) - if n.Left.Diag == 0 { - checkdefergo(n) - } - break OpSwitch - - case OPROC: - ok |= Etop - typecheck(&n.Left, Etop|Eproc|Erv) - checkdefergo(n) - break OpSwitch - - case OFOR: - ok |= Etop - typechecklist(n.Ninit, Etop) - decldepth++ - typecheck(&n.Ntest, Erv) - if n.Ntest != nil { - t := n.Ntest.Type - if t != nil && t.Etype != TBOOL { - Yyerror("non-bool %v used as for condition", Nconv(n.Ntest, obj.FmtLong)) - } - } - typecheck(&n.Nincr, Etop) - typechecklist(n.Nbody, Etop) - decldepth-- - break OpSwitch - - case OIF: - ok |= Etop - typechecklist(n.Ninit, Etop) - typecheck(&n.Ntest, Erv) - if n.Ntest != nil { - t := n.Ntest.Type - if t != nil && t.Etype != TBOOL { - Yyerror("non-bool %v used as if condition", Nconv(n.Ntest, obj.FmtLong)) - } - } - typechecklist(n.Nbody, Etop) - typechecklist(n.Nelse, Etop) - break OpSwitch - - case ORETURN: - ok |= Etop - if count(n.List) == 1 { - typechecklist(n.List, Erv|Efnstruct) - } else { - typechecklist(n.List, Erv) - } - if Curfn == nil { - Yyerror("return outside function") - n.Type = nil - return - } - - if Curfn.Type.Outnamed != 0 && n.List == nil { - break OpSwitch - } - typecheckaste(ORETURN, nil, false, getoutargx(Curfn.Type), n.List, func() string { return "return argument" }) - break OpSwitch - - case ORETJMP: - ok |= Etop - break OpSwitch - - case OSELECT: - ok |= Etop - typecheckselect(n) - break OpSwitch - - case OSWITCH: - ok |= Etop - typecheckswitch(n) - break OpSwitch - - case ORANGE: - ok |= Etop - typecheckrange(n) - break OpSwitch - - case OTYPESW: - Yyerror("use of .(type) outside type switch") - n.Type = nil - return - - case OXCASE: - ok |= Etop - typechecklist(n.List, Erv) - typechecklist(n.Nbody, Etop) - break OpSwitch - - case ODCLFUNC: - ok |= Etop - typecheckfunc(n) - break OpSwitch - - case ODCLCONST: - ok |= Etop - typecheck(&n.Left, Erv) - break OpSwitch - - case ODCLTYPE: - ok |= Etop - typecheck(&n.Left, Etype) - if incannedimport == 0 { - checkwidth(n.Left.Type) - } - break OpSwitch - } - - t := n.Type - if t != nil && t.Funarg == 0 && n.Op != OTYPE { - switch t.Etype { - case TFUNC, // might have TANY; wait until its called - TANY, - TFORW, - TIDEAL, - TNIL, - TBLANK: - break - - default: - checkwidth(t) - } - } - - if safemode != 0 && incannedimport == 0 && importpkg == nil && compiling_wrappers == 0 && t != nil && t.Etype == TUNSAFEPTR { - Yyerror("cannot use unsafe.Pointer") - } - - evconst(n) - if n.Op == OTYPE && top&Etype == 0 { - Yyerror("type %v is not an expression", n.Type) - n.Type = nil - return - } - - if top&(Erv|Etype) == Etype && n.Op != OTYPE { - Yyerror("%v is not a type", n) - n.Type = nil - return - } - - // TODO(rsc): simplify - if (top&(Ecall|Erv|Etype) != 0) && top&Etop == 0 && ok&(Erv|Etype|Ecall) == 0 { - Yyerror("%v used as value", n) - n.Type = nil - return - } - - if (top&Etop != 0) && top&(Ecall|Erv|Etype) == 0 && ok&Etop == 0 { - if n.Diag == 0 { - Yyerror("%v evaluated but not used", n) - n.Diag = 1 - } - - n.Type = nil - return - } - - /* TODO - if(n->type == T) - fatal("typecheck nil type"); - */ -} - -func checksliceindex(l *Node, r *Node, tp *Type) int { - t := r.Type - if t == nil { - return -1 - } - if !Isint[t.Etype] { - Yyerror("invalid slice index %v (type %v)", r, t) - return -1 - } - - if r.Op == OLITERAL { - 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.(*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.(*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.(*Mpint), Maxintval[TINT]) > 0 { - Yyerror("invalid slice index %v (index too large)", r) - return -1 - } - } - - return 0 -} - -func checksliceconst(lo *Node, hi *Node) int { - 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 - } - - return 0 -} - -func checkdefergo(n *Node) { - what := "defer" - if n.Op == OPROC { - what = "go" - } - - switch n.Left.Op { - // ok - case OCALLINTER, - OCALLMETH, - OCALLFUNC, - OCLOSE, - OCOPY, - ODELETE, - OPANIC, - OPRINT, - OPRINTN, - ORECOVER: - return - - case OAPPEND, - OCAP, - OCOMPLEX, - OIMAG, - OLEN, - OMAKE, - OMAKESLICE, - OMAKECHAN, - OMAKEMAP, - ONEW, - OREAL, - OLITERAL: // conversion or unsafe.Alignof, Offsetof, Sizeof - if n.Left.Orig != nil && n.Left.Orig.Op == OCONV { - break - } - Yyerror("%s discards result of %v", what, n.Left) - return - } - - // type is broken or missing, most likely a method call on a broken type - // we will warn about the broken type elsewhere. no need to emit a potentially confusing error - if n.Left.Type == nil || n.Left.Type.Broke != 0 { - return - } - - if n.Diag == 0 { - // The syntax made sure it was a call, so this must be - // a conversion. - n.Diag = 1 - - Yyerror("%s requires function call, not conversion", what) - } -} - -func implicitstar(nn **Node) { - // insert implicit * if needed for fixed array - n := *nn - - t := n.Type - if t == nil || !Isptr[t.Etype] { - return - } - t = t.Type - if t == nil { - return - } - if !Isfixedarray(t) { - return - } - n = Nod(OIND, n, nil) - n.Implicit = true - typecheck(&n, Erv) - *nn = n -} - -func onearg(n *Node, f string, args ...interface{}) int { - if n.Left != nil { - return 0 - } - if n.List == nil { - p := fmt.Sprintf(f, args...) - Yyerror("missing argument to %s: %v", p, n) - return -1 - } - - if n.List.Next != nil { - p := fmt.Sprintf(f, args...) - Yyerror("too many arguments to %s: %v", p, n) - n.Left = n.List.N - n.List = nil - return -1 - } - - n.Left = n.List.N - n.List = nil - return 0 -} - -func twoarg(n *Node) int { - if n.Left != nil { - return 0 - } - if n.List == nil { - Yyerror("missing argument to %v - %v", Oconv(int(n.Op), 0), n) - return -1 - } - - n.Left = n.List.N - if n.List.Next == nil { - Yyerror("missing argument to %v - %v", Oconv(int(n.Op), 0), n) - n.List = nil - return -1 - } - - if n.List.Next.Next != nil { - Yyerror("too many arguments to %v - %v", Oconv(int(n.Op), 0), n) - n.List = nil - return -1 - } - - n.Right = n.List.Next.N - n.List = nil - return 0 -} - -func lookdot1(errnode *Node, s *Sym, t *Type, f *Type, dostrcmp int) *Type { - var r *Type - for ; f != nil; f = f.Down { - if dostrcmp != 0 && f.Sym.Name == s.Name { - return f - } - if dostrcmp == 2 && strings.EqualFold(f.Sym.Name, s.Name) { - return f - } - if f.Sym != s { - continue - } - if r != nil { - if errnode != nil { - Yyerror("ambiguous selector %v", errnode) - } else if Isptr[t.Etype] { - Yyerror("ambiguous selector (%v).%v", t, s) - } else { - Yyerror("ambiguous selector %v.%v", t, s) - } - break - } - - r = f - } - - return r -} - -func looktypedot(n *Node, t *Type, dostrcmp int) bool { - s := n.Right.Sym - - if t.Etype == TINTER { - f1 := lookdot1(n, s, t, t.Type, dostrcmp) - if f1 == nil { - return false - } - - n.Right = methodname(n.Right, t) - n.Xoffset = f1.Width - n.Type = f1.Type - n.Op = ODOTINTER - return true - } - - // Find the base type: methtype will fail if t - // is not of the form T or *T. - f2 := methtype(t, 0) - - if f2 == nil { - return false - } - - expandmeth(f2) - f2 = lookdot1(n, s, f2, f2.Xmethod, dostrcmp) - if f2 == nil { - return false - } - - // disallow T.m if m requires *T receiver - if Isptr[getthisx(f2.Type).Type.Type.Etype] && !Isptr[t.Etype] && f2.Embedded != 2 && !isifacemethod(f2.Type) { - Yyerror("invalid method expression %v (needs pointer receiver: (*%v).%v)", n, t, Sconv(f2.Sym, obj.FmtShort)) - return false - } - - n.Right = methodname(n.Right, t) - n.Xoffset = f2.Width - n.Type = f2.Type - n.Op = ODOTMETH - return true -} - -func derefall(t *Type) *Type { - for t != nil && int(t.Etype) == Tptr { - t = t.Type - } - return t -} - -func lookdot(n *Node, t *Type, dostrcmp int) *Type { - s := n.Right.Sym - - dowidth(t) - var f1 *Type - if t.Etype == TSTRUCT || t.Etype == TINTER { - f1 = lookdot1(n, s, t, t.Type, dostrcmp) - } - - var f2 *Type - if n.Left.Type == t || n.Left.Type.Sym == nil { - f2 = methtype(t, 0) - if f2 != nil { - // Use f2->method, not f2->xmethod: adddot has - // already inserted all the necessary embedded dots. - f2 = lookdot1(n, s, f2, f2.Method, dostrcmp) - } - } - - if f1 != nil { - if dostrcmp > 1 { - // Already in the process of diagnosing an error. - return f1 - } - if f2 != nil { - Yyerror("%v is both field and method", n.Right.Sym) - } - if f1.Width == BADWIDTH { - Fatal("lookdot badwidth %v %p", f1, f1) - } - n.Xoffset = f1.Width - n.Type = f1.Type - n.Paramfld = f1 - if t.Etype == TINTER { - if Isptr[n.Left.Type.Etype] { - n.Left = Nod(OIND, n.Left, nil) // implicitstar - n.Left.Implicit = true - typecheck(&n.Left, Erv) - } - - n.Op = ODOTINTER - } - - return f1 - } - - if f2 != nil { - if dostrcmp > 1 { - // Already in the process of diagnosing an error. - return f2 - } - tt := n.Left.Type - dowidth(tt) - rcvr := getthisx(f2.Type).Type.Type - if !Eqtype(rcvr, tt) { - if int(rcvr.Etype) == Tptr && Eqtype(rcvr.Type, tt) { - checklvalue(n.Left, "call pointer method on") - n.Left = Nod(OADDR, n.Left, nil) - n.Left.Implicit = true - typecheck(&n.Left, Etype|Erv) - } else if int(tt.Etype) == Tptr && int(rcvr.Etype) != Tptr && Eqtype(tt.Type, rcvr) { - n.Left = Nod(OIND, n.Left, nil) - n.Left.Implicit = true - typecheck(&n.Left, Etype|Erv) - } else if int(tt.Etype) == Tptr && int(tt.Type.Etype) == Tptr && Eqtype(derefall(tt), derefall(rcvr)) { - Yyerror("calling method %v with receiver %v requires explicit dereference", n.Right, Nconv(n.Left, obj.FmtLong)) - for int(tt.Etype) == Tptr { - // Stop one level early for method with pointer receiver. - if int(rcvr.Etype) == Tptr && int(tt.Type.Etype) != Tptr { - break - } - n.Left = Nod(OIND, n.Left, nil) - n.Left.Implicit = true - typecheck(&n.Left, Etype|Erv) - tt = tt.Type - } - } else { - Fatal("method mismatch: %v for %v", rcvr, tt) - } - } - - ll := n.Left - for ll.Left != nil { - ll = ll.Left - } - if ll.Implicit { - if Isptr[ll.Type.Etype] && ll.Type.Sym != nil && ll.Type.Sym.Def != nil && ll.Type.Sym.Def.Op == OTYPE { - // It is invalid to automatically dereference a named pointer type when selecting a method. - // Make n->left == ll to clarify error message. - n.Left = ll - return nil - } - } - - n.Right = methodname(n.Right, n.Left.Type) - n.Xoffset = f2.Width - n.Type = f2.Type - - // print("lookdot found [%p] %T\n", f2->type, f2->type); - n.Op = ODOTMETH - - return f2 - } - - return nil -} - -func nokeys(l *NodeList) bool { - for ; l != nil; l = l.Next { - if l.N.Op == OKEY { - return false - } - } - return true -} - -func hasddd(t *Type) bool { - for tl := t.Type; tl != nil; tl = tl.Down { - if tl.Isddd { - return true - } - } - - return false -} - -func downcount(t *Type) int { - n := 0 - for tl := t.Type; tl != nil; tl = tl.Down { - n++ - } - - return n -} - -/* - * typecheck assignment: type list = expression list - */ -func typecheckaste(op int, call *Node, isddd bool, tstruct *Type, nl *NodeList, desc func() string) { - var t *Type - var n *Node - var n1 int - var n2 int - - lno := int(lineno) - - if tstruct.Broke != 0 { - goto out - } - - n = nil - if nl != nil && nl.Next == nil { - n = nl.N - if n.Type != nil { - if n.Type.Etype == TSTRUCT && n.Type.Funarg != 0 { - if !hasddd(tstruct) { - n1 := downcount(tstruct) - n2 := downcount(n.Type) - if n2 > n1 { - goto toomany - } - if n2 < n1 { - goto notenough - } - } - - tn := n.Type.Type - var why string - for tl := tstruct.Type; tl != nil; tl = tl.Down { - if tl.Isddd { - for ; tn != nil; tn = tn.Down { - if assignop(tn.Type, tl.Type.Type, &why) == 0 { - if call != nil { - Yyerror("cannot use %v as type %v in argument to %v%s", tn.Type, tl.Type.Type, call, why) - } else { - Yyerror("cannot use %v as type %v in %s%s", tn.Type, tl.Type.Type, desc(), why) - } - } - } - - goto out - } - - if tn == nil { - goto notenough - } - if assignop(tn.Type, tl.Type, &why) == 0 { - if call != nil { - Yyerror("cannot use %v as type %v in argument to %v%s", tn.Type, tl.Type, call, why) - } else { - Yyerror("cannot use %v as type %v in %s%s", tn.Type, tl.Type, desc(), why) - } - } - - tn = tn.Down - } - - if tn != nil { - goto toomany - } - goto out - } - } - } - - n1 = downcount(tstruct) - n2 = count(nl) - if !hasddd(tstruct) { - if n2 > n1 { - goto toomany - } - if n2 < n1 { - goto notenough - } - } else { - if !isddd { - if n2 < n1-1 { - goto notenough - } - } else { - if n2 > n1 { - goto toomany - } - if n2 < n1 { - goto notenough - } - } - } - - for tl := tstruct.Type; tl != nil; tl = tl.Down { - t = tl.Type - if tl.Isddd { - if isddd { - if nl == nil { - goto notenough - } - if nl.Next != nil { - goto toomany - } - n = nl.N - setlineno(n) - if n.Type != nil { - nl.N = assignconvfn(n, t, desc) - } - goto out - } - - for ; nl != nil; nl = nl.Next { - n = nl.N - setlineno(nl.N) - if n.Type != nil { - nl.N = assignconvfn(n, t.Type, desc) - } - } - - goto out - } - - if nl == nil { - goto notenough - } - n = nl.N - setlineno(n) - if n.Type != nil { - nl.N = assignconvfn(n, t, desc) - } - nl = nl.Next - } - - if nl != nil { - goto toomany - } - if isddd { - if call != nil { - Yyerror("invalid use of ... in call to %v", call) - } else { - Yyerror("invalid use of ... in %v", Oconv(int(op), 0)) - } - } - -out: - lineno = int32(lno) - return - -notenough: - if n == nil || n.Diag == 0 { - if call != nil { - Yyerror("not enough arguments in call to %v", call) - } else { - Yyerror("not enough arguments to %v", Oconv(int(op), 0)) - } - if n != nil { - n.Diag = 1 - } - } - - goto out - -toomany: - if call != nil { - Yyerror("too many arguments in call to %v", call) - } else { - Yyerror("too many arguments to %v", Oconv(int(op), 0)) - } - goto out -} - -/* - * type check composite - */ -func fielddup(n *Node, hash map[string]bool) { - if n.Op != ONAME { - Fatal("fielddup: not ONAME") - } - name := n.Sym.Name - if hash[name] { - Yyerror("duplicate field name in struct literal: %s", name) - return - } - hash[name] = true -} - -func keydup(n *Node, hash []*Node) { - orign := n - if n.Op == OCONVIFACE { - n = n.Left - } - evconst(n) - if n.Op != OLITERAL { - return // we dont check variables - } - - var b uint32 - switch n.Val.Ctype { - default: // unknown, bool, nil - b = 23 - - case CTINT, CTRUNE: - b = uint32(Mpgetfix(n.Val.U.(*Mpint))) - - case CTFLT: - d := mpgetflt(n.Val.U.(*Mpflt)) - x := math.Float64bits(d) - for i := 0; i < 8; i++ { - b = b*PRIME1 + uint32(x&0xFF) - x >>= 8 - } - - case CTSTR: - b = 0 - s := n.Val.U.(string) - for i := len(n.Val.U.(string)); i > 0; i-- { - b = b*PRIME1 + uint32(s[0]) - s = s[1:] - } - } - - h := uint(b % uint32(len(hash))) - var cmp Node - for a := hash[h]; a != nil; a = a.Ntest { - cmp.Op = OEQ - cmp.Left = n - b = 0 - if a.Op == OCONVIFACE && orign.Op == OCONVIFACE { - if Eqtype(a.Left.Type, n.Type) { - cmp.Right = a.Left - evconst(&cmp) - 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.(bool))) - } - - if b != 0 { - Yyerror("duplicate key %v in map literal", n) - return - } - } - - orign.Ntest = hash[h] - hash[h] = orign -} - -func indexdup(n *Node, hash []*Node) { - if n.Op != OLITERAL { - Fatal("indexdup: not OLITERAL") - } - - 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.(*Mpint))) - if b == c { - Yyerror("duplicate index in array literal: %d", b) - return - } - } - - n.Ntest = hash[h] - hash[h] = n -} - -func prime(h uint32, sr uint32) bool { - for n := uint32(3); n <= sr; n += 2 { - if h%n == 0 { - return false - } - } - return true -} - -func inithash(n *Node, autohash []*Node) []*Node { - // count the number of entries - h := uint32(0) - - for ll := n.List; ll != nil; ll = ll.Next { - h++ - } - - // if the auto hash table is - // large enough use it. - if h <= uint32(len(autohash)) { - for i := range autohash { - autohash[i] = nil - } - return autohash - } - - // make hash size odd and 12% larger than entries - h += h / 8 - - h |= 1 - - // calculate sqrt of h - sr := h / 2 - - for i := 0; i < 5; i++ { - sr = (sr + h/sr) / 2 - } - - // check for primeality - for !prime(h, sr) { - h += 2 - } - - // build and return a throw-away hash table - return make([]*Node, h) -} - -func iscomptype(t *Type) bool { - switch t.Etype { - case TARRAY, TSTRUCT, TMAP: - return true - - case TPTR32, TPTR64: - switch t.Type.Etype { - case TARRAY, TSTRUCT, TMAP: - return true - } - } - - return false -} - -func pushtype(n *Node, t *Type) { - if n == nil || n.Op != OCOMPLIT || !iscomptype(t) { - return - } - - if n.Right == nil { - n.Right = typenod(t) - n.Implicit = true // don't print - n.Right.Implicit = true // * is okay - } else if Debug['s'] != 0 { - typecheck(&n.Right, Etype) - if n.Right.Type != nil && Eqtype(n.Right.Type, t) { - fmt.Printf("%v: redundant type: %v\n", n.Line(), t) - } - } -} - -func typecheckcomplit(np **Node) { - n := *np - lno := lineno - defer func() { - lineno = lno - *np = n - }() - - if n.Right == nil { - if n.List != nil { - setlineno(n.List.N) - } - Yyerror("missing type in composite literal") - n.Type = nil - return - } - - // Save original node (including n->right) - norig := Nod(int(n.Op), nil, nil) - - *norig = *n - - setlineno(n.Right) - l := typecheck(&n.Right, Etype|Ecomplit) /* sic */ - t := l.Type - if t == nil { - n.Type = nil - return - } - nerr := nerrors - n.Type = t - - if Isptr[t.Etype] { - // For better or worse, we don't allow pointers as the composite literal type, - // except when using the &T syntax, which sets implicit on the OIND. - if !n.Right.Implicit { - Yyerror("invalid pointer type %v for composite literal (use &%v instead)", t, t.Type) - n.Type = nil - return - } - - // Also, the underlying type must be a struct, map, slice, or array. - if !iscomptype(t) { - Yyerror("invalid pointer type %v for composite literal", t) - n.Type = nil - return - } - - t = t.Type - } - - var r *Node - switch t.Etype { - default: - Yyerror("invalid type for composite literal: %v", t) - n.Type = nil - - case TARRAY: - var autohash [101]*Node - hash := inithash(n, autohash[:]) - - length := int64(0) - i := 0 - var l *Node - for ll := n.List; ll != nil; ll = ll.Next { - l = ll.N - setlineno(l) - if l.Op != OKEY { - l = Nod(OKEY, Nodintconst(int64(i)), l) - l.Left.Type = Types[TINT] - l.Left.Typecheck = 1 - ll.N = l - } - - typecheck(&l.Left, Erv) - evconst(l.Left) - i = nonnegconst(l.Left) - if i < 0 && l.Left.Diag == 0 { - Yyerror("array index must be non-negative integer constant") - l.Left.Diag = 1 - i = -(1 << 30) // stay negative for a while - } - - if i >= 0 { - indexdup(l.Left, hash) - } - i++ - if int64(i) > length { - length = int64(i) - if t.Bound >= 0 && length > t.Bound { - setlineno(l) - Yyerror("array index %d out of bounds [0:%d]", length-1, t.Bound) - t.Bound = -1 // no more errors - } - } - - r = l.Right - pushtype(r, t.Type) - typecheck(&r, Erv) - defaultlit(&r, t.Type) - l.Right = assignconv(r, t.Type, "array element") - } - - if t.Bound == -100 { - t.Bound = length - } - if t.Bound < 0 { - n.Right = Nodintconst(length) - } - n.Op = OARRAYLIT - - case TMAP: - var autohash [101]*Node - hash := inithash(n, autohash[:]) - - var l *Node - for ll := n.List; ll != nil; ll = ll.Next { - l = ll.N - setlineno(l) - if l.Op != OKEY { - typecheck(&ll.N, Erv) - Yyerror("missing key in map literal") - continue - } - - typecheck(&l.Left, Erv) - defaultlit(&l.Left, t.Down) - l.Left = assignconv(l.Left, t.Down, "map key") - if l.Left.Op != OCONV { - keydup(l.Left, hash) - } - - r = l.Right - pushtype(r, t.Type) - typecheck(&r, Erv) - defaultlit(&r, t.Type) - l.Right = assignconv(r, t.Type, "map value") - } - - n.Op = OMAPLIT - - case TSTRUCT: - bad := 0 - if n.List != nil && nokeys(n.List) { - // simple list of variables - f := t.Type - - var s *Sym - for ll := n.List; ll != nil; ll = ll.Next { - setlineno(ll.N) - typecheck(&ll.N, Erv) - if f == nil { - tmp12 := bad - bad++ - if tmp12 == 0 { - Yyerror("too many values in struct initializer") - } - continue - } - - s = f.Sym - if s != nil && !exportname(s.Name) && s.Pkg != localpkg { - Yyerror("implicit assignment of unexported field '%s' in %v literal", s.Name, t) - } - - // No pushtype allowed here. Must name fields for that. - ll.N = assignconv(ll.N, f.Type, "field value") - - ll.N = Nod(OKEY, newname(f.Sym), ll.N) - ll.N.Left.Type = f - ll.N.Left.Typecheck = 1 - f = f.Down - } - - if f != nil { - Yyerror("too few values in struct initializer") - } - } else { - hash := make(map[string]bool) - - // keyed list - var s *Sym - var f *Type - var l *Node - var s1 *Sym - for ll := n.List; ll != nil; ll = ll.Next { - l = ll.N - setlineno(l) - if l.Op != OKEY { - tmp13 := bad - bad++ - if tmp13 == 0 { - Yyerror("mixture of field:value and value initializers") - } - typecheck(&ll.N, Erv) - continue - } - - s = l.Left.Sym - if s == nil { - Yyerror("invalid field name %v in struct initializer", l.Left) - typecheck(&l.Right, Erv) - continue - } - - // Sym might have resolved to name in other top-level - // package, because of import dot. Redirect to correct sym - // before we do the lookup. - if s.Pkg != localpkg && exportname(s.Name) { - s1 = Lookup(s.Name) - if s1.Origpkg == s.Pkg { - s = s1 - } - } - - f = lookdot1(nil, s, t, t.Type, 0) - if f == nil { - Yyerror("unknown %v field '%v' in struct literal", t, s) - continue - } - - l.Left = newname(s) - l.Left.Typecheck = 1 - l.Left.Type = f - s = f.Sym - fielddup(newname(s), hash) - r = l.Right - - // No pushtype allowed here. Tried and rejected. - typecheck(&r, Erv) - - l.Right = assignconv(r, f.Type, "field value") - } - } - - n.Op = OSTRUCTLIT - } - - if nerr != nerrors { - n.Type = nil - return - } - - n.Orig = norig - if Isptr[n.Type.Etype] { - n = Nod(OPTRLIT, n, nil) - n.Typecheck = 1 - n.Type = n.Left.Type - n.Left.Type = t - n.Left.Typecheck = 1 - } - - n.Orig = norig - return -} - -/* - * lvalue etc - */ -func islvalue(n *Node) bool { - switch n.Op { - case OINDEX: - if Isfixedarray(n.Left.Type) { - return islvalue(n.Left) - } - if n.Left.Type != nil && n.Left.Type.Etype == TSTRING { - return false - } - fallthrough - - // fall through - case OIND, ODOTPTR, OCLOSUREVAR, OPARAM: - return true - - case ODOT: - return islvalue(n.Left) - - case ONAME: - if n.Class == PFUNC { - return false - } - return true - } - - return false -} - -func checklvalue(n *Node, verb string) { - if !islvalue(n) { - Yyerror("cannot %s %v", verb, n) - } -} - -func checkassign(stmt *Node, n *Node) { - // Variables declared in ORANGE are assigned on every iteration. - if n.Defn != stmt || stmt.Op == ORANGE { - r := outervalue(n) - 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 - } - } - - l.Assigned = true - if l.Param != nil && l.Closure != nil { - l.Closure.Assigned = true - } - } - - if islvalue(n) { - return - } - if n.Op == OINDEXMAP { - n.Etype = 1 - return - } - - // have already complained about n being undefined - if n.Op == ONONAME { - return - } - - Yyerror("cannot assign to %v", n) -} - -func checkassignlist(stmt *Node, l *NodeList) { - for ; l != nil; l = l.Next { - checkassign(stmt, l.N) - } -} - -// Check whether l and r are the same side effect-free expression, -// so that it is safe to reuse one instead of computing both. -func samesafeexpr(l *Node, r *Node) bool { - if l.Op != r.Op || !Eqtype(l.Type, r.Type) { - return false - } - - switch l.Op { - case ONAME, OCLOSUREVAR: - return l == r - - case ODOT, ODOTPTR: - return l.Right != nil && r.Right != nil && l.Right.Sym == r.Right.Sym && samesafeexpr(l.Left, r.Left) - - case OIND: - return samesafeexpr(l.Left, r.Left) - - case OINDEX: - return samesafeexpr(l.Left, r.Left) && samesafeexpr(l.Right, r.Right) - } - - return false -} - -/* - * type check assignment. - * if this assignment is the definition of a var on the left side, - * fill in the var's type. - */ -func typecheckas(n *Node) { - // delicate little dance. - // the definition of n may refer to this assignment - // as its definition, in which case it will call typecheckas. - // in that case, do not call typecheck back, or it will cycle. - // if the variable has a type (ntype) then typechecking - // will not look at defn, so it is okay (and desirable, - // so that the conversion below happens). - n.Left = resolve(n.Left) - - if n.Left.Defn != n || n.Left.Ntype != nil { - typecheck(&n.Left, Erv|Easgn) - } - - typecheck(&n.Right, Erv) - checkassign(n, n.Left) - if n.Right != nil && n.Right.Type != nil { - if n.Left.Type != nil { - n.Right = assignconv(n.Right, n.Left.Type, "assignment") - } - } - - if n.Left.Defn == n && n.Left.Ntype == nil { - defaultlit(&n.Right, nil) - n.Left.Type = n.Right.Type - } - - // second half of dance. - // now that right is done, typecheck the left - // just to get it over with. see dance above. - n.Typecheck = 1 - - if n.Left.Typecheck == 0 { - typecheck(&n.Left, Erv|Easgn) - } -} - -func checkassignto(src *Type, dst *Node) { - var why string - - if assignop(src, dst.Type, &why) == 0 { - Yyerror("cannot assign %v to %v in multiple assignment%s", src, Nconv(dst, obj.FmtLong), why) - return - } -} - -func typecheckas2(n *Node) { - for ll := n.List; ll != nil; ll = ll.Next { - // delicate little dance. - ll.N = resolve(ll.N) - - if ll.N.Defn != n || ll.N.Ntype != nil { - typecheck(&ll.N, Erv|Easgn) - } - } - - cl := count(n.List) - cr := count(n.Rlist) - if cl > 1 && cr == 1 { - typecheck(&n.Rlist.N, Erv|Efnstruct) - } else { - typechecklist(n.Rlist, Erv) - } - checkassignlist(n, n.List) - - var l *Node - var r *Node - if cl == cr { - // easy - ll := n.List - lr := n.Rlist - for ; ll != nil; ll, lr = ll.Next, lr.Next { - 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 { - defaultlit(&lr.N, nil) - ll.N.Type = lr.N.Type - } - } - - goto out - } - - l = n.List.N - r = n.Rlist.N - - // x,y,z = f() - if cr == 1 { - if r.Type == nil { - goto out - } - switch r.Op { - case OCALLMETH, OCALLINTER, OCALLFUNC: - if r.Type.Etype != TSTRUCT || r.Type.Funarg == 0 { - break - } - cr = structcount(r.Type) - if cr != cl { - goto mismatch - } - n.Op = OAS2FUNC - var s Iter - t := Structfirst(&s, &r.Type) - for ll := n.List; ll != nil; ll = ll.Next { - if t.Type != nil && ll.N.Type != nil { - checkassignto(t.Type, ll.N) - } - if ll.N.Defn == n && ll.N.Ntype == nil { - ll.N.Type = t.Type - } - t = structnext(&s) - } - - goto out - } - } - - // x, ok = y - if cl == 2 && cr == 1 { - if r.Type == nil { - goto out - } - switch r.Op { - case OINDEXMAP, ORECV, ODOTTYPE: - switch r.Op { - case OINDEXMAP: - n.Op = OAS2MAPR - - case ORECV: - n.Op = OAS2RECV - - case ODOTTYPE: - n.Op = OAS2DOTTYPE - r.Op = ODOTTYPE2 - } - - if l.Type != nil { - checkassignto(r.Type, l) - } - if l.Defn == n { - l.Type = r.Type - } - l := n.List.Next.N - if l.Type != nil && l.Type.Etype != TBOOL { - checkassignto(Types[TBOOL], l) - } - if l.Defn == n && l.Ntype == nil { - l.Type = Types[TBOOL] - } - goto out - } - } - -mismatch: - Yyerror("assignment count mismatch: %d = %d", cl, cr) - - // second half of dance -out: - n.Typecheck = 1 - - for ll := n.List; ll != nil; ll = ll.Next { - if ll.N.Typecheck == 0 { - typecheck(&ll.N, Erv|Easgn) - } - } -} - -/* - * type check function definition - */ -func typecheckfunc(n *Node) { - typecheck(&n.Nname, Erv|Easgn) - t := n.Nname.Type - if t == nil { - return - } - n.Type = t - t.Nname = n.Nname - rcvr := getthisx(t).Type - if rcvr != nil && n.Func.Shortname != nil && !isblank(n.Func.Shortname) { - addmethod(n.Func.Shortname.Sym, t, true, n.Nname.Nointerface) - } - - 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.Name.Decldepth = 1 - } - } -} - -func stringtoarraylit(np **Node) { - n := *np - if n.Left.Op != OLITERAL || n.Left.Val.Ctype != CTSTR { - Fatal("stringtoarraylit %v", n) - } - - s := n.Left.Val.U.(string) - var l *NodeList - if n.Type.Type.Etype == TUINT8 { - // []byte - for i := 0; i < len(s); i++ { - l = list(l, Nod(OKEY, Nodintconst(int64(i)), Nodintconst(int64(s[0])))) - } - } else { - // []rune - i := 0 - for _, r := range s { - l = list(l, Nod(OKEY, Nodintconst(int64(i)), Nodintconst(int64(r)))) - i++ - } - } - - nn := Nod(OCOMPLIT, nil, typenod(n.Type)) - nn.List = l - typecheck(&nn, Erv) - *np = nn -} - -var ntypecheckdeftype int - -var methodqueue *NodeList - -func domethod(n *Node) { - nt := n.Type.Nname - typecheck(&nt, Etype) - if nt.Type == nil { - // type check failed; leave empty func - n.Type.Etype = TFUNC - - n.Type.Nod = nil - return - } - - // If we have - // type I interface { - // M(_ int) - // } - // then even though I.M looks like it doesn't care about the - // value of its argument, a specific implementation of I may - // care. The _ would suppress the assignment to that argument - // while generating a call, so remove it. - for t := getinargx(nt.Type).Type; t != nil; t = t.Down { - if t.Sym != nil && t.Sym.Name == "_" { - t.Sym = nil - } - } - - *n.Type = *nt.Type - n.Type.Nod = nil - checkwidth(n.Type) -} - -var mapqueue *NodeList - -func copytype(n *Node, t *Type) { - if t.Etype == TFORW { - // This type isn't computed yet; when it is, update n. - t.Copyto = list(t.Copyto, n) - - return - } - - maplineno := int(n.Type.Maplineno) - embedlineno := int(n.Type.Embedlineno) - - l := n.Type.Copyto - *n.Type = *t - - t = n.Type - t.Sym = n.Sym - t.Local = n.Local - t.Vargen = n.Vargen - t.Siggen = 0 - t.Method = nil - t.Xmethod = nil - t.Nod = nil - t.Printed = 0 - t.Deferwidth = 0 - t.Copyto = nil - - // Update nodes waiting on this type. - for ; l != nil; l = l.Next { - copytype(l.N, t) - } - - // Double-check use of type as embedded type. - lno := int(lineno) - - if embedlineno != 0 { - lineno = int32(embedlineno) - if Isptr[t.Etype] { - Yyerror("embedded type cannot be a pointer") - } - } - - lineno = int32(lno) - - // Queue check for map until all the types are done settling. - if maplineno != 0 { - t.Maplineno = int32(maplineno) - mapqueue = list(mapqueue, n) - } -} - -func typecheckdeftype(n *Node) { - ntypecheckdeftype++ - lno := int(lineno) - setlineno(n) - n.Type.Sym = n.Sym - n.Typecheck = 1 - typecheck(&n.Ntype, Etype) - t := n.Ntype.Type - if t == nil { - n.Diag = 1 - n.Type = nil - goto ret - } - - if n.Type == nil { - n.Diag = 1 - goto ret - } - - // copy new type and clear fields - // that don't come along. - // anything zeroed here must be zeroed in - // typedcl2 too. - copytype(n, t) - -ret: - lineno = int32(lno) - - // if there are no type definitions going on, it's safe to - // try to resolve the method types for the interfaces - // we just read. - if ntypecheckdeftype == 1 { - var l *NodeList - for { - l = methodqueue - if l == nil { - break - } - methodqueue = nil - for ; l != nil; l = l.Next { - domethod(l.N) - } - } - - for l := mapqueue; l != nil; l = l.Next { - lineno = l.N.Type.Maplineno - maptype(l.N.Type, Types[TBOOL]) - } - - lineno = int32(lno) - } - - ntypecheckdeftype-- -} - -func queuemethod(n *Node) { - if ntypecheckdeftype == 0 { - domethod(n) - return - } - - methodqueue = list(methodqueue, n) -} - -func typecheckdef(n *Node) *Node { - lno := int(lineno) - setlineno(n) - - if n.Op == ONONAME { - if n.Diag == 0 { - n.Diag = 1 - if n.Lineno != 0 { - lineno = n.Lineno - } - - // Note: adderrorname looks for this string and - // adds context about the outer expression - Yyerror("undefined: %v", n.Sym) - } - - return n - } - - if n.Walkdef == 1 { - return n - } - - l := new(NodeList) - l.N = n - l.Next = typecheckdefstack - typecheckdefstack = l - - if n.Walkdef == 2 { - Flusherrors() - fmt.Printf("typecheckdef loop:") - for l := typecheckdefstack; l != nil; l = l.Next { - fmt.Printf(" %v", l.N.Sym) - } - fmt.Printf("\n") - Fatal("typecheckdef loop") - } - - n.Walkdef = 2 - - if n.Type != nil || n.Sym == nil { // builtin or no name - goto ret - } - - switch n.Op { - default: - Fatal("typecheckdef %v", Oconv(int(n.Op), 0)) - - // not really syms - case OGOTO, OLABEL: - break - - case OLITERAL: - if n.Ntype != nil { - typecheck(&n.Ntype, Etype) - n.Type = n.Ntype.Type - n.Ntype = nil - if n.Type == nil { - n.Diag = 1 - goto ret - } - } - - e := n.Defn - n.Defn = nil - if e == nil { - lineno = n.Lineno - Dump("typecheckdef nil defn", n) - Yyerror("xxx") - } - - typecheck(&e, Erv|Eiota) - if Isconst(e, CTNIL) { - Yyerror("const initializer cannot be nil") - goto ret - } - - if e.Type != nil && e.Op != OLITERAL || !isgoconst(e) { - if e.Diag == 0 { - Yyerror("const initializer %v is not a constant", e) - e.Diag = 1 - } - - goto ret - } - - t := n.Type - if t != nil { - if !okforconst[t.Etype] { - Yyerror("invalid constant type %v", t) - goto ret - } - - if !isideal(e.Type) && !Eqtype(t, e.Type) { - Yyerror("cannot use %v as type %v in const initializer", Nconv(e, obj.FmtLong), t) - goto ret - } - - Convlit(&e, t) - } - - n.Val = e.Val - n.Type = e.Type - - case ONAME: - if n.Ntype != nil { - typecheck(&n.Ntype, Etype) - n.Type = n.Ntype.Type - - if n.Type == nil { - n.Diag = 1 - goto ret - } - } - - if n.Type != nil { - break - } - if n.Defn == nil { - if n.Etype != 0 { // like OPRINTN - break - } - if nsavederrors+nerrors > 0 { - // Can have undefined variables in x := foo - // that make x have an n->ndefn == nil. - // If there are other errors anyway, don't - // bother adding to the noise. - break - } - - Fatal("var without type, init: %v", n.Sym) - } - - if n.Defn.Op == ONAME { - typecheck(&n.Defn, Erv) - n.Type = n.Defn.Type - break - } - - typecheck(&n.Defn, Etop) // fills in n->type - - case OTYPE: - if Curfn != nil { - defercheckwidth() - } - n.Walkdef = 1 - n.Type = typ(TFORW) - n.Type.Sym = n.Sym - nerrors0 := nerrors - typecheckdeftype(n) - if n.Type.Etype == TFORW && nerrors > nerrors0 { - // Something went wrong during type-checking, - // but it was reported. Silence future errors. - n.Type.Broke = 1 - } - - if Curfn != nil { - resumecheckwidth() - } - - // nothing to see here - case OPACK: - break - } - -ret: - if n.Op != OLITERAL && n.Type != nil && isideal(n.Type) { - Fatal("got %v for %v", n.Type, n) - } - if typecheckdefstack.N != n { - Fatal("typecheckdefstack mismatch") - } - l = typecheckdefstack - typecheckdefstack = l.Next - - lineno = int32(lno) - n.Walkdef = 1 - return n -} - -func checkmake(t *Type, arg string, n *Node) int { - if n.Op == OLITERAL { - switch n.Val.Ctype { - case CTINT, CTRUNE, CTFLT, CTCPLX: - n.Val = toint(n.Val) - if mpcmpfixc(n.Val.U.(*Mpint), 0) < 0 { - Yyerror("negative %s argument in make(%v)", arg, t) - return -1 - } - - if Mpcmpfixfix(n.Val.U.(*Mpint), Maxintval[TINT]) > 0 { - Yyerror("%s argument too large in make(%v)", arg, t) - return -1 - } - - // Delay defaultlit until after we've checked range, to avoid - // a redundant "constant NNN overflows int" error. - defaultlit(&n, Types[TINT]) - - return 0 - - default: - break - } - } - - if !Isint[n.Type.Etype] && n.Type.Etype != TIDEAL { - Yyerror("non-integer %s argument in make(%v) - %v", arg, t, n.Type) - return -1 - } - - // Defaultlit still necessary for non-constant: n might be 1<left may be mutated by typechecking, check it explicitly - // first to track it correctly. - typecheck(&r.Left, Erv) - - base := r.Left - typecheck(&r, Erv) - switch r.Op { - case ODOT, ODOTPTR: - break - - case OCALLPART: - Yyerror("invalid expression %v: argument is a method value", nn) - v = 0 - goto ret - - default: - goto bad - } - - v = 0 - - // add offsets for inserted dots. - var r1 *Node - for r1 = r; r1.Left != base; r1 = r1.Left { - switch r1.Op { - case ODOT: - v += r1.Xoffset - - case ODOTPTR: - Yyerror("invalid expression %v: selector implies indirection of embedded %v", nn, r1.Left) - goto ret - - default: - Dump("unsafenmagic", r) - Fatal("impossible %v node after dot insertion", Oconv(int(r1.Op), obj.FmtSharp)) - goto bad - } - } - - v += r1.Xoffset - goto yes - } - - if s.Name == "Alignof" { - typecheck(&r, Erv) - defaultlit(&r, nil) - tr := r.Type - if tr == nil { - goto bad - } - - // make struct { byte; T; } - t := typ(TSTRUCT) - - t.Type = typ(TFIELD) - t.Type.Type = Types[TUINT8] - t.Type.Down = typ(TFIELD) - t.Type.Down.Type = tr - - // compute struct widths - dowidth(t) - - // the offset of T is its required alignment - v = t.Type.Down.Width - - goto yes - } - - return nil - -bad: - Yyerror("invalid expression %v", nn) - v = 0 - goto ret - -yes: - if args.Next != nil { - Yyerror("extra arguments for %v", s) - } - - // any side effects disappear; ignore init -ret: - var val Val - val.Ctype = CTINT - - val.U = new(Mpint) - Mpmovecfix(val.U.(*Mpint), v) - n := Nod(OLITERAL, nil, nil) - n.Orig = nn - n.Val = val - n.Type = Types[TUINTPTR] - nn.Type = Types[TUINTPTR] - return n -} - -func isunsafebuiltin(n *Node) bool { - if n == nil || n.Op != ONAME || n.Sym == nil || n.Sym.Pkg != unsafepkg { - return false - } - if n.Sym.Name == "Sizeof" { - return true - } - if n.Sym.Name == "Offsetof" { - return true - } - if n.Sym.Name == "Alignof" { - return true - } - return false -} diff --git a/src/cmd/internal/gc/util.go b/src/cmd/internal/gc/util.go deleted file mode 100644 index c59af0665b..0000000000 --- a/src/cmd/internal/gc/util.go +++ /dev/null @@ -1,103 +0,0 @@ -package gc - -import ( - "os" - "runtime" - "runtime/pprof" - "strconv" - "strings" -) - -func (n *Node) Line() string { - return Ctxt.LineHist.LineString(int(n.Lineno)) -} - -func atoi(s string) int { - // NOTE: Not strconv.Atoi, accepts hex and octal prefixes. - n, _ := strconv.ParseInt(s, 0, 0) - return int(n) -} - -func isalnum(c int) bool { - return isalpha(c) || isdigit(c) -} - -func isalpha(c int) bool { - return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' -} - -func isdigit(c int) bool { - return '0' <= c && c <= '9' -} - -func plan9quote(s string) string { - if s == "" { - return "'" + strings.Replace(s, "'", "''", -1) + "'" - } - for i := 0; i < len(s); i++ { - if s[i] <= ' ' || s[i] == '\'' { - return "'" + strings.Replace(s, "'", "''", -1) + "'" - } - } - return s -} - -// strings.Compare, introduced in Go 1.5. -func stringsCompare(a, b string) int { - if a == b { - return 0 - } - if a < b { - return -1 - } - return +1 -} - -var atExitFuncs []func() - -func AtExit(f func()) { - atExitFuncs = append(atExitFuncs, f) -} - -func Exit(code int) { - for i := len(atExitFuncs) - 1; i >= 0; i-- { - f := atExitFuncs[i] - atExitFuncs = atExitFuncs[:i] - f() - } - os.Exit(code) -} - -var ( - cpuprofile string - memprofile string - memprofilerate int64 -) - -func startProfile() { - if cpuprofile != "" { - f, err := os.Create(cpuprofile) - if err != nil { - Fatal("%v", err) - } - if err := pprof.StartCPUProfile(f); err != nil { - Fatal("%v", err) - } - AtExit(pprof.StopCPUProfile) - } - if memprofile != "" { - if memprofilerate != 0 { - runtime.MemProfileRate = int(memprofilerate) - } - f, err := os.Create(memprofile) - if err != nil { - Fatal("%v", err) - } - AtExit(func() { - runtime.GC() // profile all outstanding allocations - if err := pprof.WriteHeapProfile(f); err != nil { - Fatal("%v", err) - } - }) - } -} diff --git a/src/cmd/internal/gc/walk.go b/src/cmd/internal/gc/walk.go deleted file mode 100644 index b5b8611e5b..0000000000 --- a/src/cmd/internal/gc/walk.go +++ /dev/null @@ -1,4090 +0,0 @@ -// Copyright 2009 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 gc - -import ( - "cmd/internal/obj" - "fmt" - "strings" -) - -var mpzero Mpint - -// The constant is known to runtime. -const ( - tmpstringbufsize = 32 -) - -func walk(fn *Node) { - Curfn = fn - - if Debug['W'] != 0 { - s := fmt.Sprintf("\nbefore %v", Curfn.Nname.Sym) - dumplist(s, Curfn.Nbody) - } - - lno := int(lineno) - - // Final typecheck for any unused variables. - // It's hard to be on the heap when not-used, but best to be consistent about &~PHEAP here and below. - for l := fn.Func.Dcl; l != nil; l = l.Next { - if l.N.Op == ONAME && l.N.Class&^PHEAP == PAUTO { - typecheck(&l.N, Erv|Easgn) - } - } - - // Propagate the used flag for typeswitch variables up to the NONAME in it's definition. - for l := fn.Func.Dcl; l != nil; l = l.Next { - if l.N.Op == ONAME && l.N.Class&^PHEAP == PAUTO && l.N.Defn != nil && l.N.Defn.Op == OTYPESW && l.N.Used { - l.N.Defn.Left.Used = true - } - } - - for l := fn.Func.Dcl; l != nil; l = l.Next { - if l.N.Op != ONAME || l.N.Class&^PHEAP != PAUTO || l.N.Sym.Name[0] == '&' || l.N.Used { - continue - } - if l.N.Defn != nil && l.N.Defn.Op == OTYPESW { - if l.N.Defn.Left.Used { - continue - } - lineno = l.N.Defn.Left.Lineno - Yyerror("%v declared and not used", l.N.Sym) - l.N.Defn.Left.Used = true // suppress repeats - } else { - lineno = l.N.Lineno - Yyerror("%v declared and not used", l.N.Sym) - } - } - - lineno = int32(lno) - if nerrors != 0 { - return - } - walkstmtlist(Curfn.Nbody) - if Debug['W'] != 0 { - s := fmt.Sprintf("after walk %v", Curfn.Nname.Sym) - dumplist(s, Curfn.Nbody) - } - - heapmoves() - if Debug['W'] != 0 && Curfn.Func.Enter != nil { - s := fmt.Sprintf("enter %v", Curfn.Nname.Sym) - dumplist(s, Curfn.Func.Enter) - } -} - -func walkstmtlist(l *NodeList) { - for ; l != nil; l = l.Next { - walkstmt(&l.N) - } -} - -func samelist(a *NodeList, b *NodeList) bool { - for ; a != nil && b != nil; a, b = a.Next, b.Next { - if a.N != b.N { - return false - } - } - return a == b -} - -func paramoutheap(fn *Node) bool { - for l := fn.Func.Dcl; l != nil; l = l.Next { - switch l.N.Class { - case PPARAMOUT, - PPARAMOUT | PHEAP: - return l.N.Addrtaken - - // stop early - parameters are over - case PAUTO, - PAUTO | PHEAP: - return false - } - } - - return false -} - -// adds "adjust" to all the argument locations for the call n. -// n must be a defer or go node that has already been walked. -func adjustargs(n *Node, adjust int) { - var arg *Node - var lhs *Node - - callfunc := n.Left - for args := callfunc.List; args != nil; args = args.Next { - arg = args.N - if arg.Op != OAS { - Yyerror("call arg not assignment") - } - lhs = arg.Left - if lhs.Op == ONAME { - // This is a temporary introduced by reorder1. - // The real store to the stack appears later in the arg list. - continue - } - - if lhs.Op != OINDREG { - Yyerror("call argument store does not use OINDREG") - } - - // can't really check this in machine-indep code. - //if(lhs->val.u.reg != D_SP) - // yyerror("call arg assign not indreg(SP)"); - lhs.Xoffset += int64(adjust) - } -} - -func walkstmt(np **Node) { - n := *np - if n == nil { - return - } - if n.Dodata == 2 { // don't walk, generated by anylit. - return - } - - setlineno(n) - - walkstmtlist(n.Ninit) - - switch n.Op { - default: - if n.Op == ONAME { - Yyerror("%v is not a top level statement", n.Sym) - } else { - Yyerror("%v is not a top level statement", Oconv(int(n.Op), 0)) - } - Dump("nottop", n) - - case OAS, - OASOP, - OAS2, - OAS2DOTTYPE, - OAS2RECV, - OAS2FUNC, - OAS2MAPR, - OCLOSE, - OCOPY, - OCALLMETH, - OCALLINTER, - OCALL, - OCALLFUNC, - ODELETE, - OSEND, - OPRINT, - OPRINTN, - OPANIC, - OEMPTY, - ORECOVER, - OGETG: - if n.Typecheck == 0 { - Fatal("missing typecheck: %v", Nconv(n, obj.FmtSign)) - } - init := n.Ninit - n.Ninit = nil - walkexpr(&n, &init) - addinit(&n, init) - if (*np).Op == OCOPY && n.Op == OCONVNOP { - n.Op = OEMPTY // don't leave plain values as statements. - } - - // special case for a receive where we throw away - // the value received. - case ORECV: - if n.Typecheck == 0 { - Fatal("missing typecheck: %v", Nconv(n, obj.FmtSign)) - } - init := n.Ninit - n.Ninit = nil - - walkexpr(&n.Left, &init) - n = mkcall1(chanfn("chanrecv1", 2, n.Left.Type), nil, &init, typename(n.Left.Type), n.Left, nodnil()) - walkexpr(&n, &init) - - addinit(&n, init) - - case OBREAK, - ODCL, - OCONTINUE, - OFALL, - OGOTO, - OLABEL, - ODCLCONST, - ODCLTYPE, - OCHECKNIL, - OVARKILL: - break - - case OBLOCK: - walkstmtlist(n.List) - - case OXCASE: - Yyerror("case statement out of place") - n.Op = OCASE - fallthrough - - case OCASE: - walkstmt(&n.Right) - - case ODEFER: - Hasdefer = 1 - switch n.Left.Op { - case OPRINT, OPRINTN: - walkprintfunc(&n.Left, &n.Ninit) - - case OCOPY: - n.Left = copyany(n.Left, &n.Ninit, 1) - - default: - walkexpr(&n.Left, &n.Ninit) - } - - // make room for size & fn arguments. - adjustargs(n, 2*Widthptr) - - case OFOR: - if n.Ntest != nil { - walkstmtlist(n.Ntest.Ninit) - init := n.Ntest.Ninit - n.Ntest.Ninit = nil - walkexpr(&n.Ntest, &init) - addinit(&n.Ntest, init) - } - - walkstmt(&n.Nincr) - walkstmtlist(n.Nbody) - - case OIF: - walkexpr(&n.Ntest, &n.Ninit) - walkstmtlist(n.Nbody) - walkstmtlist(n.Nelse) - - case OPROC: - switch n.Left.Op { - case OPRINT, OPRINTN: - walkprintfunc(&n.Left, &n.Ninit) - - case OCOPY: - n.Left = copyany(n.Left, &n.Ninit, 1) - - default: - walkexpr(&n.Left, &n.Ninit) - } - - // make room for size & fn arguments. - adjustargs(n, 2*Widthptr) - - case ORETURN: - walkexprlist(n.List, &n.Ninit) - if n.List == nil { - break - } - if (Curfn.Type.Outnamed != 0 && count(n.List) > 1) || paramoutheap(Curfn) { - // assign to the function out parameters, - // so that reorder3 can fix up conflicts - var rl *NodeList - - var cl uint8 - for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next { - cl = ll.N.Class &^ PHEAP - if cl == PAUTO { - break - } - if cl == PPARAMOUT { - rl = list(rl, ll.N) - } - } - - if samelist(rl, n.List) { - // special return in disguise - n.List = nil - - break - } - - if count(n.List) == 1 && count(rl) > 1 { - // OAS2FUNC in disguise - f := n.List.N - - if f.Op != OCALLFUNC && f.Op != OCALLMETH && f.Op != OCALLINTER { - Fatal("expected return of call, have %v", f) - } - n.List = concat(list1(f), ascompatet(int(n.Op), rl, &f.Type, 0, &n.Ninit)) - break - } - - // move function calls out, to make reorder3's job easier. - walkexprlistsafe(n.List, &n.Ninit) - - ll := ascompatee(int(n.Op), rl, n.List, &n.Ninit) - n.List = reorder3(ll) - break - } - - ll := ascompatte(int(n.Op), nil, false, Getoutarg(Curfn.Type), n.List, 1, &n.Ninit) - n.List = ll - - case ORETJMP: - break - - case OSELECT: - walkselect(n) - - case OSWITCH: - walkswitch(n) - - case ORANGE: - walkrange(n) - - case OXFALL: - Yyerror("fallthrough statement out of place") - n.Op = OFALL - } - - if n.Op == ONAME { - Fatal("walkstmt ended up with name: %v", Nconv(n, obj.FmtSign)) - } - - *np = n -} - -/* - * walk the whole tree of the body of an - * expression or simple statement. - * the types expressions are calculated. - * compile-time constants are evaluated. - * complex side effects like statements are appended to init - */ -func walkexprlist(l *NodeList, init **NodeList) { - for ; l != nil; l = l.Next { - walkexpr(&l.N, init) - } -} - -func walkexprlistsafe(l *NodeList, init **NodeList) { - for ; l != nil; l = l.Next { - l.N = safeexpr(l.N, init) - walkexpr(&l.N, init) - } -} - -func walkexprlistcheap(l *NodeList, init **NodeList) { - for ; l != nil; l = l.Next { - l.N = cheapexpr(l.N, init) - walkexpr(&l.N, init) - } -} - -func walkexpr(np **Node, init **NodeList) { - n := *np - - if n == nil { - return - } - - if init == &n.Ninit { - // not okay to use n->ninit when walking n, - // because we might replace n with some other node - // and would lose the init list. - Fatal("walkexpr init == &n->ninit") - } - - if n.Ninit != nil { - walkstmtlist(n.Ninit) - *init = concat(*init, n.Ninit) - n.Ninit = nil - } - - // annoying case - not typechecked - if n.Op == OKEY { - walkexpr(&n.Left, init) - walkexpr(&n.Right, init) - return - } - - lno := setlineno(n) - - if Debug['w'] > 1 { - Dump("walk-before", n) - } - - if n.Typecheck != 1 { - Fatal("missed typecheck: %v\n", Nconv(n, obj.FmtSign)) - } - - switch n.Op { - default: - Dump("walk", n) - Fatal("walkexpr: switch 1 unknown op %v", Nconv(n, obj.FmtShort|obj.FmtSign)) - - case OTYPE, - ONONAME, - OINDREG, - OEMPTY, - OPARAM, - OGETG: - goto ret - - case ONOT, - OMINUS, - OPLUS, - OCOM, - OREAL, - OIMAG, - ODOTMETH, - ODOTINTER: - walkexpr(&n.Left, init) - goto ret - - case OIND: - walkexpr(&n.Left, init) - goto ret - - case ODOT: - usefield(n) - walkexpr(&n.Left, init) - goto ret - - case ODOTPTR: - usefield(n) - if n.Op == ODOTPTR && n.Left.Type.Type.Width == 0 { - // No actual copy will be generated, so emit an explicit nil check. - n.Left = cheapexpr(n.Left, init) - - checknil(n.Left, init) - } - - walkexpr(&n.Left, init) - goto ret - - case OEFACE: - walkexpr(&n.Left, init) - walkexpr(&n.Right, init) - goto ret - - case OSPTR, OITAB: - walkexpr(&n.Left, init) - goto ret - - case OLEN, OCAP: - walkexpr(&n.Left, init) - - // replace len(*[10]int) with 10. - // delayed until now to preserve side effects. - t := n.Left.Type - - if Isptr[t.Etype] { - t = t.Type - } - if Isfixedarray(t) { - safeexpr(n.Left, init) - Nodconst(n, n.Type, t.Bound) - n.Typecheck = 1 - } - - goto ret - - case OLSH, ORSH: - walkexpr(&n.Left, init) - walkexpr(&n.Right, init) - t := n.Left.Type - n.Bounded = bounded(n.Right, 8*t.Width) - if Debug['m'] != 0 && n.Etype != 0 && !Isconst(n.Right, CTINT) { - Warn("shift bounds check elided") - } - goto ret - - // Use results from call expression as arguments for complex. - case OAND, - OSUB, - OHMUL, - OLT, - OLE, - OGE, - OGT, - OADD, - OCOMPLEX, - OLROT: - if n.Op == OCOMPLEX && n.Left == nil && n.Right == nil { - n.Left = n.List.N - n.Right = n.List.Next.N - } - - walkexpr(&n.Left, init) - walkexpr(&n.Right, init) - goto ret - - case OOR, OXOR: - walkexpr(&n.Left, init) - walkexpr(&n.Right, init) - walkrotate(&n) - goto ret - - case OEQ, ONE: - walkexpr(&n.Left, init) - walkexpr(&n.Right, init) - - // Disable safemode while compiling this code: the code we - // generate internally can refer to unsafe.Pointer. - // In this case it can happen if we need to generate an == - // for a struct containing a reflect.Value, which itself has - // an unexported field of type unsafe.Pointer. - old_safemode := safemode - - safemode = 0 - walkcompare(&n, init) - safemode = old_safemode - goto ret - - case OANDAND, OOROR: - walkexpr(&n.Left, init) - - // cannot put side effects from n.Right on init, - // because they cannot run before n.Left is checked. - // save elsewhere and store on the eventual n.Right. - var ll *NodeList - - walkexpr(&n.Right, &ll) - addinit(&n.Right, ll) - goto ret - - case OPRINT, OPRINTN: - walkexprlist(n.List, init) - n = walkprint(n, init) - goto ret - - case OPANIC: - n = mkcall("gopanic", nil, init, n.Left) - goto ret - - case ORECOVER: - n = mkcall("gorecover", n.Type, init, Nod(OADDR, nodfp, nil)) - goto ret - - case OLITERAL: - n.Addable = true - goto ret - - case OCLOSUREVAR, OCFUNC: - n.Addable = true - goto ret - - case ONAME: - if n.Class&PHEAP == 0 && n.Class != PPARAMREF { - n.Addable = true - } - goto ret - - case OCALLINTER: - t := n.Left.Type - if n.List != nil && n.List.N.Op == OAS { - goto ret - } - walkexpr(&n.Left, init) - walkexprlist(n.List, init) - ll := ascompatte(int(n.Op), n, n.Isddd, getinarg(t), n.List, 0, init) - n.List = reorder1(ll) - goto ret - - case OCALLFUNC: - if n.Left.Op == OCLOSURE { - // Transform direct call of a closure to call of a normal function. - // transformclosure already did all preparation work. - - // Append captured variables to argument list. - n.List = concat(n.List, n.Left.Func.Enter) - - n.Left.Func.Enter = nil - - // Replace OCLOSURE with ONAME/PFUNC. - n.Left = n.Left.Closure.Nname - - // Update type of OCALLFUNC node. - // Output arguments had not changed, but their offsets could. - if n.Left.Type.Outtuple == 1 { - t := getoutargx(n.Left.Type).Type - if t.Etype == TFIELD { - t = t.Type - } - n.Type = t - } else { - n.Type = getoutargx(n.Left.Type) - } - } - - t := n.Left.Type - if n.List != nil && n.List.N.Op == OAS { - goto ret - } - - walkexpr(&n.Left, init) - walkexprlist(n.List, init) - - if n.Left.Op == ONAME && n.Left.Sym.Name == "Sqrt" && n.Left.Sym.Pkg.Path == "math" { - switch Thearch.Thechar { - case '5', '6', '7': - n.Op = OSQRT - n.Left = n.List.N - n.List = nil - goto ret - } - } - - ll := ascompatte(int(n.Op), n, n.Isddd, getinarg(t), n.List, 0, init) - n.List = reorder1(ll) - goto ret - - case OCALLMETH: - t := n.Left.Type - if n.List != nil && n.List.N.Op == OAS { - goto ret - } - walkexpr(&n.Left, init) - walkexprlist(n.List, init) - ll := ascompatte(int(n.Op), n, false, getthis(t), list1(n.Left.Left), 0, init) - lr := ascompatte(int(n.Op), n, n.Isddd, getinarg(t), n.List, 0, init) - ll = concat(ll, lr) - n.Left.Left = nil - ullmancalc(n.Left) - n.List = reorder1(ll) - goto ret - - case OAS: - *init = concat(*init, n.Ninit) - n.Ninit = nil - - walkexpr(&n.Left, init) - n.Left = safeexpr(n.Left, init) - - if oaslit(n, init) { - goto ret - } - - if n.Right == nil || iszero(n.Right) && flag_race == 0 { - goto ret - } - - switch n.Right.Op { - default: - walkexpr(&n.Right, init) - - case ODOTTYPE: - // TODO(rsc): The Isfat is for consistency with componentgen and orderexpr. - // It needs to be removed in all three places. - // That would allow inlining x.(struct{*int}) the same as x.(*int). - if isdirectiface(n.Right.Type) && !Isfat(n.Right.Type) && flag_race == 0 { - // handled directly during cgen - walkexpr(&n.Right, init) - break - } - - // x = i.(T); n.Left is x, n.Right.Left is i. - // orderstmt made sure x is addressable. - walkexpr(&n.Right.Left, init) - - n1 := Nod(OADDR, n.Left, nil) - r := n.Right // i.(T) - - if Debug_typeassert > 0 { - Warn("type assertion not inlined") - } - - buf := "assert" + type2IET(r.Left.Type) + "2" + type2IET(r.Type) - fn := syslook(buf, 1) - substArgTypes(fn, r.Left.Type, r.Type) - - n = mkcall1(fn, nil, init, typename(r.Type), r.Left, n1) - walkexpr(&n, init) - goto ret - - case ORECV: - // x = <-c; n.Left is x, n.Right.Left is c. - // orderstmt made sure x is addressable. - walkexpr(&n.Right.Left, init) - - n1 := Nod(OADDR, n.Left, nil) - r := n.Right.Left // the channel - 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 { - r := convas(Nod(OAS, n.Left, n.Right), init) - r.Dodata = n.Dodata - n = r - n = applywritebarrier(n, init) - } - - goto ret - - case OAS2: - *init = concat(*init, n.Ninit) - n.Ninit = nil - walkexprlistsafe(n.List, init) - walkexprlistsafe(n.Rlist, init) - ll := ascompatee(OAS, n.List, n.Rlist, init) - ll = reorder3(ll) - for lr := ll; lr != nil; lr = lr.Next { - lr.N = applywritebarrier(lr.N, init) - } - n = liststmt(ll) - goto ret - - // a,b,... = fn() - case OAS2FUNC: - *init = concat(*init, n.Ninit) - - n.Ninit = nil - r := n.Rlist.N - walkexprlistsafe(n.List, init) - walkexpr(&r, init) - - ll := ascompatet(int(n.Op), n.List, &r.Type, 0, init) - for lr := ll; lr != nil; lr = lr.Next { - lr.N = applywritebarrier(lr.N, init) - } - n = liststmt(concat(list1(r), ll)) - goto ret - - // x, y = <-c - // orderstmt made sure x is addressable. - case OAS2RECV: - *init = concat(*init, n.Ninit) - - n.Ninit = nil - r := n.Rlist.N - walkexprlistsafe(n.List, init) - walkexpr(&r.Left, init) - var n1 *Node - if isblank(n.List.N) { - n1 = nodnil() - } else { - n1 = Nod(OADDR, n.List.N, nil) - } - n1.Etype = 1 // addr does not escape - fn := chanfn("chanrecv2", 2, r.Left.Type) - r = mkcall1(fn, n.List.Next.N.Type, init, typename(r.Left.Type), r.Left, n1) - n = Nod(OAS, n.List.Next.N, r) - typecheck(&n, Etop) - goto ret - - // a,b = m[i]; - case OAS2MAPR: - *init = concat(*init, n.Ninit) - - n.Ninit = nil - r := n.Rlist.N - walkexprlistsafe(n.List, init) - walkexpr(&r.Left, init) - walkexpr(&r.Right, init) - t := r.Left.Type - p := "" - if t.Type.Width <= 128 { // Check ../../runtime/hashmap.go:maxValueSize before changing. - switch Simsimtype(t.Down) { - case TINT32, TUINT32: - p = "mapaccess2_fast32" - - case TINT64, TUINT64: - p = "mapaccess2_fast64" - - case TSTRING: - p = "mapaccess2_faststr" - } - } - - var key *Node - if p != "" { - // fast versions take key by value - key = r.Right - } else { - // standard version takes key by reference - // orderexpr made sure key is addressable. - key = Nod(OADDR, r.Right, nil) - - p = "mapaccess2" - } - - // from: - // a,b = m[i] - // to: - // var,b = mapaccess2*(t, m, i) - // a = *var - a := n.List.N - - fn := mapfn(p, t) - r = mkcall1(fn, getoutargx(fn.Type), init, typename(t), r.Left, key) - - // mapaccess2* returns a typed bool, but due to spec changes, - // the boolean result of i.(T) is now untyped so we make it the - // same type as the variable on the lhs. - if !isblank(n.List.Next.N) { - r.Type.Type.Down.Type = n.List.Next.N.Type - } - n.Rlist = list1(r) - n.Op = OAS2FUNC - - // don't generate a = *var if a is _ - if !isblank(a) { - var_ := temp(Ptrto(t.Type)) - var_.Typecheck = 1 - n.List.N = var_ - walkexpr(&n, init) - *init = list(*init, n) - n = Nod(OAS, a, Nod(OIND, var_, nil)) - } - - typecheck(&n, Etop) - walkexpr(&n, init) - - // mapaccess needs a zero value to be at least this big. - if zerosize < t.Type.Width { - zerosize = t.Type.Width - } - - // TODO: ptr is always non-nil, so disable nil check for this OIND op. - goto ret - - case ODELETE: - *init = concat(*init, n.Ninit) - n.Ninit = nil - map_ := n.List.N - key := n.List.Next.N - walkexpr(&map_, init) - walkexpr(&key, init) - - // orderstmt made sure key is addressable. - key = Nod(OADDR, key, nil) - - t := map_.Type - n = mkcall1(mapfndel("mapdelete", t), nil, init, typename(t), map_, key) - goto ret - - case OAS2DOTTYPE: - e := n.Rlist.N // i.(T) - // TODO(rsc): The Isfat is for consistency with componentgen and orderexpr. - // It needs to be removed in all three places. - // That would allow inlining x.(struct{*int}) the same as x.(*int). - if isdirectiface(e.Type) && !Isfat(e.Type) && flag_race == 0 { - // handled directly during gen. - walkexprlistsafe(n.List, init) - walkexpr(&e.Left, init) - goto ret - } - - // res, ok = i.(T) - // orderstmt made sure a is addressable. - *init = concat(*init, n.Ninit) - n.Ninit = nil - - walkexprlistsafe(n.List, init) - walkexpr(&e.Left, init) - t := e.Type // T - from := e.Left // i - - oktype := Types[TBOOL] - ok := n.List.Next.N - if !isblank(ok) { - oktype = ok.Type - } - - fromKind := type2IET(from.Type) - toKind := type2IET(t) - - // Avoid runtime calls in a few cases of the form _, ok := i.(T). - // This is faster and shorter and allows the corresponding assertX2X2 - // routines to skip nil checks on their last argument. - if isblank(n.List.N) { - var fast *Node - switch { - case fromKind == "E" && toKind == "T": - tab := Nod(OITAB, from, nil) // type:eface::tab:iface - typ := Nod(OCONVNOP, typename(t), nil) - typ.Type = Ptrto(Types[TUINTPTR]) - fast = Nod(OEQ, tab, typ) - case fromKind == "I" && toKind == "E", - fromKind == "E" && toKind == "E": - tab := Nod(OITAB, from, nil) - fast = Nod(ONE, nodnil(), tab) - } - if fast != nil { - if Debug_typeassert > 0 { - Warn("type assertion (ok only) inlined") - } - n = Nod(OAS, ok, fast) - typecheck(&n, Etop) - goto ret - } - } - - var resptr *Node // &res - if isblank(n.List.N) { - resptr = nodnil() - } else { - resptr = Nod(OADDR, n.List.N, nil) - } - resptr.Etype = 1 // addr does not escape - - if Debug_typeassert > 0 { - Warn("type assertion not inlined") - } - buf := "assert" + fromKind + "2" + toKind + "2" - fn := syslook(buf, 1) - substArgTypes(fn, from.Type, t) - call := mkcall1(fn, oktype, init, typename(t), from, resptr) - n = Nod(OAS, ok, call) - typecheck(&n, Etop) - goto ret - - case ODOTTYPE, ODOTTYPE2: - if !isdirectiface(n.Type) || Isfat(n.Type) { - Fatal("walkexpr ODOTTYPE") // should see inside OAS only - } - walkexpr(&n.Left, init) - goto ret - - case OCONVIFACE: - walkexpr(&n.Left, init) - - // Optimize convT2E as a two-word copy when T is pointer-shaped. - if isnilinter(n.Type) && isdirectiface(n.Left.Type) { - l := Nod(OEFACE, typename(n.Left.Type), n.Left) - l.Type = n.Type - l.Typecheck = n.Typecheck - n = l - goto ret - } - - // Build name of function: convI2E etc. - // Not all names are possible - // (e.g., we'll never generate convE2E or convE2I). - buf := "conv" + type2IET(n.Left.Type) + "2" + type2IET(n.Type) - fn := syslook(buf, 1) - var ll *NodeList - if !Isinter(n.Left.Type) { - ll = list(ll, typename(n.Left.Type)) - } - if !isnilinter(n.Type) { - ll = list(ll, typename(n.Type)) - } - if !Isinter(n.Left.Type) && !isnilinter(n.Type) { - sym := Pkglookup(Tconv(n.Left.Type, obj.FmtLeft)+"."+Tconv(n.Type, obj.FmtLeft), itabpkg) - if sym.Def == nil { - l := Nod(ONAME, nil, nil) - l.Sym = sym - l.Type = Ptrto(Types[TUINT8]) - l.Addable = true - l.Class = PEXTERN - l.Xoffset = 0 - sym.Def = l - ggloblsym(sym, int32(Widthptr), obj.DUPOK|obj.NOPTR) - } - - l := Nod(OADDR, sym.Def, nil) - l.Addable = true - ll = list(ll, l) - - if isdirectiface(n.Left.Type) { - /* For pointer types, we can make a special form of optimization - * - * These statements are put onto the expression init list: - * Itab *tab = atomicloadtype(&cache); - * if(tab == nil) - * tab = typ2Itab(type, itype, &cache); - * - * The CONVIFACE expression is replaced with this: - * OEFACE{tab, ptr}; - */ - l := temp(Ptrto(Types[TUINT8])) - - n1 := Nod(OAS, l, sym.Def) - typecheck(&n1, Etop) - *init = list(*init, n1) - - fn := syslook("typ2Itab", 1) - n1 = Nod(OCALL, fn, nil) - n1.List = ll - typecheck(&n1, Erv) - walkexpr(&n1, init) - - n2 := Nod(OIF, nil, nil) - n2.Ntest = Nod(OEQ, l, nodnil()) - n2.Nbody = list1(Nod(OAS, l, n1)) - n2.Likely = -1 - typecheck(&n2, Etop) - *init = list(*init, n2) - - l = Nod(OEFACE, l, n.Left) - l.Typecheck = n.Typecheck - l.Type = n.Type - n = l - goto ret - } - } - - if Isinter(n.Left.Type) { - ll = list(ll, n.Left) - } else { - // regular types are passed by reference to avoid C vararg calls - // orderexpr arranged for n.Left to be a temporary for all - // the conversions it could see. comparison of an interface - // with a non-interface, especially in a switch on interface value - // with non-interface cases, is not visible to orderstmt, so we - // have to fall back on allocating a temp here. - if islvalue(n.Left) { - ll = list(ll, Nod(OADDR, n.Left, nil)) - } else { - ll = list(ll, Nod(OADDR, copyexpr(n.Left, n.Left.Type, init), nil)) - } - dowidth(n.Left.Type) - r := nodnil() - if n.Esc == EscNone && n.Left.Type.Width <= 1024 { - // Allocate stack buffer for value stored in interface. - r = temp(n.Left.Type) - r = Nod(OAS, r, nil) // zero temp - typecheck(&r, Etop) - *init = list(*init, r) - r = Nod(OADDR, r.Left, nil) - typecheck(&r, Erv) - } - ll = list(ll, r) - } - - if !Isinter(n.Left.Type) { - substArgTypes(fn, n.Left.Type, n.Left.Type, n.Type) - } else { - substArgTypes(fn, n.Left.Type, n.Type) - } - dowidth(fn.Type) - n = Nod(OCALL, fn, nil) - n.List = ll - typecheck(&n, Erv) - walkexpr(&n, init) - goto ret - - case OCONV, OCONVNOP: - if Thearch.Thechar == '5' { - if Isfloat[n.Left.Type.Etype] { - if n.Type.Etype == TINT64 { - n = mkcall("float64toint64", n.Type, init, conv(n.Left, Types[TFLOAT64])) - goto ret - } - - if n.Type.Etype == TUINT64 { - n = mkcall("float64touint64", n.Type, init, conv(n.Left, Types[TFLOAT64])) - goto ret - } - } - - if Isfloat[n.Type.Etype] { - if n.Left.Type.Etype == TINT64 { - n = mkcall("int64tofloat64", n.Type, init, conv(n.Left, Types[TINT64])) - goto ret - } - - if n.Left.Type.Etype == TUINT64 { - n = mkcall("uint64tofloat64", n.Type, init, conv(n.Left, Types[TUINT64])) - goto ret - } - } - } - - walkexpr(&n.Left, init) - goto ret - - case OANDNOT: - walkexpr(&n.Left, init) - n.Op = OAND - n.Right = Nod(OCOM, n.Right, nil) - typecheck(&n.Right, Erv) - walkexpr(&n.Right, init) - goto ret - - case OMUL: - walkexpr(&n.Left, init) - walkexpr(&n.Right, init) - walkmul(&n, init) - goto ret - - case ODIV, OMOD: - walkexpr(&n.Left, init) - walkexpr(&n.Right, init) - - /* - * rewrite complex div into function call. - */ - et := int(n.Left.Type.Etype) - - if Iscomplex[et] && n.Op == ODIV { - t := n.Type - n = mkcall("complex128div", Types[TCOMPLEX128], init, conv(n.Left, Types[TCOMPLEX128]), conv(n.Right, Types[TCOMPLEX128])) - n = conv(n, t) - goto ret - } - - // Nothing to do for float divisions. - if Isfloat[et] { - goto ret - } - - // Try rewriting as shifts or magic multiplies. - walkdiv(&n, init) - - /* - * rewrite 64-bit div and mod into function calls - * on 32-bit architectures. - */ - switch n.Op { - case OMOD, ODIV: - if Widthreg >= 8 || (et != TUINT64 && et != TINT64) { - goto ret - } - var fn string - if et == TINT64 { - fn = "int64" - } else { - fn = "uint64" - } - if n.Op == ODIV { - fn += "div" - } else { - fn += "mod" - } - n = mkcall(fn, n.Type, init, conv(n.Left, Types[et]), conv(n.Right, Types[et])) - - default: - break - } - - goto ret - - case OINDEX: - walkexpr(&n.Left, init) - - // save the original node for bounds checking elision. - // If it was a ODIV/OMOD walk might rewrite it. - r := n.Right - - walkexpr(&n.Right, init) - - // if range of type cannot exceed static array bound, - // disable bounds check. - if n.Bounded { - goto ret - } - t := n.Left.Type - if t != nil && Isptr[t.Etype] { - t = t.Type - } - if Isfixedarray(t) { - n.Bounded = bounded(r, t.Bound) - if Debug['m'] != 0 && n.Bounded && !Isconst(n.Right, CTINT) { - Warn("index bounds check elided") - } - if Smallintconst(n.Right) && !n.Bounded { - Yyerror("index out of bounds") - } - } else if Isconst(n.Left, CTSTR) { - 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") - } - if Smallintconst(n.Right) { - if !n.Bounded { - Yyerror("index out of bounds") - } else { - // replace "abc"[1] with 'b'. - // delayed until now because "abc"[1] is not - // an ideal constant. - v := Mpgetfix(n.Right.Val.U.(*Mpint)) - - 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.(*Mpint), &mpzero) < 0 || Mpcmpfixfix(n.Right.Val.U.(*Mpint), Maxintval[TINT]) > 0 { - Yyerror("index out of bounds") - } - } - goto ret - - case OINDEXMAP: - if n.Etype == 1 { - goto ret - } - walkexpr(&n.Left, init) - walkexpr(&n.Right, init) - - t := n.Left.Type - p := "" - if t.Type.Width <= 128 { // Check ../../runtime/hashmap.go:maxValueSize before changing. - switch Simsimtype(t.Down) { - case TINT32, TUINT32: - p = "mapaccess1_fast32" - - case TINT64, TUINT64: - p = "mapaccess1_fast64" - - case TSTRING: - p = "mapaccess1_faststr" - } - } - - var key *Node - if p != "" { - // fast versions take key by value - key = n.Right - } else { - // standard version takes key by reference. - // orderexpr made sure key is addressable. - key = Nod(OADDR, n.Right, nil) - - p = "mapaccess1" - } - - n = mkcall1(mapfn(p, t), Ptrto(t.Type), init, typename(t), n.Left, key) - n = Nod(OIND, n, nil) - n.Type = t.Type - n.Typecheck = 1 - - // mapaccess needs a zero value to be at least this big. - if zerosize < t.Type.Width { - zerosize = t.Type.Width - } - goto ret - - case ORECV: - Fatal("walkexpr ORECV") // should see inside OAS only - - case OSLICE, OSLICEARR, OSLICESTR: - walkexpr(&n.Left, init) - walkexpr(&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 = reduceSlice(n) - goto ret - - case OSLICE3, OSLICE3ARR: - 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 - } - goto ret - - case OADDR: - walkexpr(&n.Left, init) - goto ret - - case ONEW: - if n.Esc == EscNone && n.Type.Type.Width < 1<<16 { - r := temp(n.Type.Type) - r = Nod(OAS, r, nil) // zero temp - typecheck(&r, Etop) - *init = list(*init, r) - r = Nod(OADDR, r.Left, nil) - typecheck(&r, Erv) - n = r - } else { - n = callnew(n.Type.Type) - } - - goto ret - - // If one argument to the comparison is an empty string, - // 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.(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) - r.Type = n.Type - n = r - goto ret - } - - // s + "badgerbadgerbadger" == "badgerbadgerbadger" - if (n.Etype == OEQ || n.Etype == ONE) && Isconst(n.Right, CTSTR) && n.Left.Op == OADDSTR && count(n.Left.List) == 2 && Isconst(n.Left.List.Next.N, CTSTR) && cmpslit(n.Right, n.Left.List.Next.N) == 0 { - r := Nod(int(n.Etype), Nod(OLEN, n.Left.List.N, nil), Nodintconst(0)) - typecheck(&r, Erv) - walkexpr(&r, init) - r.Type = n.Type - n = r - goto ret - } - - var r *Node - if n.Etype == OEQ || n.Etype == ONE { - // prepare for rewrite below - n.Left = cheapexpr(n.Left, init) - - n.Right = cheapexpr(n.Right, init) - - r = mkcall("eqstring", Types[TBOOL], init, conv(n.Left, Types[TSTRING]), conv(n.Right, Types[TSTRING])) - - // quick check of len before full compare for == or != - // eqstring assumes that the lengths are equal - if n.Etype == OEQ { - // len(left) == len(right) && eqstring(left, right) - r = Nod(OANDAND, Nod(OEQ, Nod(OLEN, n.Left, nil), Nod(OLEN, n.Right, nil)), r) - } else { - // len(left) != len(right) || !eqstring(left, right) - r = Nod(ONOT, r, nil) - - r = Nod(OOROR, Nod(ONE, Nod(OLEN, n.Left, nil), Nod(OLEN, n.Right, nil)), r) - } - - typecheck(&r, Erv) - walkexpr(&r, nil) - } else { - // sys_cmpstring(s1, s2) :: 0 - r = mkcall("cmpstring", Types[TINT], init, conv(n.Left, Types[TSTRING]), conv(n.Right, Types[TSTRING])) - - r = Nod(int(n.Etype), r, Nodintconst(0)) - } - - typecheck(&r, Erv) - if n.Type.Etype != TBOOL { - Fatal("cmp %v", n.Type) - } - r.Type = n.Type - n = r - goto ret - - case OADDSTR: - n = addstr(n, init) - goto ret - - case OAPPEND: - // 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) - goto ret - - // cannot use chanfn - closechan takes any, not chan any - case OCLOSE: - fn := syslook("closechan", 1) - - substArgTypes(fn, n.Left.Type) - n = mkcall1(fn, nil, init, n.Left) - goto ret - - case OMAKECHAN: - n = mkcall1(chanfn("makechan", 1, n.Type), n.Type, init, typename(n.Type), conv(n.Left, Types[TINT64])) - goto ret - - case OMAKEMAP: - t := n.Type - - fn := syslook("makemap", 1) - - a := nodnil() // hmap buffer - r := nodnil() // bucket buffer - if n.Esc == EscNone { - // Allocate hmap buffer on stack. - var_ := temp(hmap(t)) - - a = Nod(OAS, var_, nil) // zero temp - typecheck(&a, Etop) - *init = list(*init, a) - a = Nod(OADDR, var_, nil) - - // Allocate one bucket on stack. - // Maximum key/value size is 128 bytes, larger objects - // are stored with an indirection. So max bucket size is 2048+eps. - var_ = temp(mapbucket(t)) - - r = Nod(OAS, var_, nil) // zero temp - typecheck(&r, Etop) - *init = list(*init, r) - r = Nod(OADDR, var_, nil) - } - - substArgTypes(fn, hmap(t), mapbucket(t), t.Down, t.Type) - n = mkcall1(fn, n.Type, init, typename(n.Type), conv(n.Left, Types[TINT64]), a, r) - goto ret - - case OMAKESLICE: - l := n.Left - r := n.Right - if r == nil { - r = safeexpr(l, init) - 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) { - // var arr [r]T - // n = arr[:l] - t = aindex(r, t.Type) // [r]T - var_ := temp(t) - a := Nod(OAS, var_, nil) // zero temp - typecheck(&a, Etop) - *init = list(*init, a) - r := Nod(OSLICE, var_, Nod(OKEY, nil, l)) // arr[:l] - r = conv(r, n.Type) // in case n.Type is named. - typecheck(&r, Erv) - walkexpr(&r, init) - n = r - } else { - // makeslice(t *Type, nel int64, max int64) (ary []any) - fn := syslook("makeslice", 1) - - substArgTypes(fn, t.Type) // any-1 - n = mkcall1(fn, n.Type, init, typename(n.Type), conv(l, Types[TINT64]), conv(r, Types[TINT64])) - } - - goto ret - - case ORUNESTR: - a := nodnil() - if n.Esc == EscNone { - t := aindex(Nodintconst(4), Types[TUINT8]) - var_ := temp(t) - a = Nod(OADDR, var_, nil) - } - - // intstring(*[4]byte, rune) - n = mkcall("intstring", n.Type, init, a, conv(n.Left, Types[TINT64])) - - goto ret - - case OARRAYBYTESTR: - a := nodnil() - if n.Esc == EscNone { - // Create temporary buffer for string on stack. - t := aindex(Nodintconst(tmpstringbufsize), Types[TUINT8]) - - a = Nod(OADDR, temp(t), nil) - } - - // slicebytetostring(*[32]byte, []byte) string; - n = mkcall("slicebytetostring", n.Type, init, a, n.Left) - - goto ret - - // slicebytetostringtmp([]byte) string; - case OARRAYBYTESTRTMP: - n = mkcall("slicebytetostringtmp", n.Type, init, n.Left) - - goto ret - - // slicerunetostring(*[32]byte, []rune) string; - case OARRAYRUNESTR: - a := nodnil() - - if n.Esc == EscNone { - // Create temporary buffer for string on stack. - t := aindex(Nodintconst(tmpstringbufsize), Types[TUINT8]) - - a = Nod(OADDR, temp(t), nil) - } - - n = mkcall("slicerunetostring", n.Type, init, a, n.Left) - goto ret - - // stringtoslicebyte(*32[byte], string) []byte; - case OSTRARRAYBYTE: - a := nodnil() - - if n.Esc == EscNone { - // Create temporary buffer for slice on stack. - t := aindex(Nodintconst(tmpstringbufsize), Types[TUINT8]) - - a = Nod(OADDR, temp(t), nil) - } - - n = mkcall("stringtoslicebyte", n.Type, init, a, conv(n.Left, Types[TSTRING])) - goto ret - - // stringtoslicebytetmp(string) []byte; - case OSTRARRAYBYTETMP: - n = mkcall("stringtoslicebytetmp", n.Type, init, conv(n.Left, Types[TSTRING])) - - goto ret - - // stringtoslicerune(*[32]rune, string) []rune - case OSTRARRAYRUNE: - a := nodnil() - - if n.Esc == EscNone { - // Create temporary buffer for slice on stack. - t := aindex(Nodintconst(tmpstringbufsize), Types[TINT32]) - - a = Nod(OADDR, temp(t), nil) - } - - n = mkcall("stringtoslicerune", n.Type, init, a, n.Left) - goto ret - - // ifaceeq(i1 any-1, i2 any-2) (ret bool); - case OCMPIFACE: - if !Eqtype(n.Left.Type, n.Right.Type) { - Fatal("ifaceeq %v %v %v", Oconv(int(n.Op), 0), n.Left.Type, n.Right.Type) - } - var fn *Node - if isnilinter(n.Left.Type) { - fn = syslook("efaceeq", 1) - } else { - fn = syslook("ifaceeq", 1) - } - - n.Right = cheapexpr(n.Right, init) - n.Left = cheapexpr(n.Left, init) - substArgTypes(fn, n.Right.Type, n.Left.Type) - r := mkcall1(fn, n.Type, init, n.Left, n.Right) - if n.Etype == ONE { - r = Nod(ONOT, r, nil) - } - - // check itable/type before full compare. - if n.Etype == OEQ { - r = Nod(OANDAND, Nod(OEQ, Nod(OITAB, n.Left, nil), Nod(OITAB, n.Right, nil)), r) - } else { - r = Nod(OOROR, Nod(ONE, Nod(OITAB, n.Left, nil), Nod(OITAB, n.Right, nil)), r) - } - typecheck(&r, Erv) - walkexpr(&r, init) - r.Type = n.Type - n = r - goto ret - - case OARRAYLIT, OMAPLIT, OSTRUCTLIT, OPTRLIT: - var_ := temp(n.Type) - anylit(0, n, var_, init) - n = var_ - goto ret - - case OSEND: - n1 := n.Right - n1 = assignconv(n1, n.Left.Type.Type, "chan send") - walkexpr(&n1, init) - n1 = Nod(OADDR, n1, nil) - n = mkcall1(chanfn("chansend1", 2, n.Left.Type), nil, init, typename(n.Left.Type), n.Left, n1) - goto ret - - case OCLOSURE: - n = walkclosure(n, init) - goto ret - - case OCALLPART: - n = walkpartialcall(n, init) - goto ret - } - - Fatal("missing switch %v", Oconv(int(n.Op), 0)) - - // Expressions that are constant at run time but not - // considered const by the language spec are not turned into - // constants until walk. For example, if n is y%1 == 0, the - // walk of y%1 may have replaced it by 0. - // Check whether n with its updated args is itself now a constant. -ret: - t := n.Type - - evconst(n) - n.Type = t - if n.Op == OLITERAL { - typecheck(&n, Erv) - } - - ullmancalc(n) - - if Debug['w'] != 0 && n != nil { - Dump("walk", n) - } - - lineno = lno - *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. - n := Nod(OAS, l, r) - - if l.Op == OINDEXMAP { - return n - } - - return convas(n, init) -} - -func ascompatee(op int, nl *NodeList, nr *NodeList, init **NodeList) *NodeList { - /* - * check assign expression list to - * a expression list. called in - * expr-list = expr-list - */ - - // ensure order of evaluation for function calls - for ll := nl; ll != nil; ll = ll.Next { - ll.N = safeexpr(ll.N, init) - } - for lr := nr; lr != nil; lr = lr.Next { - lr.N = safeexpr(lr.N, init) - } - - var nn *NodeList - ll := nl - lr := nr - for ; ll != nil && lr != nil; ll, lr = ll.Next, lr.Next { - // Do not generate 'x = x' during return. See issue 4014. - if op == ORETURN && ll.N == lr.N { - continue - } - nn = list(nn, ascompatee1(op, ll.N, lr.N, init)) - } - - // cannot happen: caller checked that lists had same length - if ll != nil || lr != nil { - Yyerror("error in shape across %v %v %v / %d %d [%s]", Hconv(nl, obj.FmtSign), Oconv(int(op), 0), Hconv(nr, obj.FmtSign), count(nl), count(nr), Curfn.Nname.Sym.Name) - } - return nn -} - -/* - * l is an lv and rt is the type of an rv - * return 1 if this implies a function call - * evaluating the lv or a function call - * in the conversion of the types - */ -func fncall(l *Node, rt *Type) bool { - if l.Ullman >= UINF || l.Op == OINDEXMAP { - return true - } - var r Node - if needwritebarrier(l, &r) { - return true - } - if Eqtype(l.Type, rt) { - return false - } - return true -} - -func ascompatet(op int, nl *NodeList, nr **Type, fp int, init **NodeList) *NodeList { - var l *Node - var tmp *Node - var a *Node - var ll *NodeList - var saver Iter - - /* - * check assign type list to - * a expression list. called in - * expr-list = func() - */ - r := Structfirst(&saver, nr) - - var nn *NodeList - var mm *NodeList - ucount := 0 - for ll = nl; ll != nil; ll = ll.Next { - if r == nil { - break - } - l = ll.N - if isblank(l) { - r = structnext(&saver) - continue - } - - // any lv that causes a fn call must be - // deferred until all the return arguments - // have been pulled from the output arguments - if fncall(l, r.Type) { - tmp = temp(r.Type) - typecheck(&tmp, Erv) - a = Nod(OAS, l, tmp) - a = convas(a, init) - mm = list(mm, a) - l = tmp - } - - a = Nod(OAS, l, nodarg(r, fp)) - a = convas(a, init) - ullmancalc(a) - if a.Ullman >= UINF { - Dump("ascompatet ucount", a) - ucount++ - } - - nn = list(nn, a) - r = structnext(&saver) - } - - if ll != nil || r != nil { - Yyerror("ascompatet: assignment count mismatch: %d = %d", count(nl), structcount(*nr)) - } - - if ucount != 0 { - Fatal("ascompatet: too many function calls evaluating parameters") - } - return concat(nn, mm) -} - -/* -* package all the arguments that match a ... T parameter into a []T. - */ -func mkdotargslice(lr0 *NodeList, nn *NodeList, l *Type, fp int, init **NodeList, ddd *Node) *NodeList { - esc := uint16(EscUnknown) - if ddd != nil { - esc = ddd.Esc - } - - tslice := typ(TARRAY) - tslice.Type = l.Type.Type - tslice.Bound = -1 - - var n *Node - if count(lr0) == 0 { - n = nodnil() - n.Type = tslice - } else { - n = Nod(OCOMPLIT, nil, typenod(tslice)) - if ddd != nil { - n.Alloc = ddd.Alloc // temporary to use - } - n.List = lr0 - n.Esc = esc - typecheck(&n, Erv) - if n.Type == nil { - Fatal("mkdotargslice: typecheck failed") - } - walkexpr(&n, init) - } - - a := Nod(OAS, nodarg(l, fp), n) - nn = list(nn, convas(a, init)) - return nn -} - -/* - * helpers for shape errors - */ -func dumptypes(nl **Type, what string) string { - var savel Iter - - fmt_ := "" - fmt_ += "\t" - first := 1 - for l := Structfirst(&savel, nl); l != nil; l = structnext(&savel) { - if first != 0 { - first = 0 - } else { - fmt_ += ", " - } - fmt_ += Tconv(l, 0) - } - - if first != 0 { - fmt_ += fmt.Sprintf("[no arguments %s]", what) - } - return fmt_ -} - -func dumpnodetypes(l *NodeList, what string) string { - var r *Node - - fmt_ := "" - fmt_ += "\t" - first := 1 - for ; l != nil; l = l.Next { - r = l.N - if first != 0 { - first = 0 - } else { - fmt_ += ", " - } - fmt_ += Tconv(r.Type, 0) - } - - if first != 0 { - fmt_ += fmt.Sprintf("[no arguments %s]", what) - } - return fmt_ -} - -/* - * check assign expression list to - * a type list. called in - * return expr-list - * func(expr-list) - */ -func ascompatte(op int, call *Node, isddd bool, nl **Type, lr *NodeList, fp int, init **NodeList) *NodeList { - var savel Iter - - lr0 := lr - l := Structfirst(&savel, nl) - var r *Node - if lr != nil { - r = lr.N - } - var nn *NodeList - - // f(g()) where g has multiple return values - var a *Node - var l2 string - var ll *Type - var l1 string - if r != nil && lr.Next == nil && r.Type.Etype == TSTRUCT && r.Type.Funarg != 0 { - // optimization - can do block copy - if eqtypenoname(r.Type, *nl) { - a := nodarg(*nl, fp) - r = Nod(OCONVNOP, r, nil) - r.Type = a.Type - nn = list1(convas(Nod(OAS, a, r), init)) - goto ret - } - - // conversions involved. - // copy into temporaries. - var alist *NodeList - - for l := Structfirst(&savel, &r.Type); l != nil; l = structnext(&savel) { - a = temp(l.Type) - alist = list(alist, a) - } - - a = Nod(OAS2, nil, nil) - a.List = alist - a.Rlist = lr - typecheck(&a, Etop) - walkstmt(&a) - *init = list(*init, a) - lr = alist - r = lr.N - l = Structfirst(&savel, nl) - } - -loop: - if l != nil && l.Isddd { - // the ddd parameter must be last - ll = structnext(&savel) - - if ll != nil { - Yyerror("... must be last argument") - } - - // special case -- - // only if we are assigning a single ddd - // argument to a ddd parameter then it is - // passed thru unencapsulated - if r != nil && lr.Next == nil && isddd && Eqtype(l.Type, r.Type) { - a = Nod(OAS, nodarg(l, fp), r) - a = convas(a, init) - nn = list(nn, a) - goto ret - } - - // normal case -- make a slice of all - // remaining arguments and pass it to - // the ddd parameter. - nn = mkdotargslice(lr, nn, l, fp, init, call.Right) - - goto ret - } - - if l == nil || r == nil { - if l != nil || r != nil { - l1 = dumptypes(nl, "expected") - l2 = dumpnodetypes(lr0, "given") - if l != nil { - Yyerror("not enough arguments to %v\n%s\n%s", Oconv(int(op), 0), l1, l2) - } else { - Yyerror("too many arguments to %v\n%s\n%s", Oconv(int(op), 0), l1, l2) - } - } - - goto ret - } - - a = Nod(OAS, nodarg(l, fp), r) - a = convas(a, init) - nn = list(nn, a) - - l = structnext(&savel) - r = nil - lr = lr.Next - if lr != nil { - r = lr.N - } - goto loop - -ret: - for lr = nn; lr != nil; lr = lr.Next { - lr.N.Typecheck = 1 - } - return nn -} - -// generate code for print -func walkprint(nn *Node, init **NodeList) *Node { - var r *Node - var n *Node - var on *Node - var t *Type - var et int - - op := int(nn.Op) - all := nn.List - var calls *NodeList - notfirst := false - - // Hoist all the argument evaluation up before the lock. - walkexprlistcheap(all, init) - - calls = list(calls, mkcall("printlock", nil, init)) - - for l := all; l != nil; l = l.Next { - if notfirst { - calls = list(calls, mkcall("printsp", nil, init)) - } - - notfirst = op == OPRINTN - - n = l.N - if n.Op == OLITERAL { - switch n.Val.Ctype { - case CTRUNE: - defaultlit(&n, runetype) - - case CTINT: - defaultlit(&n, Types[TINT64]) - - case CTFLT: - defaultlit(&n, Types[TFLOAT64]) - } - } - - if n.Op != OLITERAL && n.Type != nil && n.Type.Etype == TIDEAL { - defaultlit(&n, Types[TINT64]) - } - defaultlit(&n, nil) - l.N = n - if n.Type == nil || n.Type.Etype == TFORW { - continue - } - - t = n.Type - et = int(n.Type.Etype) - if Isinter(n.Type) { - if isnilinter(n.Type) { - on = syslook("printeface", 1) - } else { - on = syslook("printiface", 1) - } - substArgTypes(on, n.Type) // any-1 - } else if Isptr[et] || et == TCHAN || et == TMAP || et == TFUNC || et == TUNSAFEPTR { - on = syslook("printpointer", 1) - substArgTypes(on, n.Type) // any-1 - } else if Isslice(n.Type) { - on = syslook("printslice", 1) - substArgTypes(on, n.Type) // any-1 - } else if Isint[et] { - if et == TUINT64 { - if (t.Sym.Pkg == Runtimepkg || compiling_runtime != 0) && t.Sym.Name == "hex" { - on = syslook("printhex", 0) - } else { - on = syslook("printuint", 0) - } - } else { - on = syslook("printint", 0) - } - } else if Isfloat[et] { - on = syslook("printfloat", 0) - } else if Iscomplex[et] { - on = syslook("printcomplex", 0) - } else if et == TBOOL { - on = syslook("printbool", 0) - } else if et == TSTRING { - on = syslook("printstring", 0) - } else { - badtype(OPRINT, n.Type, nil) - continue - } - - t = *getinarg(on.Type) - if t != nil { - t = t.Type - } - if t != nil { - t = t.Type - } - - if !Eqtype(t, n.Type) { - n = Nod(OCONV, n, nil) - n.Type = t - } - - r = Nod(OCALL, on, nil) - r.List = list1(n) - calls = list(calls, r) - } - - if op == OPRINTN { - calls = list(calls, mkcall("printnl", nil, nil)) - } - - calls = list(calls, mkcall("printunlock", nil, init)) - - typechecklist(calls, Etop) - walkexprlist(calls, init) - - r = Nod(OEMPTY, nil, nil) - typecheck(&r, Etop) - walkexpr(&r, init) - r.Ninit = calls - return r -} - -func callnew(t *Type) *Node { - dowidth(t) - fn := syslook("newobject", 1) - substArgTypes(fn, t) - return mkcall1(fn, Ptrto(t), nil, typename(t)) -} - -func isstack(n *Node) bool { - n = outervalue(n) - - // If n is *autotmp and autotmp = &foo, replace n with foo. - // We introduce such temps when initializing struct literals. - if n.Op == OIND && n.Left.Op == ONAME && strings.HasPrefix(n.Left.Sym.Name, "autotmp_") { - defn := n.Left.Defn - if defn != nil && defn.Op == OAS && defn.Right.Op == OADDR { - n = defn.Right.Left - } - } - - switch n.Op { - case OINDREG: - return n.Reg == int16(Thearch.REGSP) - - case ONAME: - switch n.Class { - case PAUTO, PPARAM, PPARAMOUT: - return true - } - } - - return false -} - -func isglobal(n *Node) bool { - n = outervalue(n) - - switch n.Op { - case ONAME: - switch n.Class { - case PEXTERN: - return true - } - } - - return false -} - -// Do we need a write barrier for the assignment l = r? -func needwritebarrier(l *Node, r *Node) bool { - if use_writebarrier == 0 { - return false - } - - if l == nil || isblank(l) { - return false - } - - // No write barrier for write of non-pointers. - dowidth(l.Type) - - if !haspointers(l.Type) { - return false - } - - // No write barrier for write to stack. - if isstack(l) { - return false - } - - // No write barrier for implicit or explicit zeroing. - if r == nil || iszero(r) { - return false - } - - // No write barrier for initialization to constant. - if r.Op == OLITERAL { - return false - } - - // No write barrier for storing static (read-only) data. - if r.Op == ONAME && strings.HasPrefix(r.Sym.Name, "statictmp_") { - return false - } - - // No write barrier for storing address of stack values, - // which are guaranteed only to be written to the stack. - if r.Op == OADDR && isstack(r.Left) { - return false - } - - // No write barrier for storing address of global, which - // is live no matter what. - if r.Op == OADDR && isglobal(r.Left) { - return false - } - - // Otherwise, be conservative and use write barrier. - return true -} - -// TODO(rsc): Perhaps componentgen should run before this. - -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) - } - } - return n -} - -func convas(n *Node, init **NodeList) *Node { - if n.Op != OAS { - Fatal("convas: not OAS %v", Oconv(int(n.Op), 0)) - } - - n.Typecheck = 1 - - var lt *Type - var rt *Type - if n.Left == nil || n.Right == nil { - goto out - } - - lt = n.Left.Type - rt = n.Right.Type - if lt == nil || rt == nil { - goto out - } - - if isblank(n.Left) { - defaultlit(&n.Right, nil) - goto out - } - - if n.Left.Op == OINDEXMAP { - map_ := n.Left.Left - key := n.Left.Right - val := n.Right - walkexpr(&map_, init) - walkexpr(&key, init) - walkexpr(&val, init) - - // orderexpr made sure key and val are addressable. - key = Nod(OADDR, key, nil) - - val = Nod(OADDR, val, nil) - n = mkcall1(mapfn("mapassign1", map_.Type), nil, init, typename(map_.Type), map_, key, val) - goto out - } - - if !Eqtype(lt, rt) { - n.Right = assignconv(n.Right, lt, "assignment") - walkexpr(&n.Right, init) - } - -out: - ullmancalc(n) - return n -} - -/* - * from ascompat[te] - * evaluating actual function arguments. - * f(a,b) - * if there is exactly one function expr, - * then it is done first. otherwise must - * make temp variables - */ -func reorder1(all *NodeList) *NodeList { - var n *Node - - c := 0 // function calls - t := 0 // total parameters - - for l := all; l != nil; l = l.Next { - n = l.N - t++ - ullmancalc(n) - if n.Ullman >= UINF { - c++ - } - } - - if c == 0 || t == 1 { - return all - } - - var g *NodeList // fncalls assigned to tempnames - var f *Node // last fncall assigned to stack - var r *NodeList // non fncalls and tempnames assigned to stack - d := 0 - var a *Node - for l := all; l != nil; l = l.Next { - n = l.N - if n.Ullman < UINF { - r = list(r, n) - continue - } - - d++ - if d == c { - f = n - continue - } - - // make assignment of fncall to tempname - a = temp(n.Right.Type) - - a = Nod(OAS, a, n.Right) - g = list(g, a) - - // put normal arg assignment on list - // with fncall replaced by tempname - n.Right = a.Left - - r = list(r, n) - } - - if f != nil { - g = list(g, f) - } - return concat(g, r) -} - -/* - * from ascompat[ee] - * a,b = c,d - * simultaneous assignment. there cannot - * be later use of an earlier lvalue. - * - * function calls have been removed. - */ -func reorder3(all *NodeList) *NodeList { - var l *Node - - // If a needed expression may be affected by an - // earlier assignment, make an early copy of that - // expression and use the copy instead. - var early *NodeList - - var mapinit *NodeList - for list := all; list != nil; list = list.Next { - l = list.N.Left - - // Save subexpressions needed on left side. - // Drill through non-dereferences. - for { - if l.Op == ODOT || l.Op == OPAREN { - l = l.Left - continue - } - - if l.Op == OINDEX && Isfixedarray(l.Left.Type) { - reorder3save(&l.Right, all, list, &early) - l = l.Left - continue - } - - break - } - - switch l.Op { - default: - Fatal("reorder3 unexpected lvalue %v", Oconv(int(l.Op), obj.FmtSharp)) - - case ONAME: - break - - case OINDEX, OINDEXMAP: - reorder3save(&l.Left, all, list, &early) - reorder3save(&l.Right, all, list, &early) - if l.Op == OINDEXMAP { - list.N = convas(list.N, &mapinit) - } - - case OIND, ODOTPTR: - reorder3save(&l.Left, all, list, &early) - } - - // Save expression on right side. - reorder3save(&list.N.Right, all, list, &early) - } - - early = concat(mapinit, early) - return concat(early, all) -} - -/* - * if the evaluation of *np would be affected by the - * assignments in all up to but not including stop, - * copy into a temporary during *early and - * replace *np with that temp. - */ -func reorder3save(np **Node, all *NodeList, stop *NodeList, early **NodeList) { - n := *np - if !aliased(n, all, stop) { - return - } - - q := temp(n.Type) - q = Nod(OAS, q, n) - typecheck(&q, Etop) - *early = list(*early, q) - *np = q.Left -} - -/* - * what's the outer value that a write to n affects? - * outer value means containing struct or array. - */ -func outervalue(n *Node) *Node { - for { - if n.Op == OXDOT { - Fatal("OXDOT in walk") - } - if n.Op == ODOT || n.Op == OPAREN || n.Op == OCONVNOP { - n = n.Left - continue - } - - if n.Op == OINDEX && Isfixedarray(n.Left.Type) { - n = n.Left - continue - } - - break - } - - return n -} - -/* - * Is it possible that the computation of n might be - * affected by writes in as up to but not including stop? - */ -func aliased(n *Node, all *NodeList, stop *NodeList) bool { - if n == nil { - return false - } - - // Look for obvious aliasing: a variable being assigned - // during the all list and appearing in n. - // Also record whether there are any writes to main memory. - // Also record whether there are any writes to variables - // whose addresses have been taken. - memwrite := 0 - - varwrite := 0 - var a *Node - for l := all; l != stop; l = l.Next { - a = outervalue(l.N.Left) - if a.Op != ONAME { - memwrite = 1 - continue - } - - switch n.Class { - default: - varwrite = 1 - continue - - case PAUTO, PPARAM, PPARAMOUT: - if n.Addrtaken { - varwrite = 1 - continue - } - - if vmatch2(a, n) { - // Direct hit. - return true - } - } - } - - // The variables being written do not appear in n. - // However, n might refer to computed addresses - // that are being written. - - // If no computed addresses are affected by the writes, no aliasing. - if memwrite == 0 && varwrite == 0 { - return false - } - - // If n does not refer to computed addresses - // (that is, if n only refers to variables whose addresses - // have not been taken), no aliasing. - if varexpr(n) { - return false - } - - // Otherwise, both the writes and n refer to computed memory addresses. - // Assume that they might conflict. - return true -} - -/* - * does the evaluation of n only refer to variables - * whose addresses have not been taken? - * (and no other memory) - */ -func varexpr(n *Node) bool { - if n == nil { - return true - } - - switch n.Op { - case OLITERAL: - return true - - case ONAME: - switch n.Class { - case PAUTO, PPARAM, PPARAMOUT: - if !n.Addrtaken { - return true - } - } - - return false - - case OADD, - OSUB, - OOR, - OXOR, - OMUL, - ODIV, - OMOD, - OLSH, - ORSH, - OAND, - OANDNOT, - OPLUS, - OMINUS, - OCOM, - OPAREN, - OANDAND, - OOROR, - ODOT, // but not ODOTPTR - OCONV, - OCONVNOP, - OCONVIFACE, - ODOTTYPE: - return varexpr(n.Left) && varexpr(n.Right) - } - - // Be conservative. - return false -} - -/* - * is the name l mentioned in r? - */ -func vmatch2(l *Node, r *Node) bool { - if r == nil { - return false - } - switch r.Op { - // match each right given left - case ONAME: - return l == r - - case OLITERAL: - return false - } - - if vmatch2(l, r.Left) { - return true - } - if vmatch2(l, r.Right) { - return true - } - for ll := r.List; ll != nil; ll = ll.Next { - if vmatch2(l, ll.N) { - return true - } - } - return false -} - -/* - * is any name mentioned in l also mentioned in r? - * called by sinit.go - */ -func vmatch1(l *Node, r *Node) bool { - /* - * isolate all left sides - */ - if l == nil || r == nil { - return false - } - switch l.Op { - case ONAME: - switch l.Class { - case PPARAM, PPARAMREF, PAUTO: - break - - // assignment to non-stack variable - // must be delayed if right has function calls. - default: - if r.Ullman >= UINF { - return true - } - } - - return vmatch2(l, r) - - case OLITERAL: - return false - } - - if vmatch1(l.Left, r) { - return true - } - if vmatch1(l.Right, r) { - return true - } - for ll := l.List; ll != nil; ll = ll.Next { - if vmatch1(ll.N, r) { - return true - } - } - return false -} - -/* - * walk through argin parameters. - * generate and return code to allocate - * copies of escaped parameters to the heap. - */ -func paramstoheap(argin **Type, out int) *NodeList { - var savet Iter - var v *Node - var as *Node - - var nn *NodeList - for t := Structfirst(&savet, argin); t != nil; t = structnext(&savet) { - v = t.Nname - if v != nil && v.Sym != nil && v.Sym.Name[0] == '~' && v.Sym.Name[1] == 'r' { // unnamed result - v = nil - } - - // For precise stacks, the garbage collector assumes results - // are always live, so zero them always. - if out != 0 { - // Defer might stop a panic and show the - // return values as they exist at the time of panic. - // Make sure to zero them on entry to the function. - nn = list(nn, Nod(OAS, nodarg(t, 1), nil)) - } - - if v == nil || v.Class&PHEAP == 0 { - continue - } - - // generate allocation & copying code - if compiling_runtime != 0 { - Yyerror("%v escapes to heap, not allowed in runtime.", v) - } - if v.Alloc == nil { - v.Alloc = callnew(v.Type) - } - 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 - typecheck(&as, Etop) - as = applywritebarrier(as, &nn) - nn = list(nn, as) - } - } - - return nn -} - -/* - * walk through argout parameters copying back to stack - */ -func returnsfromheap(argin **Type) *NodeList { - var savet Iter - var v *Node - - var nn *NodeList - for t := Structfirst(&savet, argin); t != nil; t = structnext(&savet) { - v = t.Nname - if v == nil || v.Class != PHEAP|PPARAMOUT { - continue - } - nn = list(nn, Nod(OAS, v.Stackparam, v)) - } - - return nn -} - -/* - * take care of migrating any function in/out args - * between the stack and the heap. adds code to - * curfn's before and after lists. - */ -func heapmoves() { - lno := lineno - lineno = Curfn.Lineno - nn := paramstoheap(getthis(Curfn.Type), 0) - nn = concat(nn, paramstoheap(getinarg(Curfn.Type), 0)) - nn = concat(nn, paramstoheap(Getoutarg(Curfn.Type), 1)) - Curfn.Func.Enter = concat(Curfn.Func.Enter, nn) - lineno = Curfn.Func.Endlineno - Curfn.Func.Exit = returnsfromheap(Getoutarg(Curfn.Type)) - lineno = lno -} - -func vmkcall(fn *Node, t *Type, init **NodeList, va []*Node) *Node { - if fn.Type == nil || fn.Type.Etype != TFUNC { - Fatal("mkcall %v %v", fn, fn.Type) - } - - var args *NodeList - n := fn.Type.Intuple - for i := 0; i < n; i++ { - args = list(args, va[i]) - } - - r := Nod(OCALL, fn, nil) - r.List = args - if fn.Type.Outtuple > 0 { - typecheck(&r, Erv|Efnstruct) - } else { - typecheck(&r, Etop) - } - walkexpr(&r, init) - r.Type = t - return r -} - -func mkcall(name string, t *Type, init **NodeList, args ...*Node) *Node { - return vmkcall(syslook(name, 0), t, init, args) -} - -func mkcall1(fn *Node, t *Type, init **NodeList, args ...*Node) *Node { - return vmkcall(fn, t, init, args) -} - -func conv(n *Node, t *Type) *Node { - if Eqtype(n.Type, t) { - return n - } - n = Nod(OCONV, n, nil) - n.Type = t - typecheck(&n, Erv) - return n -} - -func chanfn(name string, n int, t *Type) *Node { - if t.Etype != TCHAN { - Fatal("chanfn %v", t) - } - fn := syslook(name, 1) - switch n { - default: - Fatal("chanfn %d", n) - case 1: - substArgTypes(fn, t.Type) - case 2: - substArgTypes(fn, t.Type, t.Type) - } - return fn -} - -func mapfn(name string, t *Type) *Node { - if t.Etype != TMAP { - Fatal("mapfn %v", t) - } - fn := syslook(name, 1) - substArgTypes(fn, t.Down, t.Type, t.Down, t.Type) - return fn -} - -func mapfndel(name string, t *Type) *Node { - if t.Etype != TMAP { - Fatal("mapfn %v", t) - } - fn := syslook(name, 1) - substArgTypes(fn, t.Down, t.Type, t.Down) - return fn -} - -func writebarrierfn(name string, l *Type, r *Type) *Node { - fn := syslook(name, 1) - substArgTypes(fn, l, r) - return fn -} - -func addstr(n *Node, init **NodeList) *Node { - // orderexpr rewrote OADDSTR to have a list of strings. - c := count(n.List) - - if c < 2 { - Yyerror("addstr count %d too small", c) - } - - buf := nodnil() - if n.Esc == EscNone { - sz := int64(0) - for l := n.List; l != nil; l = l.Next { - if n.Op == OLITERAL { - sz += int64(len(n.Val.U.(string))) - } - } - - // Don't allocate the buffer if the result won't fit. - if sz < tmpstringbufsize { - // Create temporary buffer for result string on stack. - t := aindex(Nodintconst(tmpstringbufsize), Types[TUINT8]) - - buf = Nod(OADDR, temp(t), nil) - } - } - - // build list of string arguments - args := list1(buf) - - for l := n.List; l != nil; l = l.Next { - args = list(args, conv(l.N, Types[TSTRING])) - } - - var fn string - if c <= 5 { - // small numbers of strings use direct runtime helpers. - // note: orderexpr knows this cutoff too. - fn = fmt.Sprintf("concatstring%d", c) - } else { - // large numbers of strings are passed to the runtime as a slice. - fn = "concatstrings" - - t := typ(TARRAY) - t.Type = Types[TSTRING] - t.Bound = -1 - slice := Nod(OCOMPLIT, nil, typenod(t)) - slice.Alloc = n.Alloc - slice.List = args.Next // skip buf arg - args = list1(buf) - args = list(args, slice) - slice.Esc = EscNone - } - - cat := syslook(fn, 1) - r := Nod(OCALL, cat, nil) - r.List = args - typecheck(&r, Erv) - walkexpr(&r, init) - r.Type = n.Type - - return r -} - -// expand append(l1, l2...) to -// init { -// s := l1 -// if n := len(l1) + len(l2) - cap(s); n > 0 { -// s = growslice(s, n) -// } -// s = s[:len(l1)+len(l2)] -// memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T)) -// } -// s -// -// l2 is allowed to be a string. -func appendslice(n *Node, init **NodeList) *Node { - walkexprlistsafe(n.List, 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 { - l.N = cheapexpr(l.N, init) - } - - l1 := n.List.N - l2 := n.List.Next.N - - s := temp(l1.Type) // var s []T - var l *NodeList - l = list(l, Nod(OAS, s, l1)) // s = l1 - - nt := temp(Types[TINT]) - - nif := Nod(OIF, nil, nil) - - // n := len(s) + len(l2) - cap(s) - nif.Ninit = list1(Nod(OAS, nt, Nod(OSUB, Nod(OADD, Nod(OLEN, s, nil), Nod(OLEN, l2, nil)), Nod(OCAP, s, nil)))) - - nif.Ntest = Nod(OGT, nt, Nodintconst(0)) - - // instantiate growslice(Type*, []any, int) []any - fn := syslook("growslice", 1) // growslice(, old []T, n int64) (ret []T) - substArgTypes(fn, s.Type.Type, s.Type.Type) - - // s = growslice(T, s, n) - nif.Nbody = list1(Nod(OAS, s, mkcall1(fn, s.Type, &nif.Ninit, typename(s.Type), s, nt))) - - l = list(l, nif) - - if haspointers(l1.Type.Type) { - // copy(s[len(l1):len(l1)+len(l2)], l2) - nptr1 := Nod(OSLICE, s, Nod(OKEY, Nod(OLEN, l1, nil), Nod(OADD, Nod(OLEN, l1, nil), Nod(OLEN, l2, nil)))) - - nptr1.Etype = 1 - nptr2 := l2 - fn := syslook("typedslicecopy", 1) - substArgTypes(fn, l1.Type, l2.Type) - nt := mkcall1(fn, Types[TINT], &l, typename(l1.Type.Type), nptr1, nptr2) - l = list(l, nt) - } else if flag_race != 0 { - // rely on runtime to instrument copy. - // copy(s[len(l1):len(l1)+len(l2)], l2) - nptr1 := Nod(OSLICE, s, Nod(OKEY, Nod(OLEN, l1, nil), Nod(OADD, Nod(OLEN, l1, nil), Nod(OLEN, l2, nil)))) - - nptr1.Etype = 1 - nptr2 := l2 - var fn *Node - if l2.Type.Etype == TSTRING { - fn = syslook("slicestringcopy", 1) - } else { - fn = syslook("slicecopy", 1) - } - substArgTypes(fn, l1.Type, l2.Type) - nt := mkcall1(fn, Types[TINT], &l, nptr1, nptr2, Nodintconst(s.Type.Type.Width)) - l = list(l, nt) - } else { - // memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T)) - nptr1 := Nod(OINDEX, s, Nod(OLEN, l1, nil)) - - nptr1.Bounded = true - nptr1 = Nod(OADDR, nptr1, nil) - - nptr2 := Nod(OSPTR, l2, nil) - - fn := syslook("memmove", 1) - substArgTypes(fn, s.Type.Type, s.Type.Type) - - nwid := cheapexpr(conv(Nod(OLEN, l2, nil), Types[TUINTPTR]), &l) - - nwid = Nod(OMUL, nwid, Nodintconst(s.Type.Type.Width)) - nt := mkcall1(fn, nil, &l, nptr1, nptr2, nwid) - l = list(l, nt) - } - - // s = s[:len(l1)+len(l2)] - nt = Nod(OADD, Nod(OLEN, l1, nil), Nod(OLEN, l2, nil)) - - nt = Nod(OSLICE, s, Nod(OKEY, nil, nt)) - nt.Etype = 1 - l = list(l, Nod(OAS, s, nt)) - - typechecklist(l, Etop) - walkstmtlist(l) - *init = concat(*init, l) - return s -} - -// 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 -// const argc = len(args) - 1 -// if cap(s) - len(s) < argc { -// s = growslice(s, argc) -// } -// n := len(s) -// s = s[:n+argc] -// s[n] = a -// s[n+1] = b -// ... -// } -// s -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. - // 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) - } - - nsrc := n.List.N - - // Resolve slice type of multi-valued return. - if Istype(nsrc.Type, TSTRUCT) { - nsrc.Type = nsrc.Type.Type.Type - } - argc := count(n.List) - 1 - if argc < 1 { - 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) - l = list(l, Nod(OAS, ns, nsrc)) // s = src - - na := Nodintconst(int64(argc)) // const argc - nx := Nod(OIF, nil, nil) // if cap(s) - len(s) < argc - nx.Ntest = Nod(OLT, Nod(OSUB, Nod(OCAP, ns, nil), Nod(OLEN, ns, nil)), na) - - fn := syslook("growslice", 1) // growslice(, old []T, n int) (ret []T) - substArgTypes(fn, ns.Type.Type, ns.Type.Type) - - nx.Nbody = list1(Nod(OAS, ns, mkcall1(fn, ns.Type, &nx.Ninit, typename(ns.Type), ns, na))) - - l = list(l, nx) - - nn := temp(Types[TINT]) - l = list(l, Nod(OAS, nn, Nod(OLEN, ns, nil))) // n = len(s) - - nx = Nod(OSLICE, ns, Nod(OKEY, nil, Nod(OADD, nn, na))) // ...s[:n+argc] - nx.Etype = 1 - l = list(l, Nod(OAS, ns, nx)) // s = s[:n+argc] - - for a := n.List.Next; a != nil; a = a.Next { - nx = Nod(OINDEX, ns, nn) // s[n] ... - nx.Bounded = true - l = list(l, Nod(OAS, nx, a.N)) // s[n] = arg - if a.Next != nil { - l = list(l, Nod(OAS, nn, Nod(OADD, nn, Nodintconst(1)))) // n = n + 1 - } - } - - typechecklist(l, Etop) - walkstmtlist(l) - *init = concat(*init, l) - return ns -} - -// Lower copy(a, b) to a memmove call or a runtime call. -// -// init { -// n := len(a) -// if n > len(b) { n = len(b) } -// memmove(a.ptr, b.ptr, n*sizeof(elem(a))) -// } -// n; -// -// Also works if b is a string. -// -func copyany(n *Node, init **NodeList, runtimecall int) *Node { - if haspointers(n.Left.Type.Type) { - fn := writebarrierfn("typedslicecopy", n.Left.Type, n.Right.Type) - return mkcall1(fn, n.Type, init, typename(n.Left.Type.Type), n.Left, n.Right) - } - - if runtimecall != 0 { - var fn *Node - if n.Right.Type.Etype == TSTRING { - fn = syslook("slicestringcopy", 1) - } else { - fn = syslook("slicecopy", 1) - } - substArgTypes(fn, n.Left.Type, n.Right.Type) - return mkcall1(fn, n.Type, init, n.Left, n.Right, Nodintconst(n.Left.Type.Type.Width)) - } - - walkexpr(&n.Left, init) - walkexpr(&n.Right, init) - nl := temp(n.Left.Type) - nr := temp(n.Right.Type) - var l *NodeList - l = list(l, Nod(OAS, nl, n.Left)) - l = list(l, Nod(OAS, nr, n.Right)) - - nfrm := Nod(OSPTR, nr, nil) - nto := Nod(OSPTR, nl, nil) - - nlen := temp(Types[TINT]) - - // n = len(to) - l = list(l, Nod(OAS, nlen, Nod(OLEN, nl, nil))) - - // if n > len(frm) { n = len(frm) } - nif := Nod(OIF, nil, nil) - - nif.Ntest = Nod(OGT, nlen, Nod(OLEN, nr, nil)) - nif.Nbody = list(nif.Nbody, Nod(OAS, nlen, Nod(OLEN, nr, nil))) - l = list(l, nif) - - // Call memmove. - fn := syslook("memmove", 1) - - substArgTypes(fn, nl.Type.Type, nl.Type.Type) - nwid := temp(Types[TUINTPTR]) - l = list(l, Nod(OAS, nwid, conv(nlen, Types[TUINTPTR]))) - nwid = Nod(OMUL, nwid, Nodintconst(nl.Type.Type.Width)) - l = list(l, mkcall1(fn, nil, init, nto, nfrm, nwid)) - - typechecklist(l, Etop) - walkstmtlist(l) - *init = concat(*init, l) - return nlen -} - -func eqfor(t *Type, needsize *int) *Node { - // Should only arrive here with large memory or - // a struct/array containing a non-memory field/element. - // Small memory is handled inline, and single non-memory - // is handled during type check (OCMPSTR etc). - a := algtype1(t, nil) - - if a != AMEM && a != -1 { - Fatal("eqfor %v", t) - } - - if a == AMEM { - n := syslook("memequal", 1) - substArgTypes(n, t, t) - *needsize = 1 - return n - } - - sym := typesymprefix(".eq", t) - n := newname(sym) - n.Class = PFUNC - ntype := Nod(OTFUNC, nil, nil) - ntype.List = list(ntype.List, Nod(ODCLFIELD, nil, typenod(Ptrto(t)))) - ntype.List = list(ntype.List, Nod(ODCLFIELD, nil, typenod(Ptrto(t)))) - ntype.Rlist = list(ntype.Rlist, Nod(ODCLFIELD, nil, typenod(Types[TBOOL]))) - typecheck(&ntype, Etype) - n.Type = ntype.Type - *needsize = 0 - return n -} - -func countfield(t *Type) int { - n := 0 - for t1 := t.Type; t1 != nil; t1 = t1.Down { - n++ - } - return n -} - -func walkcompare(np **Node, init **NodeList) { - n := *np - - // Given interface value l and concrete value r, rewrite - // l == r - // to - // x, ok := l.(type(r)); ok && x == r - // Handle != similarly. - // This avoids the allocation that would be required - // to convert r to l for comparison. - var l *Node - - var r *Node - if Isinter(n.Left.Type) && !Isinter(n.Right.Type) { - l = n.Left - r = n.Right - } else if !Isinter(n.Left.Type) && Isinter(n.Right.Type) { - l = n.Right - r = n.Left - } - - if l != nil { - x := temp(r.Type) - ok := temp(Types[TBOOL]) - - // l.(type(r)) - a := Nod(ODOTTYPE, l, nil) - - a.Type = r.Type - - // x, ok := l.(type(r)) - expr := Nod(OAS2, nil, nil) - - expr.List = list1(x) - expr.List = list(expr.List, ok) - expr.Rlist = list1(a) - typecheck(&expr, Etop) - walkexpr(&expr, init) - - if n.Op == OEQ { - r = Nod(OANDAND, ok, Nod(OEQ, x, r)) - } else { - r = Nod(OOROR, Nod(ONOT, ok, nil), Nod(ONE, x, r)) - } - *init = list(*init, expr) - finishcompare(np, n, r, init) - return - } - - // Must be comparison of array or struct. - // Otherwise back end handles it. - t := n.Left.Type - - switch t.Etype { - default: - return - - case TARRAY: - if Isslice(t) { - return - } - - case TSTRUCT: - break - } - - cmpl := n.Left - for cmpl != nil && cmpl.Op == OCONVNOP { - cmpl = cmpl.Left - } - cmpr := n.Right - for cmpr != nil && cmpr.Op == OCONVNOP { - cmpr = cmpr.Left - } - - if !islvalue(cmpl) || !islvalue(cmpr) { - Fatal("arguments of comparison must be lvalues - %v %v", cmpl, cmpr) - } - - l = temp(Ptrto(t)) - a := Nod(OAS, l, Nod(OADDR, cmpl, nil)) - a.Right.Etype = 1 // addr does not escape - typecheck(&a, Etop) - *init = list(*init, a) - - r = temp(Ptrto(t)) - a = Nod(OAS, r, Nod(OADDR, cmpr, nil)) - a.Right.Etype = 1 // addr does not escape - typecheck(&a, Etop) - *init = list(*init, a) - - andor := OANDAND - if n.Op == ONE { - andor = OOROR - } - - var expr *Node - if t.Etype == TARRAY && t.Bound <= 4 && issimple[t.Type.Etype] { - // Four or fewer elements of a basic type. - // Unroll comparisons. - var li *Node - var ri *Node - for i := 0; int64(i) < t.Bound; i++ { - li = Nod(OINDEX, l, Nodintconst(int64(i))) - ri = Nod(OINDEX, r, Nodintconst(int64(i))) - a = Nod(int(n.Op), li, ri) - if expr == nil { - expr = a - } else { - expr = Nod(andor, expr, a) - } - } - - if expr == nil { - expr = Nodbool(n.Op == OEQ) - } - finishcompare(np, n, expr, init) - return - } - - if t.Etype == TSTRUCT && countfield(t) <= 4 { - // Struct of four or fewer fields. - // Inline comparisons. - var li *Node - var ri *Node - for t1 := t.Type; t1 != nil; t1 = t1.Down { - if isblanksym(t1.Sym) { - continue - } - li = Nod(OXDOT, l, newname(t1.Sym)) - ri = Nod(OXDOT, r, newname(t1.Sym)) - a = Nod(int(n.Op), li, ri) - if expr == nil { - expr = a - } else { - expr = Nod(andor, expr, a) - } - } - - if expr == nil { - expr = Nodbool(n.Op == OEQ) - } - finishcompare(np, n, expr, init) - return - } - - // Chose not to inline. Call equality function directly. - var needsize int - call := Nod(OCALL, eqfor(t, &needsize), nil) - - call.List = list(call.List, l) - call.List = list(call.List, r) - if needsize != 0 { - call.List = list(call.List, Nodintconst(t.Width)) - } - r = call - if n.Op != OEQ { - r = Nod(ONOT, r, nil) - } - - finishcompare(np, n, r, init) - return -} - -func finishcompare(np **Node, n, r *Node, init **NodeList) { - // Using np here to avoid passing &r to typecheck. - *np = r - typecheck(np, Erv) - walkexpr(np, init) - r = *np - if r.Type != n.Type { - r = Nod(OCONVNOP, r, nil) - r.Type = n.Type - r.Typecheck = 1 - *np = r - } -} - -func samecheap(a *Node, b *Node) bool { - var ar *Node - var br *Node - for a != nil && b != nil && a.Op == b.Op { - switch a.Op { - default: - return false - - case ONAME: - return a == b - - case ODOT, ODOTPTR: - ar = a.Right - br = b.Right - if ar.Op != ONAME || br.Op != ONAME || ar.Sym != br.Sym { - return false - } - - case OINDEX: - ar = a.Right - br = b.Right - if !Isconst(ar, CTINT) || !Isconst(br, CTINT) || Mpcmpfixfix(ar.Val.U.(*Mpint), br.Val.U.(*Mpint)) != 0 { - return false - } - } - - a = a.Left - b = b.Left - } - - return false -} - -func walkrotate(np **Node) { - if Thearch.Thechar == '7' || Thearch.Thechar == '9' { - return - } - - n := *np - - // Want << | >> or >> | << or << ^ >> or >> ^ << on unsigned value. - l := n.Left - - r := n.Right - if (n.Op != OOR && n.Op != OXOR) || (l.Op != OLSH && l.Op != ORSH) || (r.Op != OLSH && r.Op != ORSH) || n.Type == nil || Issigned[n.Type.Etype] || l.Op == r.Op { - return - } - - // Want same, side effect-free expression on lhs of both shifts. - if !samecheap(l.Left, r.Left) { - return - } - - // Constants adding to width? - w := int(l.Type.Width * 8) - - if Smallintconst(l.Right) && Smallintconst(r.Right) { - sl := int(Mpgetfix(l.Right.Val.U.(*Mpint))) - if sl >= 0 { - 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 { - n = l - } else { - n = r - } - n.Op = OLROT - - // Remove rotate 0 and rotate w. - s := int(Mpgetfix(n.Right.Val.U.(*Mpint))) - - if s == 0 || s == w { - n = n.Left - } - - *np = n - return - } - } - return - } - - // TODO: Could allow s and 32-s if s is bounded (maybe s&31 and 32-s&31). - return -} - -/* - * walkmul rewrites integer multiplication by powers of two as shifts. - */ -func walkmul(np **Node, init **NodeList) { - n := *np - if !Isint[n.Type.Etype] { - return - } - - var nr *Node - var nl *Node - if n.Right.Op == OLITERAL { - nl = n.Left - nr = n.Right - } else if n.Left.Op == OLITERAL { - nl = n.Right - nr = n.Left - } else { - return - } - - neg := 0 - - // x*0 is 0 (and side effects of x). - var pow int - var w int - if Mpgetfix(nr.Val.U.(*Mpint)) == 0 { - cheapexpr(nl, init) - Nodconst(n, n.Type, 0) - goto ret - } - - // nr is a constant. - pow = powtwo(nr) - - if pow < 0 { - return - } - if pow >= 1000 { - // negative power of 2, like -16 - neg = 1 - - pow -= 1000 - } - - w = int(nl.Type.Width * 8) - if pow+1 >= w { // too big, shouldn't happen - return - } - - nl = cheapexpr(nl, init) - - if pow == 0 { - // x*1 is x - n = nl - - goto ret - } - - n = Nod(OLSH, nl, Nodintconst(int64(pow))) - -ret: - if neg != 0 { - n = Nod(OMINUS, n, nil) - } - - typecheck(&n, Erv) - walkexpr(&n, init) - *np = n -} - -/* - * walkdiv rewrites division by a constant as less expensive - * operations. - */ -func walkdiv(np **Node, init **NodeList) { - // if >= 0, nr is 1<= 0, nr is 1<= 1000 { - // negative power of 2 - s = 1 - - pow -= 1000 - } - - if pow+1 >= w { - // divisor too large. - return - } - - if pow < 0 { - // try to do division by multiply by (2^w)/d - // see hacker's delight chapter 10 - // TODO: support 64-bit magic multiply here. - var m Magic - m.W = w - - if Issigned[nl.Type.Etype] { - m.Sd = Mpgetfix(nr.Val.U.(*Mpint)) - Smagic(&m) - } else { - m.Ud = uint64(Mpgetfix(nr.Val.U.(*Mpint))) - Umagic(&m) - } - - if m.Bad != 0 { - return - } - - // We have a quick division method so use it - // for modulo too. - if n.Op == OMOD { - // rewrite as A%B = A - (A/B*B). - n1 := Nod(ODIV, nl, nr) - - n2 := Nod(OMUL, n1, nr) - n = Nod(OSUB, nl, n2) - goto ret - } - - switch Simtype[nl.Type.Etype] { - default: - return - - // n1 = nl * magic >> w (HMUL) - case TUINT8, TUINT16, TUINT32: - nc := Nod(OXXX, nil, nil) - - Nodconst(nc, nl.Type, int64(m.Um)) - n1 := Nod(OMUL, nl, nc) - typecheck(&n1, Erv) - n1.Op = OHMUL - if m.Ua != 0 { - // Select a Go type with (at least) twice the width. - var twide *Type - switch Simtype[nl.Type.Etype] { - default: - return - - case TUINT8, TUINT16: - twide = Types[TUINT32] - - case TUINT32: - twide = Types[TUINT64] - - case TINT8, TINT16: - twide = Types[TINT32] - - case TINT32: - twide = Types[TINT64] - } - - // add numerator (might overflow). - // n2 = (n1 + nl) - n2 := Nod(OADD, conv(n1, twide), conv(nl, twide)) - - // shift by m.s - nc := Nod(OXXX, nil, nil) - - Nodconst(nc, Types[TUINT], int64(m.S)) - n = conv(Nod(ORSH, n2, nc), nl.Type) - } else { - // n = n1 >> m.s - nc := Nod(OXXX, nil, nil) - - Nodconst(nc, Types[TUINT], int64(m.S)) - n = Nod(ORSH, n1, nc) - } - - // n1 = nl * magic >> w - case TINT8, TINT16, TINT32: - nc := Nod(OXXX, nil, nil) - - Nodconst(nc, nl.Type, m.Sm) - n1 := Nod(OMUL, nl, nc) - typecheck(&n1, Erv) - n1.Op = OHMUL - if m.Sm < 0 { - // add the numerator. - n1 = Nod(OADD, n1, nl) - } - - // shift by m.s - nc = Nod(OXXX, nil, nil) - - Nodconst(nc, Types[TUINT], int64(m.S)) - n2 := conv(Nod(ORSH, n1, nc), nl.Type) - - // add 1 iff n1 is negative. - nc = Nod(OXXX, nil, nil) - - Nodconst(nc, Types[TUINT], int64(w)-1) - n3 := Nod(ORSH, nl, nc) // n4 = -1 iff n1 is negative. - n = Nod(OSUB, n2, n3) - - // apply sign. - if m.Sd < 0 { - n = Nod(OMINUS, n, nil) - } - } - - goto ret - } - - switch pow { - case 0: - if n.Op == OMOD { - // nl % 1 is zero. - Nodconst(n, n.Type, 0) - } else if s != 0 { - // divide by -1 - n.Op = OMINUS - - n.Right = nil - } else { - // divide by 1 - n = nl - } - - default: - if Issigned[n.Type.Etype] { - if n.Op == OMOD { - // signed modulo 2^pow is like ANDing - // with the last pow bits, but if nl < 0, - // nl & (2^pow-1) is (nl+1)%2^pow - 1. - nc := Nod(OXXX, nil, nil) - - Nodconst(nc, Types[Simtype[TUINT]], int64(w)-1) - n1 := Nod(ORSH, nl, nc) // n1 = -1 iff nl < 0. - if pow == 1 { - typecheck(&n1, Erv) - n1 = cheapexpr(n1, init) - - // n = (nl+ε)&1 -ε where ε=1 iff nl<0. - n2 := Nod(OSUB, nl, n1) - - nc := Nod(OXXX, nil, nil) - Nodconst(nc, nl.Type, 1) - n3 := Nod(OAND, n2, nc) - n = Nod(OADD, n3, n1) - } else { - // n = (nl+ε)&(nr-1) - ε where ε=2^pow-1 iff nl<0. - nc := Nod(OXXX, nil, nil) - - Nodconst(nc, nl.Type, (1<= 0, nl >> n == nl / nr - // if nl < 0, we want to add 2^n-1 first. - nc := Nod(OXXX, nil, nil) - - Nodconst(nc, Types[Simtype[TUINT]], int64(w)-1) - n1 := Nod(ORSH, nl, nc) // n1 = -1 iff nl < 0. - if pow == 1 { - // nl+1 is nl-(-1) - n.Left = Nod(OSUB, nl, n1) - } else { - // Do a logical right right on -1 to keep pow bits. - nc := Nod(OXXX, nil, nil) - - Nodconst(nc, Types[Simtype[TUINT]], int64(w)-int64(pow)) - n2 := Nod(ORSH, conv(n1, tounsigned(nl.Type)), nc) - n.Left = Nod(OADD, nl, conv(n2, nl.Type)) - } - - // n = (nl + 2^pow-1) >> pow - n.Op = ORSH - - nc = Nod(OXXX, nil, nil) - Nodconst(nc, Types[Simtype[TUINT]], int64(pow)) - n.Right = nc - n.Typecheck = 0 - } - - if s != 0 { - n = Nod(OMINUS, n, nil) - } - break - } - - nc := Nod(OXXX, nil, nil) - if n.Op == OMOD { - // n = nl & (nr-1) - n.Op = OAND - - Nodconst(nc, nl.Type, Mpgetfix(nr.Val.U.(*Mpint))-1) - } else { - // n = nl >> pow - n.Op = ORSH - - Nodconst(nc, Types[Simtype[TUINT]], int64(pow)) - } - - n.Typecheck = 0 - n.Right = nc - } - - goto ret - -ret: - typecheck(&n, Erv) - walkexpr(&n, init) - *np = n -} - -// return 1 if integer n must be in range [0, max), 0 otherwise -func bounded(n *Node, max int64) bool { - if n.Type == nil || !Isint[n.Type.Etype] { - return false - } - - sign := Issigned[n.Type.Etype] - bits := int32(8 * n.Type.Width) - - if Smallintconst(n) { - v := Mpgetfix(n.Val.U.(*Mpint)) - return 0 <= v && v < max - } - - switch n.Op { - case OAND: - v := int64(-1) - if Smallintconst(n.Left) { - v = Mpgetfix(n.Left.Val.U.(*Mpint)) - } else if Smallintconst(n.Right) { - v = Mpgetfix(n.Right.Val.U.(*Mpint)) - } - - if 0 <= v && v < max { - return true - } - - case OMOD: - if !sign && Smallintconst(n.Right) { - v := Mpgetfix(n.Right.Val.U.(*Mpint)) - if 0 <= v && v <= max { - return true - } - } - - case ODIV: - if !sign && Smallintconst(n.Right) { - v := Mpgetfix(n.Right.Val.U.(*Mpint)) - for bits > 0 && v >= 2 { - bits-- - v >>= 1 - } - } - - case ORSH: - if !sign && Smallintconst(n.Right) { - v := Mpgetfix(n.Right.Val.U.(*Mpint)) - if v > int64(bits) { - return true - } - bits -= int32(v) - } - } - - if !sign && bits <= 62 && 1<= 1 && c-1 < len(yyToknames) { - if yyToknames[c-1] != "" { - return yyToknames[c-1] - } - } - return __yyfmt__.Sprintf("tok-%v", c) -} - -func yyStatname(s int) string { - if s >= 0 && s < len(yyStatenames) { - if yyStatenames[s] != "" { - return yyStatenames[s] - } - } - return __yyfmt__.Sprintf("state-%v", s) -} - -func yyErrorMessage(state, lookAhead int) string { - const TOKSTART = 4 - - 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. - expected := make([]int, 0, 4) - - // Look for shiftable tokens. - base := yyPact[state] - for tok := TOKSTART; tok-1 < len(yyToknames); tok++ { - if n := base + tok; n >= 0 && n < yyLast && yyChk[yyAct[n]] == tok { - if len(expected) == cap(expected) { - return res - } - expected = append(expected, tok) - } - } - - if yyDef[state] == -2 { - i := 0 - for yyExca[i] != -1 || yyExca[i+1] != state { - i += 2 - } - - // Look for tokens that we accept or reduce. - for i += 2; yyExca[i] >= 0; i += 2 { - tok := yyExca[i] - if tok < TOKSTART || yyExca[i+1] == 0 { - continue - } - if len(expected) == cap(expected) { - return res - } - expected = append(expected, tok) - } - - // If the default action is to accept or reduce, give up. - if yyExca[i+1] != 0 { - return res - } - } - - for i, tok := range expected { - if i == 0 { - res += ", expecting " - } else { - res += " or " - } - res += yyTokname(tok) - } - return res -} - -func yylex1(lex yyLexer, lval *yySymType) (char, token int) { - token = 0 - char = lex.Lex(lval) - if char <= 0 { - token = yyTok1[0] - goto out - } - if char < len(yyTok1) { - token = yyTok1[char] - goto out - } - if char >= yyPrivate { - if char < yyPrivate+len(yyTok2) { - token = yyTok2[char-yyPrivate] - goto out - } - } - for i := 0; i < len(yyTok3); i += 2 { - token = yyTok3[i+0] - if token == char { - token = yyTok3[i+1] - goto out - } - } - -out: - if token == 0 { - token = yyTok2[1] /* unknown char */ - } - if yyDebug >= 3 { - __yyfmt__.Printf("lex %s(%d)\n", yyTokname(token), uint(char)) - } - return char, token -} - -func yyParse(yylex yyLexer) int { - return yyNewParser().Parse(yylex) -} - -func (yyrcvr *yyParserImpl) Parse(yylex yyLexer) int { - var yyn int - var yylval yySymType - var yyVAL yySymType - var yyDollar []yySymType - yyS := make([]yySymType, yyMaxDepth) - - Nerrs := 0 /* number of errors */ - Errflag := 0 /* error recovery flag */ - yystate := 0 - yychar := -1 - yytoken := -1 // yychar translated into internal numbering - yyrcvr.lookahead = func() int { return yychar } - defer func() { - // Make sure we report no lookahead when not parsing. - yystate = -1 - yychar = -1 - yytoken = -1 - }() - yyp := -1 - goto yystack - -ret0: - return 0 - -ret1: - return 1 - -yystack: - /* put a state and value onto the stack */ - if yyDebug >= 4 { - __yyfmt__.Printf("char %v in %v\n", yyTokname(yytoken), yyStatname(yystate)) - } - - yyp++ - if yyp >= len(yyS) { - nyys := make([]yySymType, len(yyS)*2) - copy(nyys, yyS) - yyS = nyys - } - yyS[yyp] = yyVAL - yyS[yyp].yys = yystate - -yynewstate: - yyn = yyPact[yystate] - if yyn <= yyFlag { - goto yydefault /* simple state */ - } - if yychar < 0 { - yychar, yytoken = yylex1(yylex, &yylval) - } - yyn += yytoken - if yyn < 0 || yyn >= yyLast { - goto yydefault - } - yyn = yyAct[yyn] - if yyChk[yyn] == yytoken { /* valid shift */ - yychar = -1 - yytoken = -1 - yyVAL = yylval - yystate = yyn - if Errflag > 0 { - Errflag-- - } - goto yystack - } - -yydefault: - /* default state action */ - yyn = yyDef[yystate] - if yyn == -2 { - if yychar < 0 { - yychar, yytoken = yylex1(yylex, &yylval) - } - - /* look through exception table */ - xi := 0 - for { - if yyExca[xi+0] == -1 && yyExca[xi+1] == yystate { - break - } - xi += 2 - } - for xi += 2; ; xi += 2 { - yyn = yyExca[xi+0] - if yyn < 0 || yyn == yytoken { - break - } - } - yyn = yyExca[xi+1] - if yyn < 0 { - goto ret0 - } - } - if yyn == 0 { - /* error ... attempt to resume parsing */ - switch Errflag { - case 0: /* brand new error */ - yylex.Error(yyErrorMessage(yystate, yytoken)) - Nerrs++ - if yyDebug >= 1 { - __yyfmt__.Printf("%s", yyStatname(yystate)) - __yyfmt__.Printf(" saw %s\n", yyTokname(yytoken)) - } - fallthrough - - case 1, 2: /* incompletely recovered error ... try again */ - Errflag = 3 - - /* find a state where "error" is a legal shift action */ - for yyp >= 0 { - yyn = yyPact[yyS[yyp].yys] + yyErrCode - if yyn >= 0 && yyn < yyLast { - yystate = yyAct[yyn] /* simulate a shift of "error" */ - if yyChk[yystate] == yyErrCode { - goto yystack - } - } - - /* the current p has no shift on "error", pop stack */ - if yyDebug >= 2 { - __yyfmt__.Printf("error recovery pops state %d\n", yyS[yyp].yys) - } - yyp-- - } - /* there is no state on the stack with an error shift ... abort */ - goto ret1 - - case 3: /* no shift yet; clobber input char */ - if yyDebug >= 2 { - __yyfmt__.Printf("error recovery discards %s\n", yyTokname(yytoken)) - } - if yytoken == yyEofCode { - goto ret1 - } - yychar = -1 - yytoken = -1 - goto yynewstate /* try again in the same state */ - } - } - - /* reduction by production yyn */ - if yyDebug >= 2 { - __yyfmt__.Printf("reduce %v in:\n\t%v\n", yyn, yyStatname(yystate)) - } - - yynt := yyn - yypt := yyp - _ = yypt // guard against "declared and not used" - - yyp -= yyR2[yyn] - // yyp is now the index of $0. Perform the default action. Iff the - // reduced production is ε, $1 is possibly out of range. - if yyp+1 >= len(yyS) { - nyys := make([]yySymType, len(yyS)*2) - copy(nyys, yyS) - yyS = nyys - } - yyVAL = yyS[yyp+1] - - /* consult goto table to find next state */ - yyn = yyR1[yyn] - yyg := yyPgo[yyn] - yyj := yyg + yyS[yyp].yys + 1 - - if yyj >= yyLast { - yystate = yyAct[yyg] - } else { - yystate = yyAct[yyj] - if yyChk[yystate] != -yyn { - yystate = yyAct[yyg] - } - } - // dummy call; replaced with literal code - switch yynt { - - case 1: - yyDollar = yyS[yypt-4 : yypt+1] - //line go.y:189 - { - xtop = concat(xtop, yyDollar[4].list) - } - case 2: - yyDollar = yyS[yypt-0 : yypt+1] - //line go.y:195 - { - prevlineno = lineno - Yyerror("package statement must be first") - errorexit() - } - case 3: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:201 - { - mkpackage(yyDollar[2].sym.Name) - } - case 4: - yyDollar = yyS[yypt-0 : yypt+1] - //line go.y:211 - { - importpkg = Runtimepkg - - if Debug['A'] != 0 { - cannedimports("runtime.Builtin", "package runtime\n\n$$\n\n") - } else { - cannedimports("runtime.Builtin", runtimeimport) - } - curio.importsafe = true - } - case 5: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:223 - { - importpkg = nil - } - case 11: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:237 - { - ipkg := importpkg - my := importmyname - importpkg = nil - importmyname = nil - - if my == nil { - my = Lookup(ipkg.Name) - } - - pack := Nod(OPACK, nil, nil) - pack.Sym = my - pack.Pkg = ipkg - pack.Lineno = int32(yyDollar[1].i) - - if strings.HasPrefix(my.Name, ".") { - importdot(ipkg, pack) - break - } - if my.Name == "init" { - Yyerror("cannot import package as init - init must be a func") - break - } - if my.Name == "_" { - break - } - if my.Def != nil { - lineno = int32(yyDollar[1].i) - redeclare(my, "as imported package name") - } - my.Def = pack - my.Lastlineno = int32(yyDollar[1].i) - my.Block = 1 // at top level - } - case 12: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:272 - { - // When an invalid import path is passed to importfile, - // it calls Yyerror and then sets up a fake import with - // no package statement. This allows us to test more - // than one invalid import statement in a single file. - if nerrors == 0 { - Fatal("phase error in import") - } - } - case 15: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:288 - { - // import with original name - yyVAL.i = parserline() - importmyname = nil - importfile(&yyDollar[1].val, yyVAL.i) - } - case 16: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:295 - { - // import with given name - yyVAL.i = parserline() - importmyname = yyDollar[1].sym - importfile(&yyDollar[2].val, yyVAL.i) - } - case 17: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:302 - { - // import into my name space - yyVAL.i = parserline() - importmyname = Lookup(".") - importfile(&yyDollar[2].val, yyVAL.i) - } - case 18: - yyDollar = yyS[yypt-4 : yypt+1] - //line go.y:311 - { - if importpkg.Name == "" { - importpkg.Name = yyDollar[2].sym.Name - numImport[yyDollar[2].sym.Name]++ - } else if importpkg.Name != yyDollar[2].sym.Name { - Yyerror("conflicting names %s and %s for package %q", importpkg.Name, yyDollar[2].sym.Name, importpkg.Path) - } - importpkg.Direct = 1 - importpkg.Safe = curio.importsafe - - if safemode != 0 && !curio.importsafe { - Yyerror("cannot import unsafe package %q", importpkg.Path) - } - } - case 20: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:328 - { - if yyDollar[1].sym.Name == "safe" { - curio.importsafe = true - } - } - case 21: - yyDollar = yyS[yypt-0 : yypt+1] - //line go.y:335 - { - defercheckwidth() - } - case 22: - yyDollar = yyS[yypt-4 : yypt+1] - //line go.y:339 - { - resumecheckwidth() - unimportfile() - } - case 23: - yyDollar = yyS[yypt-0 : yypt+1] - //line go.y:348 - { - Yyerror("empty top-level declaration") - yyVAL.list = nil - } - case 25: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:354 - { - yyVAL.list = list1(yyDollar[1].node) - } - case 26: - yyDollar = yyS[yypt-1 : yypt+1] - //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:363 - { - yyVAL.list = nil - } - case 28: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:369 - { - yyVAL.list = yyDollar[2].list - } - case 29: - yyDollar = yyS[yypt-5 : yypt+1] - //line go.y:373 - { - yyVAL.list = yyDollar[3].list - } - case 30: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:377 - { - yyVAL.list = nil - } - case 31: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:381 - { - yyVAL.list = yyDollar[2].list - iota_ = -100000 - lastconst = nil - } - case 32: - yyDollar = yyS[yypt-5 : yypt+1] - //line go.y:387 - { - yyVAL.list = yyDollar[3].list - iota_ = -100000 - lastconst = nil - } - case 33: - yyDollar = yyS[yypt-7 : yypt+1] - //line go.y:393 - { - yyVAL.list = concat(yyDollar[3].list, yyDollar[5].list) - iota_ = -100000 - lastconst = nil - } - case 34: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:399 - { - yyVAL.list = nil - iota_ = -100000 - } - case 35: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:404 - { - yyVAL.list = list1(yyDollar[2].node) - } - case 36: - yyDollar = yyS[yypt-5 : yypt+1] - //line go.y:408 - { - yyVAL.list = yyDollar[3].list - } - case 37: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:412 - { - yyVAL.list = nil - } - case 38: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:418 - { - iota_ = 0 - } - case 39: - yyDollar = yyS[yypt-2 : yypt+1] - //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:428 - { - yyVAL.list = variter(yyDollar[1].list, yyDollar[2].node, yyDollar[4].list) - } - case 41: - yyDollar = yyS[yypt-3 : yypt+1] - //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:438 - { - yyVAL.list = constiter(yyDollar[1].list, yyDollar[2].node, yyDollar[4].list) - } - case 43: - yyDollar = yyS[yypt-3 : yypt+1] - //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:449 - { - yyVAL.list = constiter(yyDollar[1].list, yyDollar[2].node, nil) - } - case 46: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:453 - { - yyVAL.list = constiter(yyDollar[1].list, nil, nil) - } - case 47: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:459 - { - // different from dclname because the name - // becomes visible right here, not at the end - // of the declaration. - yyVAL.node = typedcl0(yyDollar[1].sym) - } - case 48: - yyDollar = yyS[yypt-2 : yypt+1] - //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:474 - { - yyVAL.node = yyDollar[1].node - - // These nodes do not carry line numbers. - // Since a bare name used as an expression is an error, - // introduce a wrapper node to give the correct line. - switch yyVAL.node.Op { - case ONAME, ONONAME, OTYPE, OPACK, OLITERAL: - yyVAL.node = Nod(OPAREN, yyVAL.node, nil) - yyVAL.node.Implicit = true - break - } - } - case 50: - yyDollar = yyS[yypt-3 : yypt+1] - //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:493 - { - if yyDollar[1].list.Next == nil && yyDollar[3].list.Next == nil { - // simple - yyVAL.node = Nod(OAS, yyDollar[1].list.N, yyDollar[3].list.N) - break - } - // multiple - yyVAL.node = Nod(OAS2, nil, nil) - yyVAL.node.List = yyDollar[1].list - yyVAL.node.Rlist = yyDollar[3].list - } - case 52: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:505 - { - if yyDollar[3].list.N.Op == OTYPESW { - yyVAL.node = Nod(OTYPESW, nil, yyDollar[3].list.N.Right) - if yyDollar[3].list.Next != nil { - Yyerror("expr.(type) must be alone in list") - } - if yyDollar[1].list.Next != nil { - Yyerror("argument count mismatch: %d = %d", count(yyDollar[1].list), 1) - } else if (yyDollar[1].list.N.Op != ONAME && yyDollar[1].list.N.Op != OTYPE && yyDollar[1].list.N.Op != ONONAME) || isblank(yyDollar[1].list.N) { - Yyerror("invalid variable name %s in type switch", yyDollar[1].list.N) - } else { - yyVAL.node.Left = dclname(yyDollar[1].list.N.Sym) - } // it's a colas, so must not re-use an oldname. - break - } - yyVAL.node = colas(yyDollar[1].list, yyDollar[3].list, int32(yyDollar[2].i)) - } - case 53: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:523 - { - yyVAL.node = Nod(OASOP, yyDollar[1].node, Nodintconst(1)) - yyVAL.node.Implicit = true - yyVAL.node.Etype = OADD - } - case 54: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:529 - { - yyVAL.node = Nod(OASOP, yyDollar[1].node, Nodintconst(1)) - yyVAL.node.Implicit = true - yyVAL.node.Etype = OSUB - } - case 55: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:537 - { - var n, nn *Node - - // will be converted to OCASE - // right will point to next case - // done in casebody() - markdcl() - yyVAL.node = Nod(OXCASE, nil, nil) - yyVAL.node.List = yyDollar[2].list - if typesw != nil && typesw.Right != nil { - n = typesw.Right.Left - if n != nil { - // type switch - declare variable - nn = newname(n.Sym) - declare(nn, dclcontext) - yyVAL.node.Nname = nn - - // keep track of the instances for reporting unused - nn.Defn = typesw.Right - } - } - } - case 56: - yyDollar = yyS[yypt-5 : yypt+1] - //line go.y:560 - { - var n *Node - - // will be converted to OCASE - // right will point to next case - // done in casebody() - markdcl() - yyVAL.node = Nod(OXCASE, nil, nil) - if yyDollar[2].list.Next == nil { - n = Nod(OAS, yyDollar[2].list.N, yyDollar[4].node) - } else { - n = Nod(OAS2, nil, nil) - n.List = yyDollar[2].list - n.Rlist = list1(yyDollar[4].node) - } - yyVAL.node.List = list1(n) - } - case 57: - yyDollar = yyS[yypt-5 : yypt+1] - //line go.y:578 - { - // will be converted to OCASE - // right will point to next case - // done in casebody() - markdcl() - yyVAL.node = Nod(OXCASE, nil, nil) - yyVAL.node.List = list1(colas(yyDollar[2].list, list1(yyDollar[4].node), int32(yyDollar[3].i))) - } - case 58: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:587 - { - var n, nn *Node - - markdcl() - yyVAL.node = Nod(OXCASE, nil, nil) - if typesw != nil && typesw.Right != nil { - n = typesw.Right.Left - if n != nil { - // type switch - declare variable - nn = newname(n.Sym) - declare(nn, dclcontext) - yyVAL.node.Nname = nn - - // keep track of the instances for reporting unused - nn.Defn = typesw.Right - } - } - } - case 59: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:608 - { - markdcl() - } - case 60: - yyDollar = yyS[yypt-4 : yypt+1] - //line go.y:612 - { - if yyDollar[3].list == nil { - yyVAL.node = Nod(OEMPTY, nil, nil) - } else { - yyVAL.node = liststmt(yyDollar[3].list) - } - popdcl() - } - case 61: - yyDollar = yyS[yypt-1 : yypt+1] - //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). - // If the last token read by the lexer was the lookahead - // leave it alone (parser has it cached in yychar). - // This is so that the stmt_list action doesn't look at - // the case tokens if the stmt_list is empty. - yylast = yychar - yyDollar[1].node.Xoffset = int64(block) - } - case 62: - yyDollar = yyS[yypt-3 : yypt+1] - //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 - // it's the only place where a statement list is not followed - // by a closing brace. Handle the error for pedantry. - - // Find the final token of the statement list. - // yylast is lookahead; yyprev is last of stmt_list - last := yyprev - - if last > 0 && last != ';' && yychar != '}' { - Yyerror("missing statement after label") - } - yyVAL.node = yyDollar[1].node - yyVAL.node.Nbody = yyDollar[3].list - popdcl() - } - case 63: - yyDollar = yyS[yypt-0 : yypt+1] - //line go.y:653 - { - yyVAL.list = nil - } - case 64: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:657 - { - yyVAL.list = list(yyDollar[1].list, yyDollar[2].node) - } - case 65: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:663 - { - markdcl() - } - case 66: - yyDollar = yyS[yypt-4 : yypt+1] - //line go.y:667 - { - yyVAL.list = yyDollar[3].list - popdcl() - } - case 67: - yyDollar = yyS[yypt-4 : yypt+1] - //line go.y:674 - { - yyVAL.node = Nod(ORANGE, nil, yyDollar[4].node) - yyVAL.node.List = yyDollar[1].list - yyVAL.node.Etype = 0 // := flag - } - case 68: - yyDollar = yyS[yypt-4 : yypt+1] - //line go.y:680 - { - yyVAL.node = Nod(ORANGE, nil, yyDollar[4].node) - yyVAL.node.List = yyDollar[1].list - yyVAL.node.Colas = true - colasdefn(yyDollar[1].list, yyVAL.node) - } - case 69: - yyDollar = yyS[yypt-2 : yypt+1] - //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:694 - { - // init ; test ; incr - if yyDollar[5].node != nil && yyDollar[5].node.Colas { - Yyerror("cannot declare in the for-increment") - } - yyVAL.node = Nod(OFOR, nil, nil) - if yyDollar[1].node != nil { - yyVAL.node.Ninit = list1(yyDollar[1].node) - } - yyVAL.node.Ntest = yyDollar[3].node - yyVAL.node.Nincr = yyDollar[5].node - } - case 71: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:707 - { - // normal test - yyVAL.node = Nod(OFOR, nil, nil) - yyVAL.node.Ntest = yyDollar[1].node - } - case 73: - yyDollar = yyS[yypt-2 : yypt+1] - //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:723 - { - markdcl() - } - case 75: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:727 - { - yyVAL.node = yyDollar[3].node - popdcl() - } - case 76: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:734 - { - // test - yyVAL.node = Nod(OIF, nil, nil) - yyVAL.node.Ntest = yyDollar[1].node - } - case 77: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:740 - { - // init ; test - yyVAL.node = Nod(OIF, nil, nil) - if yyDollar[1].node != nil { - yyVAL.node.Ninit = list1(yyDollar[1].node) - } - yyVAL.node.Ntest = yyDollar[3].node - } - case 78: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:752 - { - markdcl() - } - case 79: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:756 - { - if yyDollar[3].node.Ntest == nil { - Yyerror("missing condition in if statement") - } - } - case 80: - yyDollar = yyS[yypt-5 : yypt+1] - //line go.y:762 - { - yyDollar[3].node.Nbody = yyDollar[5].list - } - case 81: - yyDollar = yyS[yypt-8 : yypt+1] - //line go.y:766 - { - var n *Node - var nn *NodeList - - yyVAL.node = yyDollar[3].node - n = yyDollar[3].node - popdcl() - for nn = concat(yyDollar[7].list, yyDollar[8].list); nn != nil; nn = nn.Next { - if nn.N.Op == OIF { - popdcl() - } - n.Nelse = list1(nn.N) - n = nn.N - } - } - case 82: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:784 - { - markdcl() - } - case 83: - yyDollar = yyS[yypt-5 : yypt+1] - //line go.y:788 - { - if yyDollar[4].node.Ntest == nil { - Yyerror("missing condition in if statement") - } - yyDollar[4].node.Nbody = yyDollar[5].list - yyVAL.list = list1(yyDollar[4].node) - } - case 84: - yyDollar = yyS[yypt-0 : yypt+1] - //line go.y:797 - { - yyVAL.list = nil - } - case 85: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:801 - { - yyVAL.list = concat(yyDollar[1].list, yyDollar[2].list) - } - case 86: - yyDollar = yyS[yypt-0 : yypt+1] - //line go.y:806 - { - yyVAL.list = nil - } - case 87: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:810 - { - l := &NodeList{N: yyDollar[2].node} - l.End = l - yyVAL.list = l - } - case 88: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:818 - { - markdcl() - } - case 89: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:822 - { - var n *Node - n = yyDollar[3].node.Ntest - if n != nil && n.Op != OTYPESW { - n = nil - } - typesw = Nod(OXXX, typesw, n) - } - case 90: - yyDollar = yyS[yypt-7 : yypt+1] - //line go.y:831 - { - yyVAL.node = yyDollar[3].node - yyVAL.node.Op = OSWITCH - yyVAL.node.List = yyDollar[6].list - typesw = typesw.Left - popdcl() - } - case 91: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:841 - { - typesw = Nod(OXXX, typesw, nil) - } - case 92: - yyDollar = yyS[yypt-5 : yypt+1] - //line go.y:845 - { - yyVAL.node = Nod(OSELECT, nil, nil) - yyVAL.node.Lineno = typesw.Lineno - yyVAL.node.List = yyDollar[4].list - typesw = typesw.Left - } - case 94: - yyDollar = yyS[yypt-3 : yypt+1] - //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:862 - { - yyVAL.node = Nod(OANDAND, yyDollar[1].node, yyDollar[3].node) - } - case 96: - yyDollar = yyS[yypt-3 : yypt+1] - //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:870 - { - yyVAL.node = Nod(ONE, yyDollar[1].node, yyDollar[3].node) - } - case 98: - yyDollar = yyS[yypt-3 : yypt+1] - //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:878 - { - yyVAL.node = Nod(OLE, yyDollar[1].node, yyDollar[3].node) - } - case 100: - yyDollar = yyS[yypt-3 : yypt+1] - //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:886 - { - yyVAL.node = Nod(OGT, yyDollar[1].node, yyDollar[3].node) - } - case 102: - yyDollar = yyS[yypt-3 : yypt+1] - //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:894 - { - yyVAL.node = Nod(OSUB, yyDollar[1].node, yyDollar[3].node) - } - case 104: - yyDollar = yyS[yypt-3 : yypt+1] - //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:902 - { - yyVAL.node = Nod(OXOR, yyDollar[1].node, yyDollar[3].node) - } - case 106: - yyDollar = yyS[yypt-3 : yypt+1] - //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:910 - { - yyVAL.node = Nod(ODIV, yyDollar[1].node, yyDollar[3].node) - } - case 108: - yyDollar = yyS[yypt-3 : yypt+1] - //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:918 - { - yyVAL.node = Nod(OAND, yyDollar[1].node, yyDollar[3].node) - } - case 110: - yyDollar = yyS[yypt-3 : yypt+1] - //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:926 - { - yyVAL.node = Nod(OLSH, yyDollar[1].node, yyDollar[3].node) - } - case 112: - yyDollar = yyS[yypt-3 : yypt+1] - //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:935 - { - yyVAL.node = Nod(OSEND, yyDollar[1].node, yyDollar[3].node) - } - case 115: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:942 - { - yyVAL.node = Nod(OIND, yyDollar[2].node, nil) - } - case 116: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:946 - { - if yyDollar[2].node.Op == OCOMPLIT { - // Special case for &T{...}: turn into (*T){...}. - yyVAL.node = yyDollar[2].node - yyVAL.node.Right = Nod(OIND, yyVAL.node.Right, nil) - yyVAL.node.Right.Implicit = true - } else { - yyVAL.node = Nod(OADDR, yyDollar[2].node, nil) - } - } - case 117: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:957 - { - yyVAL.node = Nod(OPLUS, yyDollar[2].node, nil) - } - case 118: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:961 - { - yyVAL.node = Nod(OMINUS, yyDollar[2].node, nil) - } - case 119: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:965 - { - yyVAL.node = Nod(ONOT, yyDollar[2].node, nil) - } - case 120: - yyDollar = yyS[yypt-2 : yypt+1] - //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:974 - { - yyVAL.node = Nod(OCOM, yyDollar[2].node, nil) - } - case 122: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:978 - { - yyVAL.node = Nod(ORECV, yyDollar[2].node, nil) - } - case 123: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:988 - { - yyVAL.node = Nod(OCALL, yyDollar[1].node, nil) - } - case 124: - yyDollar = yyS[yypt-5 : yypt+1] - //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:997 - { - yyVAL.node = Nod(OCALL, yyDollar[1].node, nil) - yyVAL.node.List = yyDollar[3].list - yyVAL.node.Isddd = true - } - case 126: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:1005 - { - yyVAL.node = nodlit(yyDollar[1].val) - } - case 128: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:1010 - { - if yyDollar[1].node.Op == OPACK { - var s *Sym - s = restrictlookup(yyDollar[3].sym.Name, yyDollar[1].node.Pkg) - yyDollar[1].node.Used = true - yyVAL.node = oldname(s) - break - } - yyVAL.node = Nod(OXDOT, yyDollar[1].node, newname(yyDollar[3].sym)) - } - case 129: - yyDollar = yyS[yypt-5 : yypt+1] - //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:1025 - { - yyVAL.node = Nod(OTYPESW, nil, yyDollar[1].node) - } - case 131: - yyDollar = yyS[yypt-4 : yypt+1] - //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: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:1037 - { - if yyDollar[5].node == nil { - Yyerror("middle index required in 3-index slice") - } - if yyDollar[7].node == nil { - Yyerror("final index required in 3-index slice") - } - yyVAL.node = Nod(OSLICE3, yyDollar[1].node, Nod(OKEY, yyDollar[3].node, Nod(OKEY, yyDollar[5].node, yyDollar[7].node))) - } - case 135: - yyDollar = yyS[yypt-5 : yypt+1] - //line go.y:1048 - { - // conversion - yyVAL.node = Nod(OCALL, yyDollar[1].node, nil) - yyVAL.node.List = list1(yyDollar[3].node) - } - case 136: - yyDollar = yyS[yypt-5 : yypt+1] - //line go.y:1054 - { - yyVAL.node = yyDollar[3].node - yyVAL.node.Right = yyDollar[1].node - yyVAL.node.List = yyDollar[4].list - fixlbrace(yyDollar[2].i) - } - case 137: - yyDollar = yyS[yypt-5 : yypt+1] - //line go.y:1061 - { - yyVAL.node = yyDollar[3].node - yyVAL.node.Right = yyDollar[1].node - yyVAL.node.List = yyDollar[4].list - } - case 138: - yyDollar = yyS[yypt-7 : yypt+1] - //line go.y:1067 - { - Yyerror("cannot parenthesize type in composite literal") - yyVAL.node = yyDollar[5].node - yyVAL.node.Right = yyDollar[2].node - yyVAL.node.List = yyDollar[6].list - } - case 140: - yyDollar = yyS[yypt-0 : yypt+1] - //line go.y:1076 - { - // composite expression. - // make node early so we get the right line number. - yyVAL.node = Nod(OCOMPLIT, nil, nil) - } - case 141: - yyDollar = yyS[yypt-3 : yypt+1] - //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:1090 - { - // These nodes do not carry line numbers. - // Since a composite literal commonly spans several lines, - // the line number on errors may be misleading. - // Introduce a wrapper node to give the correct line. - yyVAL.node = yyDollar[1].node - switch yyVAL.node.Op { - case ONAME, ONONAME, OTYPE, OPACK, OLITERAL: - yyVAL.node = Nod(OPAREN, yyVAL.node, nil) - yyVAL.node.Implicit = true - } - } - case 143: - yyDollar = yyS[yypt-4 : yypt+1] - //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:1111 - { - yyVAL.node = yyDollar[2].node - yyVAL.node.List = yyDollar[3].list - } - case 147: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:1119 - { - yyVAL.node = yyDollar[2].node - - // Need to know on lhs of := whether there are ( ). - // Don't bother with the OPAREN in other cases: - // it's just a waste of memory and time. - switch yyVAL.node.Op { - case ONAME, ONONAME, OPACK, OTYPE, OLITERAL, OTYPESW: - yyVAL.node = Nod(OPAREN, yyVAL.node, nil) - } - } - case 151: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:1140 - { - yyVAL.i = LBODY - } - case 152: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:1144 - { - yyVAL.i = '{' - } - case 153: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:1155 - { - if yyDollar[1].sym == nil { - yyVAL.node = nil - } else { - yyVAL.node = newname(yyDollar[1].sym) - } - } - case 154: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:1165 - { - yyVAL.node = dclname(yyDollar[1].sym) - } - case 155: - yyDollar = yyS[yypt-0 : yypt+1] - //line go.y:1170 - { - yyVAL.node = nil - } - case 157: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:1177 - { - yyVAL.sym = yyDollar[1].sym - // during imports, unqualified non-exported identifiers are from builtinpkg - if importpkg != nil && !exportname(yyDollar[1].sym.Name) { - yyVAL.sym = Pkglookup(yyDollar[1].sym.Name, builtinpkg) - } - } - case 159: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:1186 - { - yyVAL.sym = nil - } - case 160: - yyDollar = yyS[yypt-4 : yypt+1] - //line go.y:1192 - { - var p *Pkg - - if yyDollar[2].val.U.(string) == "" { - p = importpkg - } else { - if isbadimport(yyDollar[2].val.U.(string)) { - errorexit() - } - p = mkpkg(yyDollar[2].val.U.(string)) - } - yyVAL.sym = Pkglookup(yyDollar[4].sym.Name, p) - } - case 161: - yyDollar = yyS[yypt-4 : yypt+1] - //line go.y:1206 - { - var p *Pkg - - if yyDollar[2].val.U.(string) == "" { - p = importpkg - } else { - if isbadimport(yyDollar[2].val.U.(string)) { - errorexit() - } - p = mkpkg(yyDollar[2].val.U.(string)) - } - yyVAL.sym = Pkglookup("?", p) - } - case 162: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:1222 - { - yyVAL.node = oldname(yyDollar[1].sym) - if yyVAL.node.Pack != nil { - yyVAL.node.Pack.Used = true - } - } - case 164: - yyDollar = yyS[yypt-1 : yypt+1] - //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:1248 - { - yyVAL.node = Nod(ODDD, yyDollar[2].node, nil) - } - case 171: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:1259 - { - yyVAL.node = yyDollar[2].node - } - case 175: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:1268 - { - yyVAL.node = Nod(OIND, yyDollar[2].node, nil) - } - case 180: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:1278 - { - yyVAL.node = yyDollar[2].node - } - case 190: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:1299 - { - if yyDollar[1].node.Op == OPACK { - var s *Sym - s = restrictlookup(yyDollar[3].sym.Name, yyDollar[1].node.Pkg) - yyDollar[1].node.Used = true - yyVAL.node = oldname(s) - break - } - yyVAL.node = Nod(OXDOT, yyDollar[1].node, newname(yyDollar[3].sym)) - } - case 191: - yyDollar = yyS[yypt-4 : yypt+1] - //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: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:1321 - { - yyVAL.node = Nod(OTCHAN, yyDollar[2].node, nil) - yyVAL.node.Etype = Cboth - } - case 194: - yyDollar = yyS[yypt-3 : yypt+1] - //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:1331 - { - yyVAL.node = Nod(OTMAP, yyDollar[3].node, yyDollar[5].node) - } - case 198: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:1339 - { - yyVAL.node = Nod(OIND, yyDollar[2].node, nil) - } - case 199: - yyDollar = yyS[yypt-3 : yypt+1] - //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:1352 - { - yyVAL.node = Nod(OTSTRUCT, nil, nil) - yyVAL.node.List = yyDollar[3].list - fixlbrace(yyDollar[2].i) - } - case 201: - yyDollar = yyS[yypt-3 : yypt+1] - //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:1365 - { - yyVAL.node = Nod(OTINTER, nil, nil) - yyVAL.node.List = yyDollar[3].list - fixlbrace(yyDollar[2].i) - } - case 203: - yyDollar = yyS[yypt-3 : yypt+1] - //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:1382 - { - yyVAL.node = yyDollar[2].node - if yyVAL.node == nil { - break - } - if noescape && yyDollar[3].list != nil { - Yyerror("can only use //go:noescape with external func implementations") - } - yyVAL.node.Nbody = yyDollar[3].list - yyVAL.node.Func.Endlineno = lineno - yyVAL.node.Noescape = noescape - yyVAL.node.Func.Nosplit = nosplit - yyVAL.node.Func.Nowritebarrier = nowritebarrier - funcbody(yyVAL.node) - } - case 205: - yyDollar = yyS[yypt-5 : yypt+1] - //line go.y:1400 - { - var t *Node - - yyVAL.node = nil - yyDollar[3].list = checkarglist(yyDollar[3].list, 1) - - if yyDollar[1].sym.Name == "init" { - yyDollar[1].sym = renameinit() - if yyDollar[3].list != nil || yyDollar[5].list != nil { - Yyerror("func init must have no arguments and no return values") - } - } - if localpkg.Name == "main" && yyDollar[1].sym.Name == "main" { - if yyDollar[3].list != nil || yyDollar[5].list != nil { - Yyerror("func main must have no arguments and no return values") - } - } - - t = Nod(OTFUNC, nil, nil) - t.List = yyDollar[3].list - t.Rlist = yyDollar[5].list - - 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 - declare(yyVAL.node.Nname, PFUNC) - - funchdr(yyVAL.node) - } - case 206: - yyDollar = yyS[yypt-8 : yypt+1] - //line go.y:1431 - { - var rcvr, t *Node - - yyVAL.node = nil - yyDollar[2].list = checkarglist(yyDollar[2].list, 0) - yyDollar[6].list = checkarglist(yyDollar[6].list, 1) - - if yyDollar[2].list == nil { - Yyerror("method has no receiver") - break - } - if yyDollar[2].list.Next != nil { - Yyerror("method has multiple receivers") - break - } - rcvr = yyDollar[2].list.N - if rcvr.Op != ODCLFIELD { - Yyerror("bad receiver in method") - break - } - - t = Nod(OTFUNC, rcvr, nil) - t.List = yyDollar[6].list - t.Rlist = yyDollar[8].list - - yyVAL.node = Nod(ODCLFUNC, nil, nil) - 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.Nointerface = nointerface - declare(yyVAL.node.Nname, PFUNC) - - funchdr(yyVAL.node) - } - case 207: - yyDollar = yyS[yypt-5 : yypt+1] - //line go.y:1469 - { - var s *Sym - var t *Type - - yyVAL.node = nil - - s = yyDollar[1].sym - t = functype(nil, yyDollar[3].list, yyDollar[5].list) - - importsym(s, ONAME) - if s.Def != nil && s.Def.Op == ONAME { - if Eqtype(t, s.Def.Type) { - dclcontext = PDISCARD // since we skip funchdr below - break - } - Yyerror("inconsistent definition for func %v during import\n\t%v\n\t%v", s, s.Def.Type, t) - } - - yyVAL.node = newfuncname(s) - yyVAL.node.Type = t - declare(yyVAL.node, PFUNC) - - funchdr(yyVAL.node) - } - case 208: - yyDollar = yyS[yypt-8 : yypt+1] - //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) - - checkwidth(yyVAL.node.Type) - addmethod(yyDollar[4].sym, yyVAL.node.Type, false, nointerface) - nointerface = false - funchdr(yyVAL.node) - - // inl.C's inlnode in on a dotmeth node expects to find the inlineable body as - // (dotmeth's type).Nname.Inl, and dotmeth's type has been pulled - // out by typecheck's lookdot as this $$.ttype. So by providing - // this back link here we avoid special casing there. - yyVAL.node.Type.Nname = yyVAL.node - } - case 209: - yyDollar = yyS[yypt-5 : yypt+1] - //line go.y:1512 - { - yyDollar[3].list = checkarglist(yyDollar[3].list, 1) - yyVAL.node = Nod(OTFUNC, nil, nil) - yyVAL.node.List = yyDollar[3].list - yyVAL.node.Rlist = yyDollar[5].list - } - case 210: - yyDollar = yyS[yypt-0 : yypt+1] - //line go.y:1520 - { - yyVAL.list = nil - } - case 211: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:1524 - { - yyVAL.list = yyDollar[2].list - if yyVAL.list == nil { - yyVAL.list = list1(Nod(OEMPTY, nil, nil)) - } - } - case 212: - yyDollar = yyS[yypt-0 : yypt+1] - //line go.y:1533 - { - yyVAL.list = nil - } - case 213: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:1537 - { - yyVAL.list = list1(Nod(ODCLFIELD, nil, yyDollar[1].node)) - } - case 214: - yyDollar = yyS[yypt-3 : yypt+1] - //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:1548 - { - closurehdr(yyDollar[1].node) - } - case 216: - yyDollar = yyS[yypt-4 : yypt+1] - //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:1559 - { - yyVAL.node = closurebody(nil) - } - case 218: - yyDollar = yyS[yypt-0 : yypt+1] - //line go.y:1570 - { - yyVAL.list = nil - } - case 219: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:1574 - { - yyVAL.list = concat(yyDollar[1].list, yyDollar[2].list) - if nsyntaxerrors == 0 { - testdclstack() - } - nointerface = false - noescape = false - nosplit = false - nowritebarrier = false - } - case 221: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:1588 - { - yyVAL.list = concat(yyDollar[1].list, yyDollar[3].list) - } - case 223: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:1595 - { - yyVAL.list = concat(yyDollar[1].list, yyDollar[3].list) - } - case 224: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:1601 - { - yyVAL.list = list1(yyDollar[1].node) - } - case 225: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:1605 - { - yyVAL.list = list(yyDollar[1].list, yyDollar[3].node) - } - case 227: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:1612 - { - yyVAL.list = concat(yyDollar[1].list, yyDollar[3].list) - } - case 228: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:1618 - { - yyVAL.list = list1(yyDollar[1].node) - } - case 229: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:1622 - { - yyVAL.list = list(yyDollar[1].list, yyDollar[3].node) - } - case 230: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:1628 - { - var l *NodeList - - var n *Node - l = yyDollar[1].list - if l == nil { - // ? symbol, during import (list1(nil) == nil) - n = yyDollar[2].node - if n.Op == OIND { - n = n.Left - } - n = embedded(n.Sym, importpkg) - n.Right = yyDollar[2].node - n.Val = yyDollar[3].val - yyVAL.list = list1(n) - break - } - - for l = yyDollar[1].list; l != nil; l = l.Next { - l.N = Nod(ODCLFIELD, l.N, yyDollar[2].node) - l.N.Val = yyDollar[3].val - } - } - case 231: - yyDollar = yyS[yypt-2 : yypt+1] - //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:1657 - { - yyDollar[2].node.Val = yyDollar[4].val - yyVAL.list = list1(yyDollar[2].node) - Yyerror("cannot parenthesize embedded type") - } - case 233: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:1663 - { - yyDollar[2].node.Right = Nod(OIND, yyDollar[2].node.Right, nil) - yyDollar[2].node.Val = yyDollar[3].val - yyVAL.list = list1(yyDollar[2].node) - } - case 234: - yyDollar = yyS[yypt-5 : yypt+1] - //line go.y:1669 - { - yyDollar[3].node.Right = Nod(OIND, yyDollar[3].node.Right, nil) - yyDollar[3].node.Val = yyDollar[5].val - yyVAL.list = list1(yyDollar[3].node) - Yyerror("cannot parenthesize embedded type") - } - case 235: - yyDollar = yyS[yypt-5 : yypt+1] - //line go.y:1676 - { - yyDollar[3].node.Right = Nod(OIND, yyDollar[3].node.Right, nil) - yyDollar[3].node.Val = yyDollar[5].val - yyVAL.list = list1(yyDollar[3].node) - Yyerror("cannot parenthesize embedded type") - } - case 236: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:1685 - { - var n *Node - - yyVAL.sym = yyDollar[1].sym - n = oldname(yyDollar[1].sym) - if n.Pack != nil { - n.Pack.Used = true - } - } - case 237: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:1695 - { - var pkg *Pkg - - if yyDollar[1].sym.Def == nil || yyDollar[1].sym.Def.Op != OPACK { - Yyerror("%v is not a package", yyDollar[1].sym) - pkg = localpkg - } else { - yyDollar[1].sym.Def.Used = true - pkg = yyDollar[1].sym.Def.Pkg - } - yyVAL.sym = restrictlookup(yyDollar[3].sym.Name, pkg) - } - case 238: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:1710 - { - yyVAL.node = embedded(yyDollar[1].sym, localpkg) - } - case 239: - yyDollar = yyS[yypt-2 : yypt+1] - //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:1721 - { - yyVAL.node = Nod(ODCLFIELD, nil, oldname(yyDollar[1].sym)) - } - case 241: - yyDollar = yyS[yypt-3 : yypt+1] - //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:1732 - { - // without func keyword - yyDollar[2].list = checkarglist(yyDollar[2].list, 1) - yyVAL.node = Nod(OTFUNC, fakethis(), nil) - yyVAL.node.List = yyDollar[2].list - yyVAL.node.Rlist = yyDollar[4].list - } - case 244: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:1746 - { - yyVAL.node = Nod(ONONAME, nil, nil) - yyVAL.node.Sym = yyDollar[1].sym - yyVAL.node = Nod(OKEY, yyVAL.node, yyDollar[2].node) - } - case 245: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:1752 - { - yyVAL.node = Nod(ONONAME, nil, nil) - yyVAL.node.Sym = yyDollar[1].sym - yyVAL.node = Nod(OKEY, yyVAL.node, yyDollar[2].node) - } - case 247: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:1761 - { - yyVAL.list = list1(yyDollar[1].node) - } - case 248: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:1765 - { - yyVAL.list = list(yyDollar[1].list, yyDollar[3].node) - } - case 249: - yyDollar = yyS[yypt-0 : yypt+1] - //line go.y:1770 - { - yyVAL.list = nil - } - case 250: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:1774 - { - yyVAL.list = yyDollar[1].list - } - case 251: - yyDollar = yyS[yypt-0 : yypt+1] - //line go.y:1782 - { - yyVAL.node = nil - } - case 253: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:1787 - { - yyVAL.node = liststmt(yyDollar[1].list) - } - case 255: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:1792 - { - yyVAL.node = nil - } - case 261: - yyDollar = yyS[yypt-2 : yypt+1] - //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:1808 - { - var l *NodeList - - yyDollar[1].node.Defn = yyDollar[4].node - l = list1(yyDollar[1].node) - if yyDollar[4].node != nil { - l = list(l, yyDollar[4].node) - } - yyVAL.node = liststmt(l) - } - case 263: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:1819 - { - // will be converted to OFALL - yyVAL.node = Nod(OXFALL, nil, nil) - yyVAL.node.Xoffset = int64(block) - } - case 264: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:1825 - { - yyVAL.node = Nod(OBREAK, yyDollar[2].node, nil) - } - case 265: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:1829 - { - yyVAL.node = Nod(OCONTINUE, yyDollar[2].node, nil) - } - case 266: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:1833 - { - yyVAL.node = Nod(OPROC, yyDollar[2].node, nil) - } - case 267: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:1837 - { - yyVAL.node = Nod(ODEFER, yyDollar[2].node, nil) - } - case 268: - yyDollar = yyS[yypt-2 : yypt+1] - //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:1846 - { - yyVAL.node = Nod(ORETURN, nil, nil) - yyVAL.node.List = yyDollar[2].list - if yyVAL.node.List == nil && Curfn != nil { - var l *NodeList - - for l = Curfn.Func.Dcl; l != nil; l = l.Next { - if l.N.Class == PPARAM { - continue - } - if l.N.Class != PPARAMOUT { - break - } - if l.N.Sym.Def != l.N { - Yyerror("%s is shadowed during return", l.N.Sym.Name) - } - } - } - } - case 270: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:1868 - { - yyVAL.list = nil - if yyDollar[1].node != nil { - yyVAL.list = list1(yyDollar[1].node) - } - } - case 271: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:1875 - { - yyVAL.list = yyDollar[1].list - if yyDollar[3].node != nil { - yyVAL.list = list(yyVAL.list, yyDollar[3].node) - } - } - case 272: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:1884 - { - yyVAL.list = list1(yyDollar[1].node) - } - case 273: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:1888 - { - yyVAL.list = list(yyDollar[1].list, yyDollar[3].node) - } - case 274: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:1894 - { - yyVAL.list = list1(yyDollar[1].node) - } - case 275: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:1898 - { - yyVAL.list = list(yyDollar[1].list, yyDollar[3].node) - } - case 276: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:1904 - { - yyVAL.list = list1(yyDollar[1].node) - } - case 277: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:1908 - { - yyVAL.list = list(yyDollar[1].list, yyDollar[3].node) - } - case 278: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:1914 - { - yyVAL.list = list1(yyDollar[1].node) - } - case 279: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:1918 - { - yyVAL.list = list(yyDollar[1].list, yyDollar[3].node) - } - case 280: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:1927 - { - yyVAL.list = list1(yyDollar[1].node) - } - case 281: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:1931 - { - yyVAL.list = list1(yyDollar[1].node) - } - case 282: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:1935 - { - yyVAL.list = list(yyDollar[1].list, yyDollar[3].node) - } - case 283: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:1939 - { - yyVAL.list = list(yyDollar[1].list, yyDollar[3].node) - } - case 284: - yyDollar = yyS[yypt-0 : yypt+1] - //line go.y:1944 - { - yyVAL.list = nil - } - case 285: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:1948 - { - yyVAL.list = yyDollar[1].list - } - case 290: - yyDollar = yyS[yypt-0 : yypt+1] - //line go.y:1962 - { - yyVAL.node = nil - } - case 292: - yyDollar = yyS[yypt-0 : yypt+1] - //line go.y:1968 - { - yyVAL.list = nil - } - case 294: - yyDollar = yyS[yypt-0 : yypt+1] - //line go.y:1974 - { - yyVAL.node = nil - } - case 296: - yyDollar = yyS[yypt-0 : yypt+1] - //line go.y:1980 - { - yyVAL.list = nil - } - case 298: - yyDollar = yyS[yypt-0 : yypt+1] - //line go.y:1986 - { - yyVAL.list = nil - } - case 300: - yyDollar = yyS[yypt-0 : yypt+1] - //line go.y:1992 - { - yyVAL.list = nil - } - case 302: - yyDollar = yyS[yypt-0 : yypt+1] - //line go.y:1998 - { - yyVAL.val.Ctype = CTxxx - } - case 304: - yyDollar = yyS[yypt-4 : yypt+1] - //line go.y:2008 - { - importimport(yyDollar[2].sym, yyDollar[3].val.U.(string)) - } - case 305: - yyDollar = yyS[yypt-4 : yypt+1] - //line go.y:2012 - { - importvar(yyDollar[2].sym, yyDollar[3].typ) - } - case 306: - yyDollar = yyS[yypt-5 : yypt+1] - //line go.y:2016 - { - importconst(yyDollar[2].sym, Types[TIDEAL], yyDollar[4].node) - } - case 307: - yyDollar = yyS[yypt-6 : yypt+1] - //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:2024 - { - importtype(yyDollar[2].typ, yyDollar[3].typ) - } - case 309: - yyDollar = yyS[yypt-4 : yypt+1] - //line go.y:2028 - { - if yyDollar[2].node == nil { - dclcontext = PEXTERN // since we skip the funcbody below - break - } - - yyDollar[2].node.Func.Inl = yyDollar[3].list - - funcbody(yyDollar[2].node) - importlist = list(importlist, yyDollar[2].node) - - if Debug['E'] > 0 { - fmt.Printf("import [%q] func %v \n", importpkg.Path, yyDollar[2].node) - if Debug['m'] > 2 && yyDollar[2].node.Func.Inl != nil { - fmt.Printf("inl body:%v\n", yyDollar[2].node.Func.Inl) - } - } - } - case 310: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:2049 - { - yyVAL.sym = yyDollar[1].sym - structpkg = yyVAL.sym.Pkg - } - case 311: - yyDollar = yyS[yypt-1 : yypt+1] - //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:2076 - { - yyVAL.typ = pkgtype(yyDollar[1].sym) - } - case 318: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:2080 - { - // predefined name like uint8 - yyDollar[1].sym = Pkglookup(yyDollar[1].sym.Name, builtinpkg) - if yyDollar[1].sym.Def == nil || yyDollar[1].sym.Def.Op != OTYPE { - Yyerror("%s is not a type", yyDollar[1].sym.Name) - yyVAL.typ = nil - } else { - yyVAL.typ = yyDollar[1].sym.Def.Type - } - } - case 319: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:2091 - { - yyVAL.typ = aindex(nil, yyDollar[3].typ) - } - case 320: - yyDollar = yyS[yypt-4 : yypt+1] - //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:2099 - { - yyVAL.typ = maptype(yyDollar[3].typ, yyDollar[5].typ) - } - case 322: - yyDollar = yyS[yypt-4 : yypt+1] - //line go.y:2103 - { - yyVAL.typ = tostruct(yyDollar[3].list) - } - case 323: - yyDollar = yyS[yypt-4 : yypt+1] - //line go.y:2107 - { - yyVAL.typ = tointerface(yyDollar[3].list) - } - case 324: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:2111 - { - yyVAL.typ = Ptrto(yyDollar[2].typ) - } - case 325: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:2115 - { - yyVAL.typ = typ(TCHAN) - yyVAL.typ.Type = yyDollar[2].typ - yyVAL.typ.Chan = Cboth - } - case 326: - yyDollar = yyS[yypt-4 : yypt+1] - //line go.y:2121 - { - yyVAL.typ = typ(TCHAN) - yyVAL.typ.Type = yyDollar[3].typ - yyVAL.typ.Chan = Cboth - } - case 327: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:2127 - { - yyVAL.typ = typ(TCHAN) - yyVAL.typ.Type = yyDollar[3].typ - yyVAL.typ.Chan = Csend - } - case 328: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:2135 - { - yyVAL.typ = typ(TCHAN) - yyVAL.typ.Type = yyDollar[3].typ - yyVAL.typ.Chan = Crecv - } - case 329: - yyDollar = yyS[yypt-5 : yypt+1] - //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:2149 - { - yyVAL.node = Nod(ODCLFIELD, nil, typenod(yyDollar[2].typ)) - if yyDollar[1].sym != nil { - yyVAL.node.Left = newname(yyDollar[1].sym) - } - yyVAL.node.Val = yyDollar[3].val - } - case 331: - yyDollar = yyS[yypt-4 : yypt+1] - //line go.y:2157 - { - var t *Type - - t = typ(TARRAY) - t.Bound = -1 - t.Type = yyDollar[3].typ - - yyVAL.node = Nod(ODCLFIELD, nil, typenod(t)) - if yyDollar[1].sym != nil { - yyVAL.node.Left = newname(yyDollar[1].sym) - } - yyVAL.node.Isddd = true - yyVAL.node.Val = yyDollar[4].val - } - case 332: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:2174 - { - var s *Sym - var p *Pkg - - if yyDollar[1].sym != nil && yyDollar[1].sym.Name != "?" { - yyVAL.node = Nod(ODCLFIELD, newname(yyDollar[1].sym), typenod(yyDollar[2].typ)) - yyVAL.node.Val = yyDollar[3].val - } else { - s = yyDollar[2].typ.Sym - if s == nil && Isptr[yyDollar[2].typ.Etype] { - s = yyDollar[2].typ.Type.Sym - } - p = importpkg - if yyDollar[1].sym != nil { - p = yyDollar[1].sym.Pkg - } - yyVAL.node = embedded(s, p) - yyVAL.node.Right = typenod(yyDollar[2].typ) - yyVAL.node.Val = yyDollar[3].val - } - } - case 333: - yyDollar = yyS[yypt-5 : yypt+1] - //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:2202 - { - yyVAL.node = Nod(ODCLFIELD, nil, typenod(yyDollar[1].typ)) - } - case 335: - yyDollar = yyS[yypt-0 : yypt+1] - //line go.y:2207 - { - yyVAL.list = nil - } - case 337: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:2214 - { - yyVAL.list = yyDollar[2].list - } - case 338: - yyDollar = yyS[yypt-1 : yypt+1] - //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:2228 - { - yyVAL.node = nodlit(yyDollar[1].val) - } - case 340: - yyDollar = yyS[yypt-2 : yypt+1] - //line go.y:2232 - { - yyVAL.node = nodlit(yyDollar[2].val) - switch yyVAL.node.Val.Ctype { - case CTINT, CTRUNE: - mpnegfix(yyVAL.node.Val.U.(*Mpint)) - break - case CTFLT: - mpnegflt(yyVAL.node.Val.U.(*Mpflt)) - break - case CTCPLX: - mpnegflt(&yyVAL.node.Val.U.(*Mpcplx).Real) - mpnegflt(&yyVAL.node.Val.U.(*Mpcplx).Imag) - break - default: - Yyerror("bad negated constant") - } - } - case 341: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:2250 - { - yyVAL.node = oldname(Pkglookup(yyDollar[1].sym.Name, builtinpkg)) - if yyVAL.node.Op != OLITERAL { - Yyerror("bad constant %v", yyVAL.node.Sym) - } - } - case 343: - yyDollar = yyS[yypt-5 : yypt+1] - //line go.y:2260 - { - if yyDollar[2].node.Val.Ctype == CTRUNE && yyDollar[4].node.Val.Ctype == CTINT { - yyVAL.node = yyDollar[2].node - mpaddfixfix(yyDollar[2].node.Val.U.(*Mpint), yyDollar[4].node.Val.U.(*Mpint), 0) - break - } - 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: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:2276 - { - yyVAL.list = list1(yyDollar[1].node) - } - case 347: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:2280 - { - yyVAL.list = list(yyDollar[1].list, yyDollar[3].node) - } - case 348: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:2286 - { - yyVAL.list = list1(yyDollar[1].node) - } - case 349: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:2290 - { - yyVAL.list = list(yyDollar[1].list, yyDollar[3].node) - } - case 350: - yyDollar = yyS[yypt-1 : yypt+1] - //line go.y:2296 - { - yyVAL.list = list1(yyDollar[1].node) - } - case 351: - yyDollar = yyS[yypt-3 : yypt+1] - //line go.y:2300 - { - yyVAL.list = list(yyDollar[1].list, yyDollar[3].node) - } - } - goto yystack /* stack new state and value */ -} diff --git a/src/cmd/internal/gc/y.output b/src/cmd/internal/gc/y.output deleted file mode 100644 index 2821702aea..0000000000 --- a/src/cmd/internal/gc/y.output +++ /dev/null @@ -1,10411 +0,0 @@ - -state 0 - $accept: .file $end - $$4: . (4) - - . reduce 4 (src line 210) - - file goto 1 - loadsys goto 2 - $$4 goto 3 - -state 1 - $accept: file.$end - - $end accept - . error - - -state 2 - file: loadsys.package imports xdcl_list - package: . (2) - - LPACKAGE shift 5 - . reduce 2 (src line 193) - - package goto 4 - -state 3 - loadsys: $$4.import_package import_there - - LPACKAGE shift 7 - . error - - import_package goto 6 - -state 4 - file: loadsys package.imports xdcl_list - imports: . (6) - - . reduce 6 (src line 227) - - imports goto 8 - -state 5 - package: LPACKAGE.sym ';' - - LNAME shift 10 - '?' shift 12 - '@' shift 13 - . error - - sym goto 9 - hidden_importsym goto 11 - -state 6 - loadsys: $$4 import_package.import_there - $$21: . (21) - - . reduce 21 (src line 334) - - import_there goto 14 - $$21 goto 15 - -state 7 - import_package: LPACKAGE.LNAME import_safety ';' - - LNAME shift 16 - . error - - -state 8 - file: loadsys package imports.xdcl_list - imports: imports.import ';' - xdcl_list: . (218) - - LIMPORT shift 19 - . reduce 218 (src line 1569) - - xdcl_list goto 17 - import goto 18 - -state 9 - package: LPACKAGE sym.';' - - ';' shift 20 - . error - - -state 10 - sym: LNAME. (157) - - . reduce 157 (src line 1175) - - -state 11 - sym: hidden_importsym. (158) - - . reduce 158 (src line 1184) - - -state 12 - sym: '?'. (159) - - . reduce 159 (src line 1185) - - -state 13 - hidden_importsym: '@'.LLITERAL '.' LNAME - hidden_importsym: '@'.LLITERAL '.' '?' - - LLITERAL shift 21 - . error - - -state 14 - loadsys: $$4 import_package import_there. (5) - - . reduce 5 (src line 221) - - -state 15 - import_there: $$21.hidden_import_list '$' '$' - hidden_import_list: . (344) - - . reduce 344 (src line 2271) - - hidden_import_list goto 22 - -state 16 - import_package: LPACKAGE LNAME.import_safety ';' - import_safety: . (19) - - LNAME shift 24 - . reduce 19 (src line 326) - - import_safety goto 23 - -state 17 - file: loadsys package imports xdcl_list. (1) - xdcl_list: xdcl_list.xdcl ';' - xdcl: . (23) - - $end reduce 1 (src line 184) - error shift 29 - LLITERAL shift 68 - LBREAK shift 41 - LCHAN shift 78 - LCONST shift 47 - LCONTINUE shift 42 - LDEFER shift 44 - LFALL shift 40 - LFOR shift 50 - LFUNC shift 33 - LGO shift 43 - LGOTO shift 45 - LIF shift 53 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LRETURN shift 46 - LSELECT shift 52 - LSTRUCT shift 82 - LSWITCH shift 51 - LTYPE shift 32 - LVAR shift 30 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - ';' reduce 23 (src line 347) - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 56 - expr goto 48 - fnliteral goto 73 - for_stmt goto 35 - if_stmt goto 38 - non_dcl_stmt goto 28 - labelname goto 39 - name goto 69 - new_name goto 54 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - select_stmt goto 37 - simple_stmt goto 34 - switch_stmt goto 36 - uexpr goto 55 - xfndcl goto 27 - xdcl goto 25 - expr_list goto 49 - common_dcl goto 26 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - lconst goto 31 - fnlitdcl goto 76 - -state 18 - imports: imports import.';' - - ';' shift 84 - . error - - -state 19 - import: LIMPORT.import_stmt - import: LIMPORT.'(' import_stmt_list osemi ')' - import: LIMPORT.'(' ')' - - LLITERAL shift 88 - LNAME shift 10 - '(' shift 86 - '.' shift 90 - '?' shift 12 - '@' shift 13 - . error - - import_here goto 87 - sym goto 89 - hidden_importsym goto 11 - import_stmt goto 85 - -state 20 - package: LPACKAGE sym ';'. (3) - - . reduce 3 (src line 200) - - -state 21 - hidden_importsym: '@' LLITERAL.'.' LNAME - hidden_importsym: '@' LLITERAL.'.' '?' - - '.' shift 91 - . error - - -state 22 - import_there: $$21 hidden_import_list.'$' '$' - hidden_import_list: hidden_import_list.hidden_import - - LCONST shift 96 - LFUNC shift 98 - LIMPORT shift 94 - LTYPE shift 97 - LVAR shift 95 - '$' shift 92 - . error - - hidden_import goto 93 - -state 23 - import_package: LPACKAGE LNAME import_safety.';' - - ';' shift 99 - . error - - -state 24 - import_safety: LNAME. (20) - - . reduce 20 (src line 327) - - -state 25 - xdcl_list: xdcl_list xdcl.';' - - ';' shift 100 - . error - - -state 26 - xdcl: common_dcl. (24) - - . reduce 24 (src line 352) - - -state 27 - xdcl: xfndcl. (25) - - . reduce 25 (src line 353) - - -state 28 - xdcl: non_dcl_stmt. (26) - - . reduce 26 (src line 357) - - -state 29 - xdcl: error. (27) - - . reduce 27 (src line 362) - - -state 30 - common_dcl: LVAR.vardcl - common_dcl: LVAR.'(' vardcl_list osemi ')' - common_dcl: LVAR.'(' ')' - - LNAME shift 10 - '(' shift 102 - '?' shift 12 - '@' shift 13 - . error - - sym goto 105 - dcl_name goto 104 - dcl_name_list goto 103 - vardcl goto 101 - hidden_importsym goto 11 - -state 31 - common_dcl: lconst.constdcl - common_dcl: lconst.'(' constdcl osemi ')' - common_dcl: lconst.'(' constdcl ';' constdcl_list osemi ')' - common_dcl: lconst.'(' ')' - - LNAME shift 10 - '(' shift 107 - '?' shift 12 - '@' shift 13 - . error - - sym goto 105 - dcl_name goto 104 - dcl_name_list goto 108 - constdcl goto 106 - hidden_importsym goto 11 - -state 32 - common_dcl: LTYPE.typedcl - common_dcl: LTYPE.'(' typedcl_list osemi ')' - common_dcl: LTYPE.'(' ')' - - LNAME shift 10 - '(' shift 110 - '?' shift 12 - '@' shift 13 - . error - - sym goto 112 - typedclname goto 111 - typedcl goto 109 - hidden_importsym goto 11 - -state 33 - xfndcl: LFUNC.fndcl fnbody - fntype: LFUNC.'(' oarg_type_list_ocomma ')' fnres - - LNAME shift 10 - '(' shift 114 - '?' shift 12 - '@' shift 13 - . error - - sym goto 115 - fndcl goto 113 - hidden_importsym goto 11 - -state 34 - non_dcl_stmt: simple_stmt. (256) - - . reduce 256 (src line 1796) - - -state 35 - non_dcl_stmt: for_stmt. (257) - - . reduce 257 (src line 1798) - - -state 36 - non_dcl_stmt: switch_stmt. (258) - - . reduce 258 (src line 1799) - - -state 37 - non_dcl_stmt: select_stmt. (259) - - . reduce 259 (src line 1800) - - -state 38 - non_dcl_stmt: if_stmt. (260) - - . reduce 260 (src line 1801) - - -state 39 - non_dcl_stmt: labelname.':' $$261 stmt - - ':' shift 116 - . error - - -state 40 - non_dcl_stmt: LFALL. (263) - - . reduce 263 (src line 1818) - - -state 41 - non_dcl_stmt: LBREAK.onew_name - onew_name: . (155) - - LNAME shift 10 - '?' shift 12 - '@' shift 13 - . reduce 155 (src line 1169) - - sym goto 119 - new_name goto 118 - onew_name goto 117 - hidden_importsym goto 11 - -state 42 - non_dcl_stmt: LCONTINUE.onew_name - onew_name: . (155) - - LNAME shift 10 - '?' shift 12 - '@' shift 13 - . reduce 155 (src line 1169) - - sym goto 119 - new_name goto 118 - onew_name goto 120 - hidden_importsym goto 11 - -state 43 - non_dcl_stmt: LGO.pseudocall - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - '(' shift 67 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - fnliteral goto 73 - name goto 69 - pexpr goto 122 - pexpr_no_paren goto 66 - pseudocall goto 121 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 44 - non_dcl_stmt: LDEFER.pseudocall - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - '(' shift 67 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - fnliteral goto 73 - name goto 69 - pexpr goto 122 - pexpr_no_paren goto 66 - pseudocall goto 125 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 45 - non_dcl_stmt: LGOTO.new_name - - LNAME shift 10 - '?' shift 12 - '@' shift 13 - . error - - sym goto 119 - new_name goto 126 - hidden_importsym goto 11 - -state 46 - non_dcl_stmt: LRETURN.oexpr_list - oexpr_list: . (292) - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 292 (src line 1967) - - sym goto 123 - expr goto 129 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - expr_list goto 128 - oexpr_list goto 127 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 47 - lconst: LCONST. (38) - - . reduce 38 (src line 416) - - -state 48 - simple_stmt: expr. (49) - simple_stmt: expr.LASOP expr - simple_stmt: expr.LINC - simple_stmt: expr.LDEC - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - expr_list: expr. (276) - - LASOP shift 130 - LCOLAS reduce 276 (src line 1902) - LANDAND shift 134 - LANDNOT shift 149 - LCOMM shift 152 - LDEC shift 132 - LEQ shift 135 - LGE shift 139 - LGT shift 140 - LINC shift 131 - LLE shift 138 - LLSH shift 150 - LLT shift 137 - LNE shift 136 - LOROR shift 133 - LRSH shift 151 - '+' shift 141 - '-' shift 142 - '|' shift 143 - '^' shift 144 - '*' shift 145 - '/' shift 146 - '%' shift 147 - '&' shift 148 - '=' reduce 276 (src line 1902) - ',' reduce 276 (src line 1902) - . reduce 49 (src line 472) - - -state 49 - simple_stmt: expr_list.'=' expr_list - simple_stmt: expr_list.LCOLAS expr_list - expr_list: expr_list.',' expr - - LCOLAS shift 154 - '=' shift 153 - ',' shift 155 - . error - - -state 50 - for_stmt: LFOR.$$74 for_body - $$74: . (74) - - . reduce 74 (src line 721) - - $$74 goto 156 - -state 51 - switch_stmt: LSWITCH.$$88 if_header $$89 LBODY caseblock_list '}' - $$88: . (88) - - . reduce 88 (src line 816) - - $$88 goto 157 - -state 52 - select_stmt: LSELECT.$$91 LBODY caseblock_list '}' - $$91: . (91) - - . reduce 91 (src line 839) - - $$91 goto 158 - -state 53 - if_stmt: LIF.$$78 if_header $$79 loop_body $$80 elseif_list else - $$78: . (78) - - . reduce 78 (src line 750) - - $$78 goto 159 - -state 54 - labelname: new_name. (163) - - . reduce 163 (src line 1229) - - -state 55 - expr: uexpr. (93) - - . reduce 93 (src line 855) - - -state 56 - new_name: sym. (153) - name: sym. (162) - - ':' reduce 153 (src line 1153) - . reduce 162 (src line 1220) - - -state 57 - uexpr: pexpr. (114) - pseudocall: pexpr.'(' ')' - pseudocall: pexpr.'(' expr_or_type_list ocomma ')' - pseudocall: pexpr.'(' expr_or_type_list LDDD ocomma ')' - pexpr_no_paren: pexpr.'.' sym - pexpr_no_paren: pexpr.'.' '(' expr_or_type ')' - pexpr_no_paren: pexpr.'.' '(' LTYPE ')' - pexpr_no_paren: pexpr.'[' expr ']' - pexpr_no_paren: pexpr.'[' oexpr ':' oexpr ']' - pexpr_no_paren: pexpr.'[' oexpr ':' oexpr ':' oexpr ']' - - '(' shift 160 - '.' shift 161 - '[' shift 162 - . reduce 114 (src line 939) - - -state 58 - uexpr: '*'.uexpr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 163 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 59 - uexpr: '&'.uexpr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 164 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 60 - uexpr: '+'.uexpr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 165 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 61 - uexpr: '-'.uexpr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 166 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 62 - uexpr: '!'.uexpr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 167 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 63 - uexpr: '~'.uexpr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 168 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 64 - uexpr: '^'.uexpr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 169 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 65 - uexpr: LCOMM.uexpr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 170 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 66 - pexpr_no_paren: pexpr_no_paren.'{' start_complit braced_keyval_list '}' - pexpr: pexpr_no_paren. (146) - - '{' shift 171 - . reduce 146 (src line 1116) - - -state 67 - pexpr_no_paren: '('.expr_or_type ')' '{' start_complit braced_keyval_list '}' - pexpr: '('.expr_or_type ')' - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 179 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 178 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 173 - expr_or_type goto 172 - fnliteral goto 73 - name goto 69 - non_expr_type goto 174 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - recvchantype goto 175 - othertype goto 177 - fntype goto 176 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 68 - pexpr_no_paren: LLITERAL. (126) - - . reduce 126 (src line 1003) - - -state 69 - pexpr_no_paren: name. (127) - - . reduce 127 (src line 1008) - - -state 70 - pexpr_no_paren: pseudocall. (134) - - . reduce 134 (src line 1046) - - -state 71 - pexpr_no_paren: convtype.'(' expr ocomma ')' - - '(' shift 180 - . error - - -state 72 - pexpr_no_paren: comptype.lbrace start_complit braced_keyval_list '}' - - LBODY shift 182 - '{' shift 183 - . error - - lbrace goto 181 - -state 73 - pexpr_no_paren: fnliteral. (139) - - . reduce 139 (src line 1073) - - -state 74 - convtype: fntype. (181) - fnlitdcl: fntype. (215) - - '(' reduce 181 (src line 1282) - . reduce 215 (src line 1546) - - -state 75 - convtype: othertype. (182) - comptype: othertype. (183) - - '(' reduce 182 (src line 1284) - . reduce 183 (src line 1286) - - -state 76 - fnliteral: fnlitdcl.lbrace stmt_list '}' - fnliteral: fnlitdcl.error - - error shift 185 - LBODY shift 182 - '{' shift 183 - . error - - lbrace goto 184 - -state 77 - othertype: '['.oexpr ']' ntype - othertype: '['.LDDD ']' ntype - oexpr: . (290) - - LLITERAL shift 68 - LCHAN shift 78 - LDDD shift 187 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 290 (src line 1961) - - sym goto 123 - expr goto 188 - fnliteral goto 73 - name goto 69 - oexpr goto 186 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 78 - othertype: LCHAN.non_recvchantype - othertype: LCHAN.LCOMM ntype - - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 190 - '*' shift 196 - '(' shift 195 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - dotname goto 194 - name goto 197 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 193 - non_recvchantype goto 189 - othertype goto 192 - fntype goto 191 - hidden_importsym goto 11 - -state 79 - othertype: LMAP.'[' ntype ']' ntype - - '[' shift 198 - . error - - -state 80 - othertype: structtype. (196) - - . reduce 196 (src line 1334) - - -state 81 - othertype: interfacetype. (197) - - . reduce 197 (src line 1335) - - -state 82 - structtype: LSTRUCT.lbrace structdcl_list osemi '}' - structtype: LSTRUCT.lbrace '}' - - LBODY shift 182 - '{' shift 183 - . error - - lbrace goto 199 - -state 83 - interfacetype: LINTERFACE.lbrace interfacedcl_list osemi '}' - interfacetype: LINTERFACE.lbrace '}' - - LBODY shift 182 - '{' shift 183 - . error - - lbrace goto 200 - -state 84 - imports: imports import ';'. (7) - - . reduce 7 (src line 228) - - -state 85 - import: LIMPORT import_stmt. (8) - - . reduce 8 (src line 230) - - -state 86 - import: LIMPORT '('.import_stmt_list osemi ')' - import: LIMPORT '('.')' - - LLITERAL shift 88 - LNAME shift 10 - ')' shift 202 - '.' shift 90 - '?' shift 12 - '@' shift 13 - . error - - import_here goto 87 - sym goto 89 - hidden_importsym goto 11 - import_stmt goto 203 - import_stmt_list goto 201 - -state 87 - import_stmt: import_here.import_package import_there - import_stmt: import_here.import_there - $$21: . (21) - - LPACKAGE shift 7 - . reduce 21 (src line 334) - - import_package goto 204 - import_there goto 205 - $$21 goto 15 - -state 88 - import_here: LLITERAL. (15) - - . reduce 15 (src line 286) - - -state 89 - import_here: sym.LLITERAL - - LLITERAL shift 206 - . error - - -state 90 - import_here: '.'.LLITERAL - - LLITERAL shift 207 - . error - - -state 91 - hidden_importsym: '@' LLITERAL '.'.LNAME - hidden_importsym: '@' LLITERAL '.'.'?' - - LNAME shift 208 - '?' shift 209 - . error - - -state 92 - import_there: $$21 hidden_import_list '$'.'$' - - '$' shift 210 - . error - - -state 93 - hidden_import_list: hidden_import_list hidden_import. (345) - - . reduce 345 (src line 2272) - - -state 94 - hidden_import: LIMPORT.LNAME LLITERAL ';' - - LNAME shift 211 - . error - - -state 95 - hidden_import: LVAR.hidden_pkg_importsym hidden_type ';' - - '@' shift 13 - . error - - hidden_importsym goto 213 - hidden_pkg_importsym goto 212 - -state 96 - hidden_import: LCONST.hidden_pkg_importsym '=' hidden_constant ';' - hidden_import: LCONST.hidden_pkg_importsym hidden_type '=' hidden_constant ';' - - '@' shift 13 - . error - - hidden_importsym goto 213 - hidden_pkg_importsym goto 214 - -state 97 - hidden_import: LTYPE.hidden_pkgtype hidden_type ';' - - '@' shift 13 - . error - - hidden_importsym goto 213 - hidden_pkg_importsym goto 216 - hidden_pkgtype goto 215 - -state 98 - hidden_import: LFUNC.hidden_fndcl fnbody ';' - - '(' shift 219 - '@' shift 13 - . error - - hidden_fndcl goto 217 - hidden_importsym goto 213 - hidden_pkg_importsym goto 218 - -state 99 - import_package: LPACKAGE LNAME import_safety ';'. (18) - - . reduce 18 (src line 309) - - -state 100 - xdcl_list: xdcl_list xdcl ';'. (219) - - . reduce 219 (src line 1573) - - -state 101 - common_dcl: LVAR vardcl. (28) - - . reduce 28 (src line 367) - - -state 102 - common_dcl: LVAR '('.vardcl_list osemi ')' - common_dcl: LVAR '('.')' - - LNAME shift 10 - ')' shift 221 - '?' shift 12 - '@' shift 13 - . error - - sym goto 105 - dcl_name goto 104 - dcl_name_list goto 103 - vardcl goto 222 - vardcl_list goto 220 - hidden_importsym goto 11 - -state 103 - vardcl: dcl_name_list.ntype - vardcl: dcl_name_list.ntype '=' expr_list - vardcl: dcl_name_list.'=' expr_list - dcl_name_list: dcl_name_list.',' dcl_name - - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 232 - '*' shift 196 - '(' shift 231 - '=' shift 224 - '[' shift 77 - '?' shift 12 - '@' shift 13 - ',' shift 225 - . error - - sym goto 123 - ntype goto 223 - dotname goto 230 - name goto 197 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 229 - recvchantype goto 226 - othertype goto 228 - fntype goto 227 - hidden_importsym goto 11 - -state 104 - dcl_name_list: dcl_name. (274) - - . reduce 274 (src line 1892) - - -state 105 - dcl_name: sym. (154) - - . reduce 154 (src line 1163) - - -state 106 - common_dcl: lconst constdcl. (31) - - . reduce 31 (src line 380) - - -state 107 - common_dcl: lconst '('.constdcl osemi ')' - common_dcl: lconst '('.constdcl ';' constdcl_list osemi ')' - common_dcl: lconst '('.')' - - LNAME shift 10 - ')' shift 234 - '?' shift 12 - '@' shift 13 - . error - - sym goto 105 - dcl_name goto 104 - dcl_name_list goto 108 - constdcl goto 233 - hidden_importsym goto 11 - -state 108 - constdcl: dcl_name_list.ntype '=' expr_list - constdcl: dcl_name_list.'=' expr_list - dcl_name_list: dcl_name_list.',' dcl_name - - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 232 - '*' shift 196 - '(' shift 231 - '=' shift 236 - '[' shift 77 - '?' shift 12 - '@' shift 13 - ',' shift 225 - . error - - sym goto 123 - ntype goto 235 - dotname goto 230 - name goto 197 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 229 - recvchantype goto 226 - othertype goto 228 - fntype goto 227 - hidden_importsym goto 11 - -state 109 - common_dcl: LTYPE typedcl. (35) - - . reduce 35 (src line 403) - - -state 110 - common_dcl: LTYPE '('.typedcl_list osemi ')' - common_dcl: LTYPE '('.')' - - LNAME shift 10 - ')' shift 238 - '?' shift 12 - '@' shift 13 - . error - - sym goto 112 - typedclname goto 111 - typedcl goto 239 - typedcl_list goto 237 - hidden_importsym goto 11 - -state 111 - typedcl: typedclname.ntype - - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 232 - '*' shift 196 - '(' shift 231 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - ntype goto 240 - dotname goto 230 - name goto 197 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 229 - recvchantype goto 226 - othertype goto 228 - fntype goto 227 - hidden_importsym goto 11 - -state 112 - typedclname: sym. (47) - - . reduce 47 (src line 457) - - -state 113 - xfndcl: LFUNC fndcl.fnbody - fnbody: . (210) - - '{' shift 242 - . reduce 210 (src line 1519) - - fnbody goto 241 - -state 114 - fndcl: '('.oarg_type_list_ocomma ')' sym '(' oarg_type_list_ocomma ')' fnres - fntype: LFUNC '('.oarg_type_list_ocomma ')' fnres - oarg_type_list_ocomma: . (249) - - LCHAN shift 78 - LDDD shift 250 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 232 - '*' shift 196 - '(' shift 231 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 249 (src line 1769) - - sym goto 247 - ntype goto 249 - arg_type goto 245 - dotname goto 230 - name goto 197 - name_or_type goto 246 - oarg_type_list_ocomma goto 243 - arg_type_list goto 244 - dotdotdot goto 248 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 229 - recvchantype goto 226 - othertype goto 228 - fntype goto 227 - hidden_importsym goto 11 - -state 115 - fndcl: sym.'(' oarg_type_list_ocomma ')' fnres - - '(' shift 251 - . error - - -state 116 - non_dcl_stmt: labelname ':'.$$261 stmt - $$261: . (261) - - . reduce 261 (src line 1802) - - $$261 goto 252 - -state 117 - non_dcl_stmt: LBREAK onew_name. (264) - - . reduce 264 (src line 1824) - - -state 118 - onew_name: new_name. (156) - - . reduce 156 (src line 1173) - - -state 119 - new_name: sym. (153) - - . reduce 153 (src line 1153) - - -state 120 - non_dcl_stmt: LCONTINUE onew_name. (265) - - . reduce 265 (src line 1828) - - -state 121 - pexpr_no_paren: pseudocall. (134) - non_dcl_stmt: LGO pseudocall. (266) - - '(' 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 - pseudocall: pexpr.'(' ')' - pseudocall: pexpr.'(' expr_or_type_list ocomma ')' - pseudocall: pexpr.'(' expr_or_type_list LDDD ocomma ')' - pexpr_no_paren: pexpr.'.' sym - pexpr_no_paren: pexpr.'.' '(' expr_or_type ')' - pexpr_no_paren: pexpr.'.' '(' LTYPE ')' - pexpr_no_paren: pexpr.'[' expr ']' - pexpr_no_paren: pexpr.'[' oexpr ':' oexpr ']' - pexpr_no_paren: pexpr.'[' oexpr ':' oexpr ':' oexpr ']' - - '(' shift 160 - '.' shift 161 - '[' shift 162 - . error - - -state 123 - name: sym. (162) - - . reduce 162 (src line 1220) - - -state 124 - fntype: LFUNC.'(' oarg_type_list_ocomma ')' fnres - - '(' shift 253 - . error - - -state 125 - pexpr_no_paren: pseudocall. (134) - non_dcl_stmt: LDEFER pseudocall. (267) - - '(' 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 1840) - - -state 127 - non_dcl_stmt: LRETURN oexpr_list. (269) - - . reduce 269 (src line 1845) - - -state 128 - expr_list: expr_list.',' expr - oexpr_list: expr_list. (293) - - ',' shift 155 - . reduce 293 (src line 1971) - - -state 129 - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - expr_list: expr. (276) - - LANDAND shift 134 - LANDNOT shift 149 - LCOMM shift 152 - LEQ shift 135 - LGE shift 139 - LGT shift 140 - LLE shift 138 - LLSH shift 150 - LLT shift 137 - LNE shift 136 - LOROR shift 133 - LRSH shift 151 - '+' shift 141 - '-' shift 142 - '|' shift 143 - '^' shift 144 - '*' shift 145 - '/' shift 146 - '%' shift 147 - '&' shift 148 - . reduce 276 (src line 1902) - - -state 130 - simple_stmt: expr LASOP.expr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 254 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 131 - simple_stmt: expr LINC. (53) - - . reduce 53 (src line 522) - - -state 132 - simple_stmt: expr LDEC. (54) - - . reduce 54 (src line 528) - - -state 133 - expr: expr LOROR.expr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 255 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 134 - expr: expr LANDAND.expr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 256 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 135 - expr: expr LEQ.expr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 257 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 136 - expr: expr LNE.expr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 258 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 137 - expr: expr LLT.expr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 259 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 138 - expr: expr LLE.expr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 260 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 139 - expr: expr LGE.expr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 261 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 140 - expr: expr LGT.expr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 262 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 141 - expr: expr '+'.expr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 263 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 142 - expr: expr '-'.expr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 264 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 143 - expr: expr '|'.expr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 265 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 144 - expr: expr '^'.expr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 266 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 145 - expr: expr '*'.expr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 267 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 146 - expr: expr '/'.expr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 268 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 147 - expr: expr '%'.expr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 269 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 148 - expr: expr '&'.expr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 270 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 149 - expr: expr LANDNOT.expr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 271 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 150 - expr: expr LLSH.expr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 272 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 151 - expr: expr LRSH.expr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 273 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 152 - expr: expr LCOMM.expr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 274 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 153 - simple_stmt: expr_list '='.expr_list - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 129 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - expr_list goto 275 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 154 - simple_stmt: expr_list LCOLAS.expr_list - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 129 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - expr_list goto 276 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 155 - expr_list: expr_list ','.expr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 277 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 156 - for_stmt: LFOR $$74.for_body - osimple_stmt: . (294) - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LRANGE shift 284 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 294 (src line 1973) - - sym goto 123 - expr goto 48 - fnliteral goto 73 - for_body goto 278 - for_header goto 279 - name goto 69 - osimple_stmt goto 280 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - range_stmt goto 281 - simple_stmt goto 282 - uexpr goto 55 - expr_list goto 283 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 157 - switch_stmt: LSWITCH $$88.if_header $$89 LBODY caseblock_list '}' - osimple_stmt: . (294) - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 294 (src line 1973) - - sym goto 123 - expr goto 48 - fnliteral goto 73 - if_header goto 285 - name goto 69 - osimple_stmt goto 286 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - simple_stmt goto 282 - uexpr goto 55 - expr_list goto 49 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 158 - select_stmt: LSELECT $$91.LBODY caseblock_list '}' - - LBODY shift 287 - . error - - -state 159 - if_stmt: LIF $$78.if_header $$79 loop_body $$80 elseif_list else - osimple_stmt: . (294) - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 294 (src line 1973) - - sym goto 123 - expr goto 48 - fnliteral goto 73 - if_header goto 288 - name goto 69 - osimple_stmt goto 286 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - simple_stmt goto 282 - uexpr goto 55 - expr_list goto 49 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 160 - pseudocall: pexpr '('.')' - pseudocall: pexpr '('.expr_or_type_list ocomma ')' - pseudocall: pexpr '('.expr_or_type_list LDDD ocomma ')' - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 179 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 178 - '&' shift 59 - '(' shift 67 - ')' shift 289 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 173 - expr_or_type goto 291 - fnliteral goto 73 - name goto 69 - non_expr_type goto 174 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - expr_or_type_list goto 290 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - recvchantype goto 175 - othertype goto 177 - fntype goto 176 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 161 - pexpr_no_paren: pexpr '.'.sym - pexpr_no_paren: pexpr '.'.'(' expr_or_type ')' - pexpr_no_paren: pexpr '.'.'(' LTYPE ')' - - LNAME shift 10 - '(' shift 293 - '?' shift 12 - '@' shift 13 - . error - - sym goto 292 - hidden_importsym goto 11 - -state 162 - pexpr_no_paren: pexpr '['.expr ']' - pexpr_no_paren: pexpr '['.oexpr ':' oexpr ']' - pexpr_no_paren: pexpr '['.oexpr ':' oexpr ':' oexpr ']' - oexpr: . (290) - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 290 (src line 1961) - - sym goto 123 - expr goto 294 - fnliteral goto 73 - name goto 69 - oexpr goto 295 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 163 - uexpr: '*' uexpr. (115) - - . reduce 115 (src line 941) - - -state 164 - uexpr: '&' uexpr. (116) - - . reduce 116 (src line 945) - - -state 165 - uexpr: '+' uexpr. (117) - - . reduce 117 (src line 956) - - -state 166 - uexpr: '-' uexpr. (118) - - . reduce 118 (src line 960) - - -state 167 - uexpr: '!' uexpr. (119) - - . reduce 119 (src line 964) - - -state 168 - uexpr: '~' uexpr. (120) - - . reduce 120 (src line 968) - - -state 169 - uexpr: '^' uexpr. (121) - - . reduce 121 (src line 973) - - -state 170 - uexpr: LCOMM uexpr. (122) - - . 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 1075) - - start_complit goto 296 - -state 172 - pexpr_no_paren: '(' expr_or_type.')' '{' start_complit braced_keyval_list '}' - pexpr: '(' expr_or_type.')' - - ')' shift 297 - . error - - -state 173 - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - expr_or_type: expr. (148) - - LANDAND shift 134 - LANDNOT shift 149 - LCOMM shift 152 - LEQ shift 135 - LGE shift 139 - LGT shift 140 - LLE shift 138 - LLSH shift 150 - LLT shift 137 - LNE shift 136 - LOROR shift 133 - LRSH shift 151 - '+' shift 141 - '-' shift 142 - '|' shift 143 - '^' shift 144 - '*' shift 145 - '/' shift 146 - '%' shift 147 - '&' shift 148 - . reduce 148 (src line 1131) - - -state 174 - expr_or_type: non_expr_type. (149) - - . reduce 149 (src line 1133) - - -state 175 - non_expr_type: recvchantype. (172) - - . reduce 172 (src line 1263) - - -state 176 - non_expr_type: fntype. (173) - convtype: fntype. (181) - fnlitdcl: fntype. (215) - - 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 - non_expr_type: othertype. (174) - convtype: othertype. (182) - comptype: othertype. (183) - - LBODY reduce 183 (src line 1286) - '(' reduce 182 (src line 1284) - '{' reduce 183 (src line 1286) - . reduce 174 (src line 1266) - - -state 178 - uexpr: '*'.uexpr - non_expr_type: '*'.non_expr_type - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 179 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 178 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - fnliteral goto 73 - name goto 69 - non_expr_type goto 298 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 163 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - recvchantype goto 175 - othertype goto 177 - fntype goto 176 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 179 - uexpr: LCOMM.uexpr - recvchantype: LCOMM.LCHAN ntype - - LLITERAL shift 68 - LCHAN shift 299 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 170 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 180 - pexpr_no_paren: convtype '('.expr ocomma ')' - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 300 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 181 - pexpr_no_paren: comptype lbrace.start_complit braced_keyval_list '}' - start_complit: . (140) - - . reduce 140 (src line 1075) - - start_complit goto 301 - -state 182 - lbrace: LBODY. (151) - - . reduce 151 (src line 1138) - - -state 183 - lbrace: '{'. (152) - - . reduce 152 (src line 1143) - - -state 184 - fnliteral: fnlitdcl lbrace.stmt_list '}' - stmt: . (251) - - error shift 307 - LLITERAL shift 68 - LBREAK shift 41 - LCHAN shift 78 - LCONST shift 47 - LCONTINUE shift 42 - LDEFER shift 44 - LFALL shift 40 - LFOR shift 50 - LFUNC shift 124 - LGO shift 43 - LGOTO shift 45 - LIF shift 53 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LRETURN shift 46 - LSELECT shift 52 - LSTRUCT shift 82 - LSWITCH shift 51 - LTYPE shift 32 - LVAR shift 30 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - ';' reduce 251 (src line 1781) - '{' shift 308 - '}' reduce 251 (src line 1781) - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 56 - stmt goto 303 - compound_stmt goto 304 - expr goto 48 - fnliteral goto 73 - for_stmt goto 35 - if_stmt goto 38 - non_dcl_stmt goto 306 - labelname goto 39 - name goto 69 - new_name goto 54 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - select_stmt goto 37 - simple_stmt goto 34 - switch_stmt goto 36 - uexpr goto 55 - expr_list goto 49 - stmt_list goto 302 - common_dcl goto 305 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - lconst goto 31 - fnlitdcl goto 76 - -state 185 - fnliteral: fnlitdcl error. (217) - - . reduce 217 (src line 1558) - - -state 186 - othertype: '[' oexpr.']' ntype - - ']' shift 309 - . error - - -state 187 - othertype: '[' LDDD.']' ntype - - ']' shift 310 - . error - - -state 188 - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - oexpr: expr. (291) - - LANDAND shift 134 - LANDNOT shift 149 - LCOMM shift 152 - LEQ shift 135 - LGE shift 139 - LGT shift 140 - LLE shift 138 - LLSH shift 150 - LLT shift 137 - LNE shift 136 - LOROR shift 133 - LRSH shift 151 - '+' shift 141 - '-' shift 142 - '|' shift 143 - '^' shift 144 - '*' shift 145 - '/' shift 146 - '%' shift 147 - '&' shift 148 - . reduce 291 (src line 1965) - - -state 189 - othertype: LCHAN non_recvchantype. (193) - - . reduce 193 (src line 1320) - - -state 190 - othertype: LCHAN LCOMM.ntype - - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 232 - '*' shift 196 - '(' shift 231 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - ntype goto 311 - dotname goto 230 - name goto 197 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 229 - recvchantype goto 226 - othertype goto 228 - fntype goto 227 - hidden_importsym goto 11 - -state 191 - non_recvchantype: fntype. (176) - - . reduce 176 (src line 1272) - - -state 192 - non_recvchantype: othertype. (177) - - . reduce 177 (src line 1274) - - -state 193 - non_recvchantype: ptrtype. (178) - - . reduce 178 (src line 1275) - - -state 194 - non_recvchantype: dotname. (179) - - . reduce 179 (src line 1276) - - -state 195 - non_recvchantype: '('.ntype ')' - - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 232 - '*' shift 196 - '(' shift 231 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - ntype goto 312 - dotname goto 230 - name goto 197 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 229 - recvchantype goto 226 - othertype goto 228 - fntype goto 227 - hidden_importsym goto 11 - -state 196 - ptrtype: '*'.ntype - - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 232 - '*' shift 196 - '(' shift 231 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - ntype goto 313 - dotname goto 230 - name goto 197 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 229 - recvchantype goto 226 - othertype goto 228 - fntype goto 227 - hidden_importsym goto 11 - -state 197 - dotname: name. (189) - dotname: name.'.' sym - - '.' shift 314 - . reduce 189 (src line 1296) - - -state 198 - othertype: LMAP '['.ntype ']' ntype - - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 232 - '*' shift 196 - '(' shift 231 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - ntype goto 315 - dotname goto 230 - name goto 197 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 229 - recvchantype goto 226 - othertype goto 228 - fntype goto 227 - hidden_importsym goto 11 - -state 199 - structtype: LSTRUCT lbrace.structdcl_list osemi '}' - structtype: LSTRUCT lbrace.'}' - - LNAME shift 325 - '*' shift 322 - '(' shift 321 - '}' shift 317 - '?' shift 12 - '@' shift 13 - . error - - sym goto 119 - packname goto 324 - embed goto 320 - new_name goto 323 - new_name_list goto 319 - structdcl goto 318 - structdcl_list goto 316 - hidden_importsym goto 11 - -state 200 - interfacetype: LINTERFACE lbrace.interfacedcl_list osemi '}' - interfacetype: LINTERFACE lbrace.'}' - - LNAME shift 325 - '(' shift 331 - '}' shift 327 - '?' shift 12 - '@' shift 13 - . error - - sym goto 119 - packname goto 330 - interfacedcl goto 328 - new_name goto 329 - interfacedcl_list goto 326 - hidden_importsym goto 11 - -state 201 - import: LIMPORT '(' import_stmt_list.osemi ')' - import_stmt_list: import_stmt_list.';' import_stmt - osemi: . (286) - - ';' shift 333 - . reduce 286 (src line 1955) - - osemi goto 332 - -state 202 - import: LIMPORT '(' ')'. (10) - - . reduce 10 (src line 233) - - -state 203 - import_stmt_list: import_stmt. (13) - - . reduce 13 (src line 282) - - -state 204 - import_stmt: import_here import_package.import_there - $$21: . (21) - - . reduce 21 (src line 334) - - import_there goto 334 - $$21 goto 15 - -state 205 - import_stmt: import_here import_there. (12) - - . reduce 12 (src line 271) - - -state 206 - import_here: sym LLITERAL. (16) - - . reduce 16 (src line 294) - - -state 207 - import_here: '.' LLITERAL. (17) - - . reduce 17 (src line 301) - - -state 208 - hidden_importsym: '@' LLITERAL '.' LNAME. (160) - - . reduce 160 (src line 1190) - - -state 209 - hidden_importsym: '@' LLITERAL '.' '?'. (161) - - . reduce 161 (src line 1205) - - -state 210 - import_there: $$21 hidden_import_list '$' '$'. (22) - - . reduce 22 (src line 338) - - -state 211 - hidden_import: LIMPORT LNAME.LLITERAL ';' - - LLITERAL shift 335 - . error - - -state 212 - hidden_import: LVAR hidden_pkg_importsym.hidden_type ';' - - LCHAN shift 347 - LFUNC shift 349 - LINTERFACE shift 345 - LMAP shift 343 - LNAME shift 341 - LSTRUCT shift 344 - LCOMM shift 348 - '*' shift 346 - '[' shift 342 - '@' shift 13 - . error - - hidden_importsym goto 340 - hidden_type goto 336 - hidden_type_misc goto 337 - hidden_type_func goto 339 - hidden_type_recv_chan goto 338 - -state 213 - hidden_pkg_importsym: hidden_importsym. (310) - - . reduce 310 (src line 2047) - - -state 214 - hidden_import: LCONST hidden_pkg_importsym.'=' hidden_constant ';' - hidden_import: LCONST hidden_pkg_importsym.hidden_type '=' hidden_constant ';' - - LCHAN shift 347 - LFUNC shift 349 - LINTERFACE shift 345 - LMAP shift 343 - LNAME shift 341 - LSTRUCT shift 344 - LCOMM shift 348 - '*' shift 346 - '=' shift 350 - '[' shift 342 - '@' shift 13 - . error - - hidden_importsym goto 340 - hidden_type goto 351 - hidden_type_misc goto 337 - hidden_type_func goto 339 - hidden_type_recv_chan goto 338 - -state 215 - hidden_import: LTYPE hidden_pkgtype.hidden_type ';' - - LCHAN shift 347 - LFUNC shift 349 - LINTERFACE shift 345 - LMAP shift 343 - LNAME shift 341 - LSTRUCT shift 344 - LCOMM shift 348 - '*' shift 346 - '[' shift 342 - '@' shift 13 - . error - - hidden_importsym goto 340 - hidden_type goto 352 - hidden_type_misc goto 337 - hidden_type_func goto 339 - hidden_type_recv_chan goto 338 - -state 216 - hidden_pkgtype: hidden_pkg_importsym. (311) - - . reduce 311 (src line 2054) - - -state 217 - hidden_import: LFUNC hidden_fndcl.fnbody ';' - fnbody: . (210) - - '{' shift 242 - . reduce 210 (src line 1519) - - fnbody goto 353 - -state 218 - hidden_fndcl: hidden_pkg_importsym.'(' ohidden_funarg_list ')' ohidden_funres - - '(' shift 354 - . error - - -state 219 - hidden_fndcl: '('.hidden_funarg_list ')' sym '(' ohidden_funarg_list ')' ohidden_funres - - LNAME shift 10 - '?' shift 12 - '@' shift 13 - . error - - sym goto 357 - hidden_importsym goto 11 - hidden_funarg goto 356 - hidden_funarg_list goto 355 - -state 220 - common_dcl: LVAR '(' vardcl_list.osemi ')' - vardcl_list: vardcl_list.';' vardcl - osemi: . (286) - - ';' shift 359 - . reduce 286 (src line 1955) - - osemi goto 358 - -state 221 - common_dcl: LVAR '(' ')'. (30) - - . reduce 30 (src line 376) - - -state 222 - vardcl_list: vardcl. (220) - - . reduce 220 (src line 1585) - - -state 223 - vardcl: dcl_name_list ntype. (39) - vardcl: dcl_name_list ntype.'=' expr_list - - '=' shift 360 - . reduce 39 (src line 422) - - -state 224 - vardcl: dcl_name_list '='.expr_list - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 129 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - expr_list goto 361 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 225 - dcl_name_list: dcl_name_list ','.dcl_name - - LNAME shift 10 - '?' shift 12 - '@' shift 13 - . error - - sym goto 105 - dcl_name goto 362 - hidden_importsym goto 11 - -state 226 - ntype: recvchantype. (166) - - . reduce 166 (src line 1252) - - -state 227 - ntype: fntype. (167) - - . reduce 167 (src line 1254) - - -state 228 - ntype: othertype. (168) - - . reduce 168 (src line 1255) - - -state 229 - ntype: ptrtype. (169) - - . reduce 169 (src line 1256) - - -state 230 - ntype: dotname. (170) - - . reduce 170 (src line 1257) - - -state 231 - ntype: '('.ntype ')' - - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 232 - '*' shift 196 - '(' shift 231 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - ntype goto 363 - dotname goto 230 - name goto 197 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 229 - recvchantype goto 226 - othertype goto 228 - fntype goto 227 - hidden_importsym goto 11 - -state 232 - recvchantype: LCOMM.LCHAN ntype - - LCHAN shift 364 - . error - - -state 233 - common_dcl: lconst '(' constdcl.osemi ')' - common_dcl: lconst '(' constdcl.';' constdcl_list osemi ')' - osemi: . (286) - - ';' shift 366 - . reduce 286 (src line 1955) - - osemi goto 365 - -state 234 - common_dcl: lconst '(' ')'. (34) - - . reduce 34 (src line 398) - - -state 235 - constdcl: dcl_name_list ntype.'=' expr_list - - '=' shift 367 - . error - - -state 236 - constdcl: dcl_name_list '='.expr_list - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 129 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - expr_list goto 368 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 237 - common_dcl: LTYPE '(' typedcl_list.osemi ')' - typedcl_list: typedcl_list.';' typedcl - osemi: . (286) - - ';' shift 370 - . reduce 286 (src line 1955) - - osemi goto 369 - -state 238 - common_dcl: LTYPE '(' ')'. (37) - - . reduce 37 (src line 411) - - -state 239 - typedcl_list: typedcl. (224) - - . reduce 224 (src line 1599) - - -state 240 - typedcl: typedclname ntype. (48) - - . reduce 48 (src line 466) - - -state 241 - xfndcl: LFUNC fndcl fnbody. (204) - - . reduce 204 (src line 1380) - - -state 242 - fnbody: '{'.stmt_list '}' - stmt: . (251) - - error shift 307 - LLITERAL shift 68 - LBREAK shift 41 - LCHAN shift 78 - LCONST shift 47 - LCONTINUE shift 42 - LDEFER shift 44 - LFALL shift 40 - LFOR shift 50 - LFUNC shift 124 - LGO shift 43 - LGOTO shift 45 - LIF shift 53 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LRETURN shift 46 - LSELECT shift 52 - LSTRUCT shift 82 - LSWITCH shift 51 - LTYPE shift 32 - LVAR shift 30 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - ';' reduce 251 (src line 1781) - '{' shift 308 - '}' reduce 251 (src line 1781) - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 56 - stmt goto 303 - compound_stmt goto 304 - expr goto 48 - fnliteral goto 73 - for_stmt goto 35 - if_stmt goto 38 - non_dcl_stmt goto 306 - labelname goto 39 - name goto 69 - new_name goto 54 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - select_stmt goto 37 - simple_stmt goto 34 - switch_stmt goto 36 - uexpr goto 55 - expr_list goto 49 - stmt_list goto 371 - common_dcl goto 305 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - lconst goto 31 - fnlitdcl goto 76 - -state 243 - fndcl: '(' oarg_type_list_ocomma.')' sym '(' oarg_type_list_ocomma ')' fnres - fntype: LFUNC '(' oarg_type_list_ocomma.')' fnres - - ')' shift 372 - . error - - -state 244 - arg_type_list: arg_type_list.',' arg_type - oarg_type_list_ocomma: arg_type_list.ocomma - ocomma: . (288) - - ',' shift 373 - . reduce 288 (src line 1958) - - ocomma goto 374 - -state 245 - arg_type_list: arg_type. (247) - - . reduce 247 (src line 1759) - - -state 246 - arg_type: name_or_type. (243) - - . reduce 243 (src line 1743) - - -state 247 - name: sym. (162) - arg_type: sym.name_or_type - arg_type: sym.dotdotdot - - LCHAN shift 78 - LDDD shift 250 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 232 - '*' shift 196 - '(' shift 231 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 162 (src line 1220) - - sym goto 123 - ntype goto 249 - dotname goto 230 - name goto 197 - name_or_type goto 375 - dotdotdot goto 376 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 229 - recvchantype goto 226 - othertype goto 228 - fntype goto 227 - hidden_importsym goto 11 - -state 248 - arg_type: dotdotdot. (246) - - . reduce 246 (src line 1757) - - -state 249 - name_or_type: ntype. (150) - - . reduce 150 (src line 1135) - - -state 250 - dotdotdot: LDDD. (164) - dotdotdot: LDDD.ntype - - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 232 - '*' shift 196 - '(' shift 231 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 164 (src line 1241) - - sym goto 123 - ntype goto 377 - dotname goto 230 - name goto 197 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 229 - recvchantype goto 226 - othertype goto 228 - fntype goto 227 - hidden_importsym goto 11 - -state 251 - fndcl: sym '('.oarg_type_list_ocomma ')' fnres - oarg_type_list_ocomma: . (249) - - LCHAN shift 78 - LDDD shift 250 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 232 - '*' shift 196 - '(' shift 231 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 249 (src line 1769) - - sym goto 247 - ntype goto 249 - arg_type goto 245 - dotname goto 230 - name goto 197 - name_or_type goto 246 - oarg_type_list_ocomma goto 378 - arg_type_list goto 244 - dotdotdot goto 248 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 229 - recvchantype goto 226 - othertype goto 228 - fntype goto 227 - hidden_importsym goto 11 - -state 252 - non_dcl_stmt: labelname ':' $$261.stmt - stmt: . (251) - - error shift 307 - LLITERAL shift 68 - LBREAK shift 41 - LCASE reduce 251 (src line 1781) - LCHAN shift 78 - LCONST shift 47 - LCONTINUE shift 42 - LDEFAULT reduce 251 (src line 1781) - LDEFER shift 44 - LFALL shift 40 - LFOR shift 50 - LFUNC shift 124 - LGO shift 43 - LGOTO shift 45 - LIF shift 53 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LRETURN shift 46 - LSELECT shift 52 - LSTRUCT shift 82 - LSWITCH shift 51 - LTYPE shift 32 - LVAR shift 30 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - ';' reduce 251 (src line 1781) - '{' shift 308 - '}' reduce 251 (src line 1781) - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 56 - stmt goto 379 - compound_stmt goto 304 - expr goto 48 - fnliteral goto 73 - for_stmt goto 35 - if_stmt goto 38 - non_dcl_stmt goto 306 - labelname goto 39 - name goto 69 - new_name goto 54 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - select_stmt goto 37 - simple_stmt goto 34 - switch_stmt goto 36 - uexpr goto 55 - expr_list goto 49 - common_dcl goto 305 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - lconst goto 31 - fnlitdcl goto 76 - -state 253 - fntype: LFUNC '('.oarg_type_list_ocomma ')' fnres - oarg_type_list_ocomma: . (249) - - LCHAN shift 78 - LDDD shift 250 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 232 - '*' shift 196 - '(' shift 231 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 249 (src line 1769) - - sym goto 247 - ntype goto 249 - arg_type goto 245 - dotname goto 230 - name goto 197 - name_or_type goto 246 - oarg_type_list_ocomma goto 380 - arg_type_list goto 244 - dotdotdot goto 248 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 229 - recvchantype goto 226 - othertype goto 228 - fntype goto 227 - hidden_importsym goto 11 - -state 254 - simple_stmt: expr LASOP expr. (50) - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - - LANDAND shift 134 - LANDNOT shift 149 - LCOMM shift 152 - LEQ shift 135 - LGE shift 139 - LGT shift 140 - LLE shift 138 - LLSH shift 150 - LLT shift 137 - LNE shift 136 - LOROR shift 133 - LRSH shift 151 - '+' shift 141 - '-' shift 142 - '|' shift 143 - '^' shift 144 - '*' shift 145 - '/' shift 146 - '%' shift 147 - '&' shift 148 - . reduce 50 (src line 487) - - -state 255 - expr: expr.LOROR expr - expr: expr LOROR expr. (94) - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - - LANDAND shift 134 - LANDNOT shift 149 - LEQ shift 135 - LGE shift 139 - LGT shift 140 - LLE shift 138 - LLSH shift 150 - LLT shift 137 - LNE shift 136 - LRSH shift 151 - '+' shift 141 - '-' shift 142 - '|' shift 143 - '^' shift 144 - '*' shift 145 - '/' shift 146 - '%' shift 147 - '&' shift 148 - . reduce 94 (src line 857) - - -state 256 - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr LANDAND expr. (95) - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - - LANDNOT shift 149 - LEQ shift 135 - LGE shift 139 - LGT shift 140 - LLE shift 138 - LLSH shift 150 - LLT shift 137 - LNE shift 136 - LRSH shift 151 - '+' shift 141 - '-' shift 142 - '|' shift 143 - '^' shift 144 - '*' shift 145 - '/' shift 146 - '%' shift 147 - '&' shift 148 - . reduce 95 (src line 861) - - -state 257 - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr LEQ expr. (96) - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - - LANDNOT shift 149 - LLSH shift 150 - LRSH shift 151 - '+' shift 141 - '-' shift 142 - '|' shift 143 - '^' shift 144 - '*' shift 145 - '/' shift 146 - '%' shift 147 - '&' shift 148 - . reduce 96 (src line 865) - - -state 258 - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr LNE expr. (97) - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - - LANDNOT shift 149 - LLSH shift 150 - LRSH shift 151 - '+' shift 141 - '-' shift 142 - '|' shift 143 - '^' shift 144 - '*' shift 145 - '/' shift 146 - '%' shift 147 - '&' shift 148 - . reduce 97 (src line 869) - - -state 259 - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr LLT expr. (98) - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - - LANDNOT shift 149 - LLSH shift 150 - LRSH shift 151 - '+' shift 141 - '-' shift 142 - '|' shift 143 - '^' shift 144 - '*' shift 145 - '/' shift 146 - '%' shift 147 - '&' shift 148 - . reduce 98 (src line 873) - - -state 260 - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr LLE expr. (99) - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - - LANDNOT shift 149 - LLSH shift 150 - LRSH shift 151 - '+' shift 141 - '-' shift 142 - '|' shift 143 - '^' shift 144 - '*' shift 145 - '/' shift 146 - '%' shift 147 - '&' shift 148 - . reduce 99 (src line 877) - - -state 261 - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr LGE expr. (100) - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - - LANDNOT shift 149 - LLSH shift 150 - LRSH shift 151 - '+' shift 141 - '-' shift 142 - '|' shift 143 - '^' shift 144 - '*' shift 145 - '/' shift 146 - '%' shift 147 - '&' shift 148 - . reduce 100 (src line 881) - - -state 262 - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr LGT expr. (101) - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - - LANDNOT shift 149 - LLSH shift 150 - LRSH shift 151 - '+' shift 141 - '-' shift 142 - '|' shift 143 - '^' shift 144 - '*' shift 145 - '/' shift 146 - '%' shift 147 - '&' shift 148 - . reduce 101 (src line 885) - - -state 263 - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr '+' expr. (102) - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - - LANDNOT shift 149 - LLSH shift 150 - LRSH shift 151 - '*' shift 145 - '/' shift 146 - '%' shift 147 - '&' shift 148 - . reduce 102 (src line 889) - - -state 264 - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr '-' expr. (103) - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - - LANDNOT shift 149 - LLSH shift 150 - LRSH shift 151 - '*' shift 145 - '/' shift 146 - '%' shift 147 - '&' shift 148 - . reduce 103 (src line 893) - - -state 265 - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr '|' expr. (104) - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - - LANDNOT shift 149 - LLSH shift 150 - LRSH shift 151 - '*' shift 145 - '/' shift 146 - '%' shift 147 - '&' shift 148 - . reduce 104 (src line 897) - - -state 266 - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr '^' expr. (105) - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - - LANDNOT shift 149 - LLSH shift 150 - LRSH shift 151 - '*' shift 145 - '/' shift 146 - '%' shift 147 - '&' shift 148 - . reduce 105 (src line 901) - - -state 267 - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr '*' expr. (106) - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - - . reduce 106 (src line 905) - - -state 268 - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr '/' expr. (107) - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - - . reduce 107 (src line 909) - - -state 269 - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr '%' expr. (108) - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - - . reduce 108 (src line 913) - - -state 270 - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr '&' expr. (109) - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - - . reduce 109 (src line 917) - - -state 271 - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr LANDNOT expr. (110) - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - - . reduce 110 (src line 921) - - -state 272 - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr LLSH expr. (111) - expr: expr.LRSH expr - expr: expr.LCOMM expr - - . reduce 111 (src line 925) - - -state 273 - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr LRSH expr. (112) - expr: expr.LCOMM expr - - . reduce 112 (src line 929) - - -state 274 - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - expr: expr LCOMM expr. (113) - - LANDAND shift 134 - LANDNOT shift 149 - LEQ shift 135 - LGE shift 139 - LGT shift 140 - LLE shift 138 - LLSH shift 150 - LLT shift 137 - LNE shift 136 - LOROR shift 133 - LRSH shift 151 - '+' shift 141 - '-' shift 142 - '|' shift 143 - '^' shift 144 - '*' shift 145 - '/' shift 146 - '%' shift 147 - '&' shift 148 - . reduce 113 (src line 934) - - -state 275 - simple_stmt: expr_list '=' expr_list. (51) - expr_list: expr_list.',' expr - - ',' shift 155 - . reduce 51 (src line 492) - - -state 276 - simple_stmt: expr_list LCOLAS expr_list. (52) - expr_list: expr_list.',' expr - - ',' shift 155 - . reduce 52 (src line 504) - - -state 277 - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - expr_list: expr_list ',' expr. (277) - - LANDAND shift 134 - LANDNOT shift 149 - LCOMM shift 152 - LEQ shift 135 - LGE shift 139 - LGT shift 140 - LLE shift 138 - LLSH shift 150 - LLT shift 137 - LNE shift 136 - LOROR shift 133 - LRSH shift 151 - '+' shift 141 - '-' shift 142 - '|' shift 143 - '^' shift 144 - '*' shift 145 - '/' shift 146 - '%' shift 147 - '&' shift 148 - . reduce 277 (src line 1907) - - -state 278 - for_stmt: LFOR $$74 for_body. (75) - - . reduce 75 (src line 726) - - -state 279 - for_body: for_header.loop_body - - LBODY shift 382 - . error - - loop_body goto 381 - -state 280 - for_header: osimple_stmt.';' osimple_stmt ';' osimple_stmt - for_header: osimple_stmt. (71) - - ';' shift 383 - . reduce 71 (src line 706) - - -state 281 - for_header: range_stmt. (72) - - . reduce 72 (src line 712) - - -state 282 - osimple_stmt: simple_stmt. (295) - - . reduce 295 (src line 1977) - - -state 283 - simple_stmt: expr_list.'=' expr_list - simple_stmt: expr_list.LCOLAS expr_list - range_stmt: expr_list.'=' LRANGE expr - range_stmt: expr_list.LCOLAS LRANGE expr - expr_list: expr_list.',' expr - - LCOLAS shift 385 - '=' shift 384 - ',' shift 155 - . error - - -state 284 - range_stmt: LRANGE.expr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 386 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 285 - switch_stmt: LSWITCH $$88 if_header.$$89 LBODY caseblock_list '}' - $$89: . (89) - - . reduce 89 (src line 821) - - $$89 goto 387 - -state 286 - if_header: osimple_stmt. (76) - if_header: osimple_stmt.';' osimple_stmt - - ';' shift 388 - . reduce 76 (src line 732) - - -state 287 - select_stmt: LSELECT $$91 LBODY.caseblock_list '}' - caseblock_list: . (63) - - . reduce 63 (src line 652) - - caseblock_list goto 389 - -state 288 - if_stmt: LIF $$78 if_header.$$79 loop_body $$80 elseif_list else - $$79: . (79) - - . reduce 79 (src line 755) - - $$79 goto 390 - -state 289 - pseudocall: pexpr '(' ')'. (123) - - . reduce 123 (src line 986) - - -state 290 - pseudocall: pexpr '(' expr_or_type_list.ocomma ')' - pseudocall: pexpr '(' expr_or_type_list.LDDD ocomma ')' - expr_or_type_list: expr_or_type_list.',' expr_or_type - ocomma: . (288) - - LDDD shift 392 - ',' shift 393 - . reduce 288 (src line 1958) - - ocomma goto 391 - -state 291 - expr_or_type_list: expr_or_type. (278) - - . reduce 278 (src line 1912) - - -state 292 - pexpr_no_paren: pexpr '.' sym. (128) - - . reduce 128 (src line 1009) - - -state 293 - pexpr_no_paren: pexpr '.' '('.expr_or_type ')' - pexpr_no_paren: pexpr '.' '('.LTYPE ')' - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LTYPE shift 395 - LCOMM shift 179 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 178 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 173 - expr_or_type goto 394 - fnliteral goto 73 - name goto 69 - non_expr_type goto 174 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - recvchantype goto 175 - othertype goto 177 - fntype goto 176 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 294 - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - pexpr_no_paren: pexpr '[' expr.']' - oexpr: expr. (291) - - LANDAND shift 134 - LANDNOT shift 149 - LCOMM shift 152 - LEQ shift 135 - LGE shift 139 - LGT shift 140 - LLE shift 138 - LLSH shift 150 - LLT shift 137 - LNE shift 136 - LOROR shift 133 - LRSH shift 151 - '+' shift 141 - '-' shift 142 - '|' shift 143 - '^' shift 144 - '*' shift 145 - '/' shift 146 - '%' shift 147 - '&' shift 148 - ']' shift 396 - . reduce 291 (src line 1965) - - -state 295 - pexpr_no_paren: pexpr '[' oexpr.':' oexpr ']' - pexpr_no_paren: pexpr '[' oexpr.':' oexpr ':' oexpr ']' - - ':' shift 397 - . error - - -state 296 - pexpr_no_paren: pexpr_no_paren '{' start_complit.braced_keyval_list '}' - braced_keyval_list: . (284) - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '{' shift 403 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 284 (src line 1943) - - sym goto 123 - expr goto 402 - bare_complitexpr goto 401 - fnliteral goto 73 - keyval goto 400 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - keyval_list goto 399 - braced_keyval_list goto 398 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 297 - pexpr_no_paren: '(' expr_or_type ')'.'{' start_complit braced_keyval_list '}' - pexpr: '(' expr_or_type ')'. (147) - - '{' shift 404 - . reduce 147 (src line 1118) - - -state 298 - non_expr_type: '*' non_expr_type. (175) - - . reduce 175 (src line 1267) - - -state 299 - othertype: LCHAN.non_recvchantype - othertype: LCHAN.LCOMM ntype - recvchantype: LCOMM LCHAN.ntype - - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 405 - '*' shift 196 - '(' shift 411 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - ntype goto 406 - dotname goto 410 - name goto 197 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 409 - recvchantype goto 226 - non_recvchantype goto 189 - othertype goto 408 - fntype goto 407 - hidden_importsym goto 11 - -state 300 - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - pexpr_no_paren: convtype '(' expr.ocomma ')' - ocomma: . (288) - - LANDAND shift 134 - LANDNOT shift 149 - LCOMM shift 152 - LEQ shift 135 - LGE shift 139 - LGT shift 140 - LLE shift 138 - LLSH shift 150 - LLT shift 137 - LNE shift 136 - LOROR shift 133 - LRSH shift 151 - '+' shift 141 - '-' shift 142 - '|' shift 143 - '^' shift 144 - '*' shift 145 - '/' shift 146 - '%' shift 147 - '&' shift 148 - ',' shift 413 - . reduce 288 (src line 1958) - - ocomma goto 412 - -state 301 - pexpr_no_paren: comptype lbrace start_complit.braced_keyval_list '}' - braced_keyval_list: . (284) - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '{' shift 403 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 284 (src line 1943) - - sym goto 123 - expr goto 402 - bare_complitexpr goto 401 - fnliteral goto 73 - keyval goto 400 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - keyval_list goto 399 - braced_keyval_list goto 414 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 302 - fnliteral: fnlitdcl lbrace stmt_list.'}' - stmt_list: stmt_list.';' stmt - - ';' shift 416 - '}' shift 415 - . error - - -state 303 - stmt_list: stmt. (270) - - . reduce 270 (src line 1866) - - -state 304 - stmt: compound_stmt. (252) - - . reduce 252 (src line 1785) - - -state 305 - stmt: common_dcl. (253) - - . reduce 253 (src line 1786) - - -state 306 - stmt: non_dcl_stmt. (254) - - . reduce 254 (src line 1790) - - -state 307 - stmt: error. (255) - - . reduce 255 (src line 1791) - - -state 308 - compound_stmt: '{'.$$59 stmt_list '}' - $$59: . (59) - - . reduce 59 (src line 606) - - $$59 goto 417 - -state 309 - othertype: '[' oexpr ']'.ntype - - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 232 - '*' shift 196 - '(' shift 231 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - ntype goto 418 - dotname goto 230 - name goto 197 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 229 - recvchantype goto 226 - othertype goto 228 - fntype goto 227 - hidden_importsym goto 11 - -state 310 - othertype: '[' LDDD ']'.ntype - - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 232 - '*' shift 196 - '(' shift 231 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - ntype goto 419 - dotname goto 230 - name goto 197 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 229 - recvchantype goto 226 - othertype goto 228 - fntype goto 227 - hidden_importsym goto 11 - -state 311 - othertype: LCHAN LCOMM ntype. (194) - - . reduce 194 (src line 1325) - - -state 312 - non_recvchantype: '(' ntype.')' - - ')' shift 420 - . error - - -state 313 - ptrtype: '*' ntype. (198) - - . reduce 198 (src line 1337) - - -state 314 - dotname: name '.'.sym - - LNAME shift 10 - '?' shift 12 - '@' shift 13 - . error - - sym goto 421 - hidden_importsym goto 11 - -state 315 - othertype: LMAP '[' ntype.']' ntype - - ']' shift 422 - . error - - -state 316 - structtype: LSTRUCT lbrace structdcl_list.osemi '}' - structdcl_list: structdcl_list.';' structdcl - osemi: . (286) - - ';' shift 424 - . reduce 286 (src line 1955) - - osemi goto 423 - -state 317 - structtype: LSTRUCT lbrace '}'. (201) - - . reduce 201 (src line 1357) - - -state 318 - structdcl_list: structdcl. (226) - - . reduce 226 (src line 1609) - - -state 319 - structdcl: new_name_list.ntype oliteral - new_name_list: new_name_list.',' new_name - - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 232 - '*' shift 196 - '(' shift 231 - '[' shift 77 - '?' shift 12 - '@' shift 13 - ',' shift 426 - . error - - sym goto 123 - ntype goto 425 - dotname goto 230 - name goto 197 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 229 - recvchantype goto 226 - othertype goto 228 - fntype goto 227 - hidden_importsym goto 11 - -state 320 - structdcl: embed.oliteral - oliteral: . (302) - - LLITERAL shift 428 - . reduce 302 (src line 1997) - - oliteral goto 427 - -state 321 - structdcl: '('.embed ')' oliteral - structdcl: '('.'*' embed ')' oliteral - - LNAME shift 431 - '*' shift 430 - . error - - packname goto 324 - embed goto 429 - -state 322 - structdcl: '*'.embed oliteral - structdcl: '*'.'(' embed ')' oliteral - - LNAME shift 431 - '(' shift 433 - . error - - packname goto 324 - embed goto 432 - -state 323 - new_name_list: new_name. (272) - - . reduce 272 (src line 1882) - - -state 324 - embed: packname. (238) - - . reduce 238 (src line 1708) - - -state 325 - sym: LNAME. (157) - packname: LNAME. (236) - packname: LNAME.'.' sym - - LLITERAL reduce 236 (src line 1683) - ';' reduce 236 (src line 1683) - '.' shift 434 - '}' reduce 236 (src line 1683) - . reduce 157 (src line 1175) - - -state 326 - interfacetype: LINTERFACE lbrace interfacedcl_list.osemi '}' - interfacedcl_list: interfacedcl_list.';' interfacedcl - osemi: . (286) - - ';' shift 436 - . reduce 286 (src line 1955) - - osemi goto 435 - -state 327 - interfacetype: LINTERFACE lbrace '}'. (203) - - . reduce 203 (src line 1370) - - -state 328 - interfacedcl_list: interfacedcl. (228) - - . reduce 228 (src line 1616) - - -state 329 - interfacedcl: new_name.indcl - - '(' shift 438 - . error - - indcl goto 437 - -state 330 - interfacedcl: packname. (240) - - . reduce 240 (src line 1720) - - -state 331 - interfacedcl: '('.packname ')' - - LNAME shift 431 - . error - - packname goto 439 - -state 332 - import: LIMPORT '(' import_stmt_list osemi.')' - - ')' shift 440 - . error - - -state 333 - import_stmt_list: import_stmt_list ';'.import_stmt - osemi: ';'. (287) - - LLITERAL shift 88 - LNAME shift 10 - '.' shift 90 - '?' shift 12 - '@' shift 13 - . reduce 287 (src line 1956) - - import_here goto 87 - sym goto 89 - hidden_importsym goto 11 - import_stmt goto 441 - -state 334 - import_stmt: import_here import_package import_there. (11) - - . reduce 11 (src line 235) - - -state 335 - hidden_import: LIMPORT LNAME LLITERAL.';' - - ';' shift 442 - . error - - -state 336 - hidden_import: LVAR hidden_pkg_importsym hidden_type.';' - - ';' shift 443 - . error - - -state 337 - hidden_type: hidden_type_misc. (312) - - . reduce 312 (src line 2065) - - -state 338 - hidden_type: hidden_type_recv_chan. (313) - - . reduce 313 (src line 2067) - - -state 339 - hidden_type: hidden_type_func. (314) - - . reduce 314 (src line 2068) - - -state 340 - hidden_type_misc: hidden_importsym. (317) - - . reduce 317 (src line 2074) - - -state 341 - hidden_type_misc: LNAME. (318) - - . reduce 318 (src line 2079) - - -state 342 - hidden_type_misc: '['.']' hidden_type - hidden_type_misc: '['.LLITERAL ']' hidden_type - - LLITERAL shift 445 - ']' shift 444 - . error - - -state 343 - hidden_type_misc: LMAP.'[' hidden_type ']' hidden_type - - '[' shift 446 - . error - - -state 344 - hidden_type_misc: LSTRUCT.'{' ohidden_structdcl_list '}' - - '{' shift 447 - . error - - -state 345 - hidden_type_misc: LINTERFACE.'{' ohidden_interfacedcl_list '}' - - '{' shift 448 - . error - - -state 346 - hidden_type_misc: '*'.hidden_type - - LCHAN shift 347 - LFUNC shift 349 - LINTERFACE shift 345 - LMAP shift 343 - LNAME shift 341 - LSTRUCT shift 344 - LCOMM shift 348 - '*' shift 346 - '[' shift 342 - '@' shift 13 - . error - - hidden_importsym goto 340 - hidden_type goto 449 - hidden_type_misc goto 337 - hidden_type_func goto 339 - hidden_type_recv_chan goto 338 - -state 347 - hidden_type_misc: LCHAN.hidden_type_non_recv_chan - hidden_type_misc: LCHAN.'(' hidden_type_recv_chan ')' - hidden_type_misc: LCHAN.LCOMM hidden_type - - LCHAN shift 347 - LFUNC shift 349 - LINTERFACE shift 345 - LMAP shift 343 - LNAME shift 341 - LSTRUCT shift 344 - LCOMM shift 452 - '*' shift 346 - '(' shift 451 - '[' shift 342 - '@' shift 13 - . error - - hidden_importsym goto 340 - hidden_type_misc goto 453 - hidden_type_func goto 454 - hidden_type_non_recv_chan goto 450 - -state 348 - hidden_type_recv_chan: LCOMM.LCHAN hidden_type - - LCHAN shift 455 - . error - - -state 349 - hidden_type_func: LFUNC.'(' ohidden_funarg_list ')' ohidden_funres - - '(' shift 456 - . error - - -state 350 - hidden_import: LCONST hidden_pkg_importsym '='.hidden_constant ';' - - LLITERAL shift 460 - LNAME shift 10 - '-' shift 461 - '(' shift 459 - '?' shift 12 - '@' shift 13 - . error - - sym goto 462 - hidden_importsym goto 11 - hidden_constant goto 457 - hidden_literal goto 458 - -state 351 - hidden_import: LCONST hidden_pkg_importsym hidden_type.'=' hidden_constant ';' - - '=' shift 463 - . error - - -state 352 - hidden_import: LTYPE hidden_pkgtype hidden_type.';' - - ';' shift 464 - . error - - -state 353 - hidden_import: LFUNC hidden_fndcl fnbody.';' - - ';' shift 465 - . error - - -state 354 - hidden_fndcl: hidden_pkg_importsym '('.ohidden_funarg_list ')' ohidden_funres - ohidden_funarg_list: . (296) - - LNAME shift 10 - '?' shift 12 - '@' shift 13 - . reduce 296 (src line 1979) - - sym goto 357 - hidden_importsym goto 11 - hidden_funarg goto 356 - hidden_funarg_list goto 467 - ohidden_funarg_list goto 466 - -state 355 - hidden_fndcl: '(' hidden_funarg_list.')' sym '(' ohidden_funarg_list ')' ohidden_funres - hidden_funarg_list: hidden_funarg_list.',' hidden_funarg - - ')' shift 468 - ',' shift 469 - . error - - -state 356 - hidden_funarg_list: hidden_funarg. (346) - - . reduce 346 (src line 2274) - - -state 357 - hidden_funarg: sym.hidden_type oliteral - hidden_funarg: sym.LDDD hidden_type oliteral - - LCHAN shift 347 - LDDD shift 471 - LFUNC shift 349 - LINTERFACE shift 345 - LMAP shift 343 - LNAME shift 341 - LSTRUCT shift 344 - LCOMM shift 348 - '*' shift 346 - '[' shift 342 - '@' shift 13 - . error - - hidden_importsym goto 340 - hidden_type goto 470 - hidden_type_misc goto 337 - hidden_type_func goto 339 - hidden_type_recv_chan goto 338 - -state 358 - common_dcl: LVAR '(' vardcl_list osemi.')' - - ')' shift 472 - . error - - -state 359 - vardcl_list: vardcl_list ';'.vardcl - osemi: ';'. (287) - - LNAME shift 10 - '?' shift 12 - '@' shift 13 - . reduce 287 (src line 1956) - - sym goto 105 - dcl_name goto 104 - dcl_name_list goto 103 - vardcl goto 473 - hidden_importsym goto 11 - -state 360 - vardcl: dcl_name_list ntype '='.expr_list - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 129 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - expr_list goto 474 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 361 - vardcl: dcl_name_list '=' expr_list. (41) - expr_list: expr_list.',' expr - - ',' shift 155 - . reduce 41 (src line 431) - - -state 362 - dcl_name_list: dcl_name_list ',' dcl_name. (275) - - . reduce 275 (src line 1897) - - -state 363 - ntype: '(' ntype.')' - - ')' shift 475 - . error - - -state 364 - recvchantype: LCOMM LCHAN.ntype - - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 232 - '*' shift 196 - '(' shift 231 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - ntype goto 406 - dotname goto 230 - name goto 197 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 229 - recvchantype goto 226 - othertype goto 228 - fntype goto 227 - hidden_importsym goto 11 - -state 365 - common_dcl: lconst '(' constdcl osemi.')' - - ')' shift 476 - . error - - -state 366 - common_dcl: lconst '(' constdcl ';'.constdcl_list osemi ')' - osemi: ';'. (287) - - LNAME shift 10 - '?' shift 12 - '@' shift 13 - . reduce 287 (src line 1956) - - sym goto 105 - dcl_name goto 104 - dcl_name_list goto 480 - constdcl goto 479 - constdcl1 goto 478 - constdcl_list goto 477 - hidden_importsym goto 11 - -state 367 - constdcl: dcl_name_list ntype '='.expr_list - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 129 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - expr_list goto 481 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 368 - constdcl: dcl_name_list '=' expr_list. (43) - expr_list: expr_list.',' expr - - ',' shift 155 - . reduce 43 (src line 441) - - -state 369 - common_dcl: LTYPE '(' typedcl_list osemi.')' - - ')' shift 482 - . error - - -state 370 - typedcl_list: typedcl_list ';'.typedcl - osemi: ';'. (287) - - LNAME shift 10 - '?' shift 12 - '@' shift 13 - . reduce 287 (src line 1956) - - sym goto 112 - typedclname goto 111 - typedcl goto 483 - hidden_importsym goto 11 - -state 371 - fnbody: '{' stmt_list.'}' - stmt_list: stmt_list.';' stmt - - ';' shift 416 - '}' shift 484 - . error - - -state 372 - fndcl: '(' oarg_type_list_ocomma ')'.sym '(' oarg_type_list_ocomma ')' fnres - fntype: LFUNC '(' oarg_type_list_ocomma ')'.fnres - fnres: . (212) - - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 232 - '*' shift 196 - '(' shift 488 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 212 (src line 1531) - - sym goto 485 - dotname goto 493 - name goto 197 - fnres goto 486 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 492 - recvchantype goto 489 - othertype goto 491 - fnret_type goto 487 - fntype goto 490 - hidden_importsym goto 11 - -state 373 - arg_type_list: arg_type_list ','.arg_type - ocomma: ','. (289) - - LCHAN shift 78 - LDDD shift 250 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 232 - '*' shift 196 - '(' shift 231 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 289 (src line 1959) - - sym goto 247 - ntype goto 249 - arg_type goto 494 - dotname goto 230 - name goto 197 - name_or_type goto 246 - dotdotdot goto 248 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 229 - recvchantype goto 226 - othertype goto 228 - fntype goto 227 - hidden_importsym goto 11 - -state 374 - oarg_type_list_ocomma: arg_type_list ocomma. (250) - - . reduce 250 (src line 1773) - - -state 375 - arg_type: sym name_or_type. (244) - - . reduce 244 (src line 1745) - - -state 376 - arg_type: sym dotdotdot. (245) - - . reduce 245 (src line 1751) - - -state 377 - dotdotdot: LDDD ntype. (165) - - . reduce 165 (src line 1247) - - -state 378 - fndcl: sym '(' oarg_type_list_ocomma.')' fnres - - ')' shift 495 - . error - - -state 379 - non_dcl_stmt: labelname ':' $$261 stmt. (262) - - . reduce 262 (src line 1807) - - -state 380 - fntype: LFUNC '(' oarg_type_list_ocomma.')' fnres - - ')' shift 496 - . error - - -state 381 - for_body: for_header loop_body. (73) - - . reduce 73 (src line 714) - - -state 382 - loop_body: LBODY.$$65 stmt_list '}' - $$65: . (65) - - . reduce 65 (src line 661) - - $$65 goto 497 - -state 383 - for_header: osimple_stmt ';'.osimple_stmt ';' osimple_stmt - osimple_stmt: . (294) - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 294 (src line 1973) - - sym goto 123 - expr goto 48 - fnliteral goto 73 - name goto 69 - osimple_stmt goto 498 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - simple_stmt goto 282 - uexpr goto 55 - expr_list goto 49 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 384 - simple_stmt: expr_list '='.expr_list - range_stmt: expr_list '='.LRANGE expr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LRANGE shift 499 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 129 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - expr_list goto 275 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 385 - simple_stmt: expr_list LCOLAS.expr_list - range_stmt: expr_list LCOLAS.LRANGE expr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LRANGE shift 500 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 129 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - expr_list goto 276 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 386 - range_stmt: LRANGE expr. (69) - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - - LANDAND shift 134 - LANDNOT shift 149 - LCOMM shift 152 - LEQ shift 135 - LGE shift 139 - LGT shift 140 - LLE shift 138 - LLSH shift 150 - LLT shift 137 - LNE shift 136 - LOROR shift 133 - LRSH shift 151 - '+' shift 141 - '-' shift 142 - '|' shift 143 - '^' shift 144 - '*' shift 145 - '/' shift 146 - '%' shift 147 - '&' shift 148 - . reduce 69 (src line 686) - - -state 387 - switch_stmt: LSWITCH $$88 if_header $$89.LBODY caseblock_list '}' - - LBODY shift 501 - . error - - -state 388 - if_header: osimple_stmt ';'.osimple_stmt - osimple_stmt: . (294) - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 294 (src line 1973) - - sym goto 123 - expr goto 48 - fnliteral goto 73 - name goto 69 - osimple_stmt goto 502 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - simple_stmt goto 282 - uexpr goto 55 - expr_list goto 49 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 389 - caseblock_list: caseblock_list.caseblock - select_stmt: LSELECT $$91 LBODY caseblock_list.'}' - - LCASE shift 506 - LDEFAULT shift 507 - '}' shift 504 - . error - - case goto 505 - caseblock goto 503 - -state 390 - if_stmt: LIF $$78 if_header $$79.loop_body $$80 elseif_list else - - LBODY shift 382 - . error - - loop_body goto 508 - -state 391 - pseudocall: pexpr '(' expr_or_type_list ocomma.')' - - ')' shift 509 - . error - - -state 392 - pseudocall: pexpr '(' expr_or_type_list LDDD.ocomma ')' - ocomma: . (288) - - ',' shift 413 - . reduce 288 (src line 1958) - - ocomma goto 510 - -state 393 - expr_or_type_list: expr_or_type_list ','.expr_or_type - ocomma: ','. (289) - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 179 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 178 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 289 (src line 1959) - - sym goto 123 - expr goto 173 - expr_or_type goto 511 - fnliteral goto 73 - name goto 69 - non_expr_type goto 174 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - recvchantype goto 175 - othertype goto 177 - fntype goto 176 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 394 - pexpr_no_paren: pexpr '.' '(' expr_or_type.')' - - ')' shift 512 - . error - - -state 395 - pexpr_no_paren: pexpr '.' '(' LTYPE.')' - - ')' shift 513 - . error - - -state 396 - pexpr_no_paren: pexpr '[' expr ']'. (131) - - . reduce 131 (src line 1028) - - -state 397 - pexpr_no_paren: pexpr '[' oexpr ':'.oexpr ']' - pexpr_no_paren: pexpr '[' oexpr ':'.oexpr ':' oexpr ']' - oexpr: . (290) - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 290 (src line 1961) - - sym goto 123 - expr goto 188 - fnliteral goto 73 - name goto 69 - oexpr goto 514 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 398 - pexpr_no_paren: pexpr_no_paren '{' start_complit braced_keyval_list.'}' - - '}' shift 515 - . error - - -state 399 - keyval_list: keyval_list.',' keyval - keyval_list: keyval_list.',' bare_complitexpr - braced_keyval_list: keyval_list.ocomma - ocomma: . (288) - - ',' shift 516 - . reduce 288 (src line 1958) - - ocomma goto 517 - -state 400 - keyval_list: keyval. (280) - - . reduce 280 (src line 1925) - - -state 401 - keyval_list: bare_complitexpr. (281) - - . reduce 281 (src line 1930) - - -state 402 - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - keyval: expr.':' complitexpr - bare_complitexpr: expr. (142) - - LANDAND shift 134 - LANDNOT shift 149 - LCOMM shift 152 - LEQ shift 135 - LGE shift 139 - LGT shift 140 - LLE shift 138 - LLSH shift 150 - LLT shift 137 - LNE shift 136 - LOROR shift 133 - LRSH shift 151 - '+' shift 141 - '-' shift 142 - '|' shift 143 - '^' shift 144 - '*' shift 145 - '/' shift 146 - '%' shift 147 - '&' shift 148 - ':' shift 518 - . reduce 142 (src line 1088) - - -state 403 - bare_complitexpr: '{'.start_complit braced_keyval_list '}' - start_complit: . (140) - - . reduce 140 (src line 1075) - - start_complit goto 519 - -state 404 - pexpr_no_paren: '(' expr_or_type ')' '{'.start_complit braced_keyval_list '}' - start_complit: . (140) - - . reduce 140 (src line 1075) - - start_complit goto 520 - -state 405 - othertype: LCHAN LCOMM.ntype - recvchantype: LCOMM.LCHAN ntype - - LCHAN shift 299 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 232 - '*' shift 196 - '(' shift 231 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - ntype goto 311 - dotname goto 230 - name goto 197 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 229 - recvchantype goto 226 - othertype goto 228 - fntype goto 227 - hidden_importsym goto 11 - -state 406 - recvchantype: LCOMM LCHAN ntype. (199) - - . reduce 199 (src line 1343) - - -state 407 - ntype: fntype. (167) - non_recvchantype: fntype. (176) - - 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 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 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 1276) - '(' reduce 179 (src line 1276) - '{' reduce 179 (src line 1276) - . reduce 170 (src line 1257) - - -state 411 - ntype: '('.ntype ')' - non_recvchantype: '('.ntype ')' - - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 232 - '*' shift 196 - '(' shift 231 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - ntype goto 521 - dotname goto 230 - name goto 197 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 229 - recvchantype goto 226 - othertype goto 228 - fntype goto 227 - hidden_importsym goto 11 - -state 412 - pexpr_no_paren: convtype '(' expr ocomma.')' - - ')' shift 522 - . error - - -state 413 - ocomma: ','. (289) - - . reduce 289 (src line 1959) - - -state 414 - pexpr_no_paren: comptype lbrace start_complit braced_keyval_list.'}' - - '}' shift 523 - . error - - -state 415 - fnliteral: fnlitdcl lbrace stmt_list '}'. (216) - - . reduce 216 (src line 1552) - - -state 416 - stmt_list: stmt_list ';'.stmt - stmt: . (251) - - error shift 307 - LLITERAL shift 68 - LBREAK shift 41 - LCASE reduce 251 (src line 1781) - LCHAN shift 78 - LCONST shift 47 - LCONTINUE shift 42 - LDEFAULT reduce 251 (src line 1781) - LDEFER shift 44 - LFALL shift 40 - LFOR shift 50 - LFUNC shift 124 - LGO shift 43 - LGOTO shift 45 - LIF shift 53 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LRETURN shift 46 - LSELECT shift 52 - LSTRUCT shift 82 - LSWITCH shift 51 - LTYPE shift 32 - LVAR shift 30 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - ';' reduce 251 (src line 1781) - '{' shift 308 - '}' reduce 251 (src line 1781) - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 56 - stmt goto 524 - compound_stmt goto 304 - expr goto 48 - fnliteral goto 73 - for_stmt goto 35 - if_stmt goto 38 - non_dcl_stmt goto 306 - labelname goto 39 - name goto 69 - new_name goto 54 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - select_stmt goto 37 - simple_stmt goto 34 - switch_stmt goto 36 - uexpr goto 55 - expr_list goto 49 - common_dcl goto 305 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - lconst goto 31 - fnlitdcl goto 76 - -state 417 - compound_stmt: '{' $$59.stmt_list '}' - stmt: . (251) - - error shift 307 - LLITERAL shift 68 - LBREAK shift 41 - LCHAN shift 78 - LCONST shift 47 - LCONTINUE shift 42 - LDEFER shift 44 - LFALL shift 40 - LFOR shift 50 - LFUNC shift 124 - LGO shift 43 - LGOTO shift 45 - LIF shift 53 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LRETURN shift 46 - LSELECT shift 52 - LSTRUCT shift 82 - LSWITCH shift 51 - LTYPE shift 32 - LVAR shift 30 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - ';' reduce 251 (src line 1781) - '{' shift 308 - '}' reduce 251 (src line 1781) - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 56 - stmt goto 303 - compound_stmt goto 304 - expr goto 48 - fnliteral goto 73 - for_stmt goto 35 - if_stmt goto 38 - non_dcl_stmt goto 306 - labelname goto 39 - name goto 69 - new_name goto 54 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - select_stmt goto 37 - simple_stmt goto 34 - switch_stmt goto 36 - uexpr goto 55 - expr_list goto 49 - stmt_list goto 525 - common_dcl goto 305 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - lconst goto 31 - fnlitdcl goto 76 - -state 418 - othertype: '[' oexpr ']' ntype. (191) - - . reduce 191 (src line 1310) - - -state 419 - othertype: '[' LDDD ']' ntype. (192) - - . reduce 192 (src line 1315) - - -state 420 - non_recvchantype: '(' ntype ')'. (180) - - . reduce 180 (src line 1277) - - -state 421 - dotname: name '.' sym. (190) - - . reduce 190 (src line 1298) - - -state 422 - othertype: LMAP '[' ntype ']'.ntype - - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 232 - '*' shift 196 - '(' shift 231 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - ntype goto 526 - dotname goto 230 - name goto 197 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 229 - recvchantype goto 226 - othertype goto 228 - fntype goto 227 - hidden_importsym goto 11 - -state 423 - structtype: LSTRUCT lbrace structdcl_list osemi.'}' - - '}' shift 527 - . error - - -state 424 - structdcl_list: structdcl_list ';'.structdcl - osemi: ';'. (287) - - LNAME shift 325 - '*' shift 322 - '(' shift 321 - '?' shift 12 - '@' shift 13 - . reduce 287 (src line 1956) - - sym goto 119 - packname goto 324 - embed goto 320 - new_name goto 323 - new_name_list goto 319 - structdcl goto 528 - hidden_importsym goto 11 - -state 425 - structdcl: new_name_list ntype.oliteral - oliteral: . (302) - - LLITERAL shift 428 - . reduce 302 (src line 1997) - - oliteral goto 529 - -state 426 - new_name_list: new_name_list ','.new_name - - LNAME shift 10 - '?' shift 12 - '@' shift 13 - . error - - sym goto 119 - new_name goto 530 - hidden_importsym goto 11 - -state 427 - structdcl: embed oliteral. (231) - - . reduce 231 (src line 1651) - - -state 428 - oliteral: LLITERAL. (303) - - . reduce 303 (src line 2001) - - -state 429 - structdcl: '(' embed.')' oliteral - - ')' shift 531 - . error - - -state 430 - structdcl: '(' '*'.embed ')' oliteral - - LNAME shift 431 - . error - - packname goto 324 - embed goto 532 - -state 431 - packname: LNAME. (236) - packname: LNAME.'.' sym - - '.' shift 434 - . reduce 236 (src line 1683) - - -state 432 - structdcl: '*' embed.oliteral - oliteral: . (302) - - LLITERAL shift 428 - . reduce 302 (src line 1997) - - oliteral goto 533 - -state 433 - structdcl: '*' '('.embed ')' oliteral - - LNAME shift 431 - . error - - packname goto 324 - embed goto 534 - -state 434 - packname: LNAME '.'.sym - - LNAME shift 10 - '?' shift 12 - '@' shift 13 - . error - - sym goto 535 - hidden_importsym goto 11 - -state 435 - interfacetype: LINTERFACE lbrace interfacedcl_list osemi.'}' - - '}' shift 536 - . error - - -state 436 - interfacedcl_list: interfacedcl_list ';'.interfacedcl - osemi: ';'. (287) - - LNAME shift 325 - '(' shift 331 - '?' shift 12 - '@' shift 13 - . reduce 287 (src line 1956) - - sym goto 119 - packname goto 330 - interfacedcl goto 537 - new_name goto 329 - hidden_importsym goto 11 - -state 437 - interfacedcl: new_name indcl. (239) - - . reduce 239 (src line 1714) - - -state 438 - indcl: '('.oarg_type_list_ocomma ')' fnres - oarg_type_list_ocomma: . (249) - - LCHAN shift 78 - LDDD shift 250 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 232 - '*' shift 196 - '(' shift 231 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 249 (src line 1769) - - sym goto 247 - ntype goto 249 - arg_type goto 245 - dotname goto 230 - name goto 197 - name_or_type goto 246 - oarg_type_list_ocomma goto 538 - arg_type_list goto 244 - dotdotdot goto 248 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 229 - recvchantype goto 226 - othertype goto 228 - fntype goto 227 - hidden_importsym goto 11 - -state 439 - interfacedcl: '(' packname.')' - - ')' shift 539 - . error - - -state 440 - import: LIMPORT '(' import_stmt_list osemi ')'. (9) - - . reduce 9 (src line 232) - - -state 441 - import_stmt_list: import_stmt_list ';' import_stmt. (14) - - . reduce 14 (src line 284) - - -state 442 - hidden_import: LIMPORT LNAME LLITERAL ';'. (304) - - . reduce 304 (src line 2006) - - -state 443 - hidden_import: LVAR hidden_pkg_importsym hidden_type ';'. (305) - - . reduce 305 (src line 2011) - - -state 444 - hidden_type_misc: '[' ']'.hidden_type - - LCHAN shift 347 - LFUNC shift 349 - LINTERFACE shift 345 - LMAP shift 343 - LNAME shift 341 - LSTRUCT shift 344 - LCOMM shift 348 - '*' shift 346 - '[' shift 342 - '@' shift 13 - . error - - hidden_importsym goto 340 - hidden_type goto 540 - hidden_type_misc goto 337 - hidden_type_func goto 339 - hidden_type_recv_chan goto 338 - -state 445 - hidden_type_misc: '[' LLITERAL.']' hidden_type - - ']' shift 541 - . error - - -state 446 - hidden_type_misc: LMAP '['.hidden_type ']' hidden_type - - LCHAN shift 347 - LFUNC shift 349 - LINTERFACE shift 345 - LMAP shift 343 - LNAME shift 341 - LSTRUCT shift 344 - LCOMM shift 348 - '*' shift 346 - '[' shift 342 - '@' shift 13 - . error - - hidden_importsym goto 340 - hidden_type goto 542 - hidden_type_misc goto 337 - hidden_type_func goto 339 - hidden_type_recv_chan goto 338 - -state 447 - hidden_type_misc: LSTRUCT '{'.ohidden_structdcl_list '}' - ohidden_structdcl_list: . (298) - - LNAME shift 10 - '?' shift 12 - '@' shift 13 - . reduce 298 (src line 1985) - - sym goto 546 - hidden_importsym goto 11 - hidden_structdcl goto 545 - hidden_structdcl_list goto 544 - ohidden_structdcl_list goto 543 - -state 448 - hidden_type_misc: LINTERFACE '{'.ohidden_interfacedcl_list '}' - ohidden_interfacedcl_list: . (300) - - LCHAN shift 347 - LFUNC shift 349 - LINTERFACE shift 345 - LMAP shift 343 - LNAME shift 552 - LSTRUCT shift 344 - LCOMM shift 348 - '*' shift 346 - '[' shift 342 - '?' shift 12 - '@' shift 13 - . reduce 300 (src line 1991) - - sym goto 550 - hidden_importsym goto 553 - hidden_interfacedcl goto 549 - hidden_interfacedcl_list goto 548 - ohidden_interfacedcl_list goto 547 - hidden_type goto 551 - hidden_type_misc goto 337 - hidden_type_func goto 339 - hidden_type_recv_chan goto 338 - -state 449 - hidden_type_misc: '*' hidden_type. (324) - - . reduce 324 (src line 2110) - - -state 450 - hidden_type_misc: LCHAN hidden_type_non_recv_chan. (325) - - . reduce 325 (src line 2114) - - -state 451 - hidden_type_misc: LCHAN '('.hidden_type_recv_chan ')' - - LCOMM shift 348 - . error - - hidden_type_recv_chan goto 554 - -state 452 - hidden_type_misc: LCHAN LCOMM.hidden_type - - LCHAN shift 347 - LFUNC shift 349 - LINTERFACE shift 345 - LMAP shift 343 - LNAME shift 341 - LSTRUCT shift 344 - LCOMM shift 348 - '*' shift 346 - '[' shift 342 - '@' shift 13 - . error - - hidden_importsym goto 340 - hidden_type goto 555 - hidden_type_misc goto 337 - hidden_type_func goto 339 - hidden_type_recv_chan goto 338 - -state 453 - hidden_type_non_recv_chan: hidden_type_misc. (315) - - . reduce 315 (src line 2070) - - -state 454 - hidden_type_non_recv_chan: hidden_type_func. (316) - - . reduce 316 (src line 2072) - - -state 455 - hidden_type_recv_chan: LCOMM LCHAN.hidden_type - - LCHAN shift 347 - LFUNC shift 349 - LINTERFACE shift 345 - LMAP shift 343 - LNAME shift 341 - LSTRUCT shift 344 - LCOMM shift 348 - '*' shift 346 - '[' shift 342 - '@' shift 13 - . error - - hidden_importsym goto 340 - hidden_type goto 556 - hidden_type_misc goto 337 - hidden_type_func goto 339 - hidden_type_recv_chan goto 338 - -state 456 - hidden_type_func: LFUNC '('.ohidden_funarg_list ')' ohidden_funres - ohidden_funarg_list: . (296) - - LNAME shift 10 - '?' shift 12 - '@' shift 13 - . reduce 296 (src line 1979) - - sym goto 357 - hidden_importsym goto 11 - hidden_funarg goto 356 - hidden_funarg_list goto 467 - ohidden_funarg_list goto 557 - -state 457 - hidden_import: LCONST hidden_pkg_importsym '=' hidden_constant.';' - - ';' shift 558 - . error - - -state 458 - hidden_constant: hidden_literal. (342) - - . reduce 342 (src line 2257) - - -state 459 - hidden_constant: '('.hidden_literal '+' hidden_literal ')' - - LLITERAL shift 460 - LNAME shift 10 - '-' shift 461 - '?' shift 12 - '@' shift 13 - . error - - sym goto 462 - hidden_importsym goto 11 - hidden_literal goto 559 - -state 460 - hidden_literal: LLITERAL. (339) - - . reduce 339 (src line 2226) - - -state 461 - hidden_literal: '-'.LLITERAL - - LLITERAL shift 560 - . error - - -state 462 - hidden_literal: sym. (341) - - . reduce 341 (src line 2249) - - -state 463 - hidden_import: LCONST hidden_pkg_importsym hidden_type '='.hidden_constant ';' - - LLITERAL shift 460 - LNAME shift 10 - '-' shift 461 - '(' shift 459 - '?' shift 12 - '@' shift 13 - . error - - sym goto 462 - hidden_importsym goto 11 - hidden_constant goto 561 - hidden_literal goto 458 - -state 464 - hidden_import: LTYPE hidden_pkgtype hidden_type ';'. (308) - - . reduce 308 (src line 2023) - - -state 465 - hidden_import: LFUNC hidden_fndcl fnbody ';'. (309) - - . reduce 309 (src line 2027) - - -state 466 - hidden_fndcl: hidden_pkg_importsym '(' ohidden_funarg_list.')' ohidden_funres - - ')' shift 562 - . error - - -state 467 - ohidden_funarg_list: hidden_funarg_list. (297) - hidden_funarg_list: hidden_funarg_list.',' hidden_funarg - - ',' shift 469 - . reduce 297 (src line 1983) - - -state 468 - hidden_fndcl: '(' hidden_funarg_list ')'.sym '(' ohidden_funarg_list ')' ohidden_funres - - LNAME shift 10 - '?' shift 12 - '@' shift 13 - . error - - sym goto 563 - hidden_importsym goto 11 - -state 469 - hidden_funarg_list: hidden_funarg_list ','.hidden_funarg - - LNAME shift 10 - '?' shift 12 - '@' shift 13 - . error - - sym goto 357 - hidden_importsym goto 11 - hidden_funarg goto 564 - -state 470 - hidden_funarg: sym hidden_type.oliteral - oliteral: . (302) - - LLITERAL shift 428 - . reduce 302 (src line 1997) - - oliteral goto 565 - -state 471 - hidden_funarg: sym LDDD.hidden_type oliteral - - LCHAN shift 347 - LFUNC shift 349 - LINTERFACE shift 345 - LMAP shift 343 - LNAME shift 341 - LSTRUCT shift 344 - LCOMM shift 348 - '*' shift 346 - '[' shift 342 - '@' shift 13 - . error - - hidden_importsym goto 340 - hidden_type goto 566 - hidden_type_misc goto 337 - hidden_type_func goto 339 - hidden_type_recv_chan goto 338 - -state 472 - common_dcl: LVAR '(' vardcl_list osemi ')'. (29) - - . reduce 29 (src line 372) - - -state 473 - vardcl_list: vardcl_list ';' vardcl. (221) - - . reduce 221 (src line 1587) - - -state 474 - vardcl: dcl_name_list ntype '=' expr_list. (40) - expr_list: expr_list.',' expr - - ',' shift 155 - . reduce 40 (src line 427) - - -state 475 - ntype: '(' ntype ')'. (171) - - . reduce 171 (src line 1258) - - -state 476 - common_dcl: lconst '(' constdcl osemi ')'. (32) - - . reduce 32 (src line 386) - - -state 477 - common_dcl: lconst '(' constdcl ';' constdcl_list.osemi ')' - constdcl_list: constdcl_list.';' constdcl1 - osemi: . (286) - - ';' shift 568 - . reduce 286 (src line 1955) - - osemi goto 567 - -state 478 - constdcl_list: constdcl1. (222) - - . reduce 222 (src line 1592) - - -state 479 - constdcl1: constdcl. (44) - - . reduce 44 (src line 446) - - -state 480 - constdcl: dcl_name_list.ntype '=' expr_list - constdcl: dcl_name_list.'=' expr_list - constdcl1: dcl_name_list.ntype - constdcl1: dcl_name_list. (46) - dcl_name_list: dcl_name_list.',' dcl_name - - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 232 - '*' shift 196 - '(' shift 231 - '=' shift 236 - '[' shift 77 - '?' shift 12 - '@' shift 13 - ',' shift 225 - . reduce 46 (src line 452) - - sym goto 123 - ntype goto 569 - dotname goto 230 - name goto 197 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 229 - recvchantype goto 226 - othertype goto 228 - fntype goto 227 - hidden_importsym goto 11 - -state 481 - constdcl: dcl_name_list ntype '=' expr_list. (42) - expr_list: expr_list.',' expr - - ',' shift 155 - . reduce 42 (src line 436) - - -state 482 - common_dcl: LTYPE '(' typedcl_list osemi ')'. (36) - - . reduce 36 (src line 407) - - -state 483 - typedcl_list: typedcl_list ';' typedcl. (225) - - . reduce 225 (src line 1604) - - -state 484 - fnbody: '{' stmt_list '}'. (211) - - . reduce 211 (src line 1523) - - -state 485 - name: sym. (162) - fndcl: '(' oarg_type_list_ocomma ')' sym.'(' oarg_type_list_ocomma ')' fnres - - '(' shift 570 - . reduce 162 (src line 1220) - - -state 486 - fntype: LFUNC '(' oarg_type_list_ocomma ')' fnres. (209) - - . reduce 209 (src line 1510) - - -state 487 - fnres: fnret_type. (213) - - . reduce 213 (src line 1536) - - -state 488 - fnres: '('.oarg_type_list_ocomma ')' - oarg_type_list_ocomma: . (249) - - LCHAN shift 78 - LDDD shift 250 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 232 - '*' shift 196 - '(' shift 231 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 249 (src line 1769) - - sym goto 247 - ntype goto 249 - arg_type goto 245 - dotname goto 230 - name goto 197 - name_or_type goto 246 - oarg_type_list_ocomma goto 571 - arg_type_list goto 244 - dotdotdot goto 248 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 229 - recvchantype goto 226 - othertype goto 228 - fntype goto 227 - hidden_importsym goto 11 - -state 489 - fnret_type: recvchantype. (184) - - . reduce 184 (src line 1289) - - -state 490 - fnret_type: fntype. (185) - - . reduce 185 (src line 1291) - - -state 491 - fnret_type: othertype. (186) - - . reduce 186 (src line 1292) - - -state 492 - fnret_type: ptrtype. (187) - - . reduce 187 (src line 1293) - - -state 493 - fnret_type: dotname. (188) - - . reduce 188 (src line 1294) - - -state 494 - arg_type_list: arg_type_list ',' arg_type. (248) - - . reduce 248 (src line 1764) - - -state 495 - fndcl: sym '(' oarg_type_list_ocomma ')'.fnres - fnres: . (212) - - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 232 - '*' shift 196 - '(' shift 488 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 212 (src line 1531) - - sym goto 123 - dotname goto 493 - name goto 197 - fnres goto 572 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 492 - recvchantype goto 489 - othertype goto 491 - fnret_type goto 487 - fntype goto 490 - hidden_importsym goto 11 - -state 496 - fntype: LFUNC '(' oarg_type_list_ocomma ')'.fnres - fnres: . (212) - - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 232 - '*' shift 196 - '(' shift 488 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 212 (src line 1531) - - sym goto 123 - dotname goto 493 - name goto 197 - fnres goto 486 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 492 - recvchantype goto 489 - othertype goto 491 - fnret_type goto 487 - fntype goto 490 - hidden_importsym goto 11 - -state 497 - loop_body: LBODY $$65.stmt_list '}' - stmt: . (251) - - error shift 307 - LLITERAL shift 68 - LBREAK shift 41 - LCHAN shift 78 - LCONST shift 47 - LCONTINUE shift 42 - LDEFER shift 44 - LFALL shift 40 - LFOR shift 50 - LFUNC shift 124 - LGO shift 43 - LGOTO shift 45 - LIF shift 53 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LRETURN shift 46 - LSELECT shift 52 - LSTRUCT shift 82 - LSWITCH shift 51 - LTYPE shift 32 - LVAR shift 30 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - ';' reduce 251 (src line 1781) - '{' shift 308 - '}' reduce 251 (src line 1781) - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 56 - stmt goto 303 - compound_stmt goto 304 - expr goto 48 - fnliteral goto 73 - for_stmt goto 35 - if_stmt goto 38 - non_dcl_stmt goto 306 - labelname goto 39 - name goto 69 - new_name goto 54 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - select_stmt goto 37 - simple_stmt goto 34 - switch_stmt goto 36 - uexpr goto 55 - expr_list goto 49 - stmt_list goto 573 - common_dcl goto 305 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - lconst goto 31 - fnlitdcl goto 76 - -state 498 - for_header: osimple_stmt ';' osimple_stmt.';' osimple_stmt - - ';' shift 574 - . error - - -state 499 - range_stmt: expr_list '=' LRANGE.expr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 575 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 500 - range_stmt: expr_list LCOLAS LRANGE.expr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 576 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 501 - switch_stmt: LSWITCH $$88 if_header $$89 LBODY.caseblock_list '}' - caseblock_list: . (63) - - . reduce 63 (src line 652) - - caseblock_list goto 577 - -state 502 - if_header: osimple_stmt ';' osimple_stmt. (77) - - . reduce 77 (src line 739) - - -state 503 - caseblock_list: caseblock_list caseblock. (64) - - . reduce 64 (src line 656) - - -state 504 - select_stmt: LSELECT $$91 LBODY caseblock_list '}'. (92) - - . reduce 92 (src line 844) - - -state 505 - caseblock: case.$$61 stmt_list - $$61: . (61) - - . reduce 61 (src line 621) - - $$61 goto 578 - -state 506 - case: LCASE.expr_or_type_list ':' - case: LCASE.expr_or_type_list '=' expr ':' - case: LCASE.expr_or_type_list LCOLAS expr ':' - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 179 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 178 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 173 - expr_or_type goto 291 - fnliteral goto 73 - name goto 69 - non_expr_type goto 174 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - expr_or_type_list goto 579 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - recvchantype goto 175 - othertype goto 177 - fntype goto 176 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 507 - case: LDEFAULT.':' - - ':' shift 580 - . error - - -state 508 - if_stmt: LIF $$78 if_header $$79 loop_body.$$80 elseif_list else - $$80: . (80) - - . reduce 80 (src line 761) - - $$80 goto 581 - -state 509 - pseudocall: pexpr '(' expr_or_type_list ocomma ')'. (124) - - . reduce 124 (src line 991) - - -state 510 - pseudocall: pexpr '(' expr_or_type_list LDDD ocomma.')' - - ')' shift 582 - . error - - -state 511 - expr_or_type_list: expr_or_type_list ',' expr_or_type. (279) - - . reduce 279 (src line 1917) - - -state 512 - pexpr_no_paren: pexpr '.' '(' expr_or_type ')'. (129) - - . reduce 129 (src line 1020) - - -state 513 - pexpr_no_paren: pexpr '.' '(' LTYPE ')'. (130) - - . reduce 130 (src line 1024) - - -state 514 - pexpr_no_paren: pexpr '[' oexpr ':' oexpr.']' - pexpr_no_paren: pexpr '[' oexpr ':' oexpr.':' oexpr ']' - - ':' shift 584 - ']' shift 583 - . error - - -state 515 - pexpr_no_paren: pexpr_no_paren '{' start_complit braced_keyval_list '}'. (137) - - . reduce 137 (src line 1060) - - -state 516 - keyval_list: keyval_list ','.keyval - keyval_list: keyval_list ','.bare_complitexpr - ocomma: ','. (289) - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '{' shift 403 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 289 (src line 1959) - - sym goto 123 - expr goto 402 - bare_complitexpr goto 586 - fnliteral goto 73 - keyval goto 585 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 517 - braced_keyval_list: keyval_list ocomma. (285) - - . reduce 285 (src line 1947) - - -state 518 - keyval: expr ':'.complitexpr - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '{' shift 589 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 588 - complitexpr goto 587 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 519 - bare_complitexpr: '{' start_complit.braced_keyval_list '}' - braced_keyval_list: . (284) - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '{' shift 403 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 284 (src line 1943) - - sym goto 123 - expr goto 402 - bare_complitexpr goto 401 - fnliteral goto 73 - keyval goto 400 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - keyval_list goto 399 - braced_keyval_list goto 590 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 520 - pexpr_no_paren: '(' expr_or_type ')' '{' start_complit.braced_keyval_list '}' - braced_keyval_list: . (284) - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '{' shift 403 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 284 (src line 1943) - - sym goto 123 - expr goto 402 - bare_complitexpr goto 401 - fnliteral goto 73 - keyval goto 400 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - keyval_list goto 399 - braced_keyval_list goto 591 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 521 - ntype: '(' ntype.')' - non_recvchantype: '(' ntype.')' - - ')' shift 592 - . error - - -state 522 - pexpr_no_paren: convtype '(' expr ocomma ')'. (135) - - . reduce 135 (src line 1047) - - -state 523 - pexpr_no_paren: comptype lbrace start_complit braced_keyval_list '}'. (136) - - . reduce 136 (src line 1053) - - -state 524 - stmt_list: stmt_list ';' stmt. (271) - - . reduce 271 (src line 1874) - - -state 525 - compound_stmt: '{' $$59 stmt_list.'}' - stmt_list: stmt_list.';' stmt - - ';' shift 416 - '}' shift 593 - . error - - -state 526 - othertype: LMAP '[' ntype ']' ntype. (195) - - . reduce 195 (src line 1330) - - -state 527 - structtype: LSTRUCT lbrace structdcl_list osemi '}'. (200) - - . reduce 200 (src line 1350) - - -state 528 - structdcl_list: structdcl_list ';' structdcl. (227) - - . reduce 227 (src line 1611) - - -state 529 - structdcl: new_name_list ntype oliteral. (230) - - . reduce 230 (src line 1626) - - -state 530 - new_name_list: new_name_list ',' new_name. (273) - - . reduce 273 (src line 1887) - - -state 531 - structdcl: '(' embed ')'.oliteral - oliteral: . (302) - - LLITERAL shift 428 - . reduce 302 (src line 1997) - - oliteral goto 594 - -state 532 - structdcl: '(' '*' embed.')' oliteral - - ')' shift 595 - . error - - -state 533 - structdcl: '*' embed oliteral. (233) - - . reduce 233 (src line 1662) - - -state 534 - structdcl: '*' '(' embed.')' oliteral - - ')' shift 596 - . error - - -state 535 - packname: LNAME '.' sym. (237) - - . reduce 237 (src line 1694) - - -state 536 - interfacetype: LINTERFACE lbrace interfacedcl_list osemi '}'. (202) - - . reduce 202 (src line 1363) - - -state 537 - interfacedcl_list: interfacedcl_list ';' interfacedcl. (229) - - . reduce 229 (src line 1621) - - -state 538 - indcl: '(' oarg_type_list_ocomma.')' fnres - - ')' shift 597 - . error - - -state 539 - interfacedcl: '(' packname ')'. (241) - - . reduce 241 (src line 1724) - - -state 540 - hidden_type_misc: '[' ']' hidden_type. (319) - - . reduce 319 (src line 2090) - - -state 541 - hidden_type_misc: '[' LLITERAL ']'.hidden_type - - LCHAN shift 347 - LFUNC shift 349 - LINTERFACE shift 345 - LMAP shift 343 - LNAME shift 341 - LSTRUCT shift 344 - LCOMM shift 348 - '*' shift 346 - '[' shift 342 - '@' shift 13 - . error - - hidden_importsym goto 340 - hidden_type goto 598 - hidden_type_misc goto 337 - hidden_type_func goto 339 - hidden_type_recv_chan goto 338 - -state 542 - hidden_type_misc: LMAP '[' hidden_type.']' hidden_type - - ']' shift 599 - . error - - -state 543 - hidden_type_misc: LSTRUCT '{' ohidden_structdcl_list.'}' - - '}' shift 600 - . error - - -state 544 - ohidden_structdcl_list: hidden_structdcl_list. (299) - hidden_structdcl_list: hidden_structdcl_list.';' hidden_structdcl - - ';' shift 601 - . reduce 299 (src line 1989) - - -state 545 - hidden_structdcl_list: hidden_structdcl. (348) - - . reduce 348 (src line 2284) - - -state 546 - hidden_structdcl: sym.hidden_type oliteral - - LCHAN shift 347 - LFUNC shift 349 - LINTERFACE shift 345 - LMAP shift 343 - LNAME shift 341 - LSTRUCT shift 344 - LCOMM shift 348 - '*' shift 346 - '[' shift 342 - '@' shift 13 - . error - - hidden_importsym goto 340 - hidden_type goto 602 - hidden_type_misc goto 337 - hidden_type_func goto 339 - hidden_type_recv_chan goto 338 - -state 547 - hidden_type_misc: LINTERFACE '{' ohidden_interfacedcl_list.'}' - - '}' shift 603 - . error - - -state 548 - ohidden_interfacedcl_list: hidden_interfacedcl_list. (301) - hidden_interfacedcl_list: hidden_interfacedcl_list.';' hidden_interfacedcl - - ';' shift 604 - . reduce 301 (src line 1995) - - -state 549 - hidden_interfacedcl_list: hidden_interfacedcl. (350) - - . reduce 350 (src line 2294) - - -state 550 - hidden_interfacedcl: sym.'(' ohidden_funarg_list ')' ohidden_funres - - '(' shift 605 - . error - - -state 551 - hidden_interfacedcl: hidden_type. (334) - - . reduce 334 (src line 2201) - - -state 552 - sym: LNAME. (157) - hidden_type_misc: LNAME. (318) - - '(' 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 1184) - . reduce 317 (src line 2074) - - -state 554 - hidden_type_misc: LCHAN '(' hidden_type_recv_chan.')' - - ')' shift 606 - . error - - -state 555 - hidden_type_misc: LCHAN LCOMM hidden_type. (327) - - . reduce 327 (src line 2126) - - -state 556 - hidden_type_recv_chan: LCOMM LCHAN hidden_type. (328) - - . reduce 328 (src line 2133) - - -state 557 - hidden_type_func: LFUNC '(' ohidden_funarg_list.')' ohidden_funres - - ')' shift 607 - . error - - -state 558 - hidden_import: LCONST hidden_pkg_importsym '=' hidden_constant ';'. (306) - - . reduce 306 (src line 2015) - - -state 559 - hidden_constant: '(' hidden_literal.'+' hidden_literal ')' - - '+' shift 608 - . error - - -state 560 - hidden_literal: '-' LLITERAL. (340) - - . reduce 340 (src line 2231) - - -state 561 - hidden_import: LCONST hidden_pkg_importsym hidden_type '=' hidden_constant.';' - - ';' shift 609 - . error - - -state 562 - hidden_fndcl: hidden_pkg_importsym '(' ohidden_funarg_list ')'.ohidden_funres - ohidden_funres: . (335) - - LCHAN shift 347 - LFUNC shift 349 - LINTERFACE shift 345 - LMAP shift 343 - LNAME shift 341 - LSTRUCT shift 344 - LCOMM shift 348 - '*' shift 346 - '(' shift 612 - '[' shift 342 - '@' shift 13 - . reduce 335 (src line 2206) - - hidden_importsym goto 340 - hidden_funres goto 611 - ohidden_funres goto 610 - hidden_type goto 613 - hidden_type_misc goto 337 - hidden_type_func goto 339 - hidden_type_recv_chan goto 338 - -state 563 - hidden_fndcl: '(' hidden_funarg_list ')' sym.'(' ohidden_funarg_list ')' ohidden_funres - - '(' shift 614 - . error - - -state 564 - hidden_funarg_list: hidden_funarg_list ',' hidden_funarg. (347) - - . reduce 347 (src line 2279) - - -state 565 - hidden_funarg: sym hidden_type oliteral. (330) - - . reduce 330 (src line 2147) - - -state 566 - hidden_funarg: sym LDDD hidden_type.oliteral - oliteral: . (302) - - LLITERAL shift 428 - . reduce 302 (src line 1997) - - oliteral goto 615 - -state 567 - common_dcl: lconst '(' constdcl ';' constdcl_list osemi.')' - - ')' shift 616 - . error - - -state 568 - constdcl_list: constdcl_list ';'.constdcl1 - osemi: ';'. (287) - - LNAME shift 10 - '?' shift 12 - '@' shift 13 - . reduce 287 (src line 1956) - - sym goto 105 - dcl_name goto 104 - dcl_name_list goto 480 - constdcl goto 479 - constdcl1 goto 617 - hidden_importsym goto 11 - -state 569 - constdcl: dcl_name_list ntype.'=' expr_list - constdcl1: dcl_name_list ntype. (45) - - '=' shift 367 - . reduce 45 (src line 448) - - -state 570 - fndcl: '(' oarg_type_list_ocomma ')' sym '('.oarg_type_list_ocomma ')' fnres - oarg_type_list_ocomma: . (249) - - LCHAN shift 78 - LDDD shift 250 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 232 - '*' shift 196 - '(' shift 231 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 249 (src line 1769) - - sym goto 247 - ntype goto 249 - arg_type goto 245 - dotname goto 230 - name goto 197 - name_or_type goto 246 - oarg_type_list_ocomma goto 618 - arg_type_list goto 244 - dotdotdot goto 248 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 229 - recvchantype goto 226 - othertype goto 228 - fntype goto 227 - hidden_importsym goto 11 - -state 571 - fnres: '(' oarg_type_list_ocomma.')' - - ')' shift 619 - . error - - -state 572 - fndcl: sym '(' oarg_type_list_ocomma ')' fnres. (205) - - . reduce 205 (src line 1398) - - -state 573 - loop_body: LBODY $$65 stmt_list.'}' - stmt_list: stmt_list.';' stmt - - ';' shift 416 - '}' shift 620 - . error - - -state 574 - for_header: osimple_stmt ';' osimple_stmt ';'.osimple_stmt - osimple_stmt: . (294) - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 294 (src line 1973) - - sym goto 123 - expr goto 48 - fnliteral goto 73 - name goto 69 - osimple_stmt goto 621 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - simple_stmt goto 282 - uexpr goto 55 - expr_list goto 49 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 575 - range_stmt: expr_list '=' LRANGE expr. (67) - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - - LANDAND shift 134 - LANDNOT shift 149 - LCOMM shift 152 - LEQ shift 135 - LGE shift 139 - LGT shift 140 - LLE shift 138 - LLSH shift 150 - LLT shift 137 - LNE shift 136 - LOROR shift 133 - LRSH shift 151 - '+' shift 141 - '-' shift 142 - '|' shift 143 - '^' shift 144 - '*' shift 145 - '/' shift 146 - '%' shift 147 - '&' shift 148 - . reduce 67 (src line 672) - - -state 576 - range_stmt: expr_list LCOLAS LRANGE expr. (68) - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - - LANDAND shift 134 - LANDNOT shift 149 - LCOMM shift 152 - LEQ shift 135 - LGE shift 139 - LGT shift 140 - LLE shift 138 - LLSH shift 150 - LLT shift 137 - LNE shift 136 - LOROR shift 133 - LRSH shift 151 - '+' shift 141 - '-' shift 142 - '|' shift 143 - '^' shift 144 - '*' shift 145 - '/' shift 146 - '%' shift 147 - '&' shift 148 - . reduce 68 (src line 679) - - -state 577 - caseblock_list: caseblock_list.caseblock - switch_stmt: LSWITCH $$88 if_header $$89 LBODY caseblock_list.'}' - - LCASE shift 506 - LDEFAULT shift 507 - '}' shift 622 - . error - - case goto 505 - caseblock goto 503 - -state 578 - caseblock: case $$61.stmt_list - stmt: . (251) - - error shift 307 - LLITERAL shift 68 - LBREAK shift 41 - LCASE reduce 251 (src line 1781) - LCHAN shift 78 - LCONST shift 47 - LCONTINUE shift 42 - LDEFAULT reduce 251 (src line 1781) - LDEFER shift 44 - LFALL shift 40 - LFOR shift 50 - LFUNC shift 124 - LGO shift 43 - LGOTO shift 45 - LIF shift 53 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LRETURN shift 46 - LSELECT shift 52 - LSTRUCT shift 82 - LSWITCH shift 51 - LTYPE shift 32 - LVAR shift 30 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - ';' reduce 251 (src line 1781) - '{' shift 308 - '}' reduce 251 (src line 1781) - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 56 - stmt goto 303 - compound_stmt goto 304 - expr goto 48 - fnliteral goto 73 - for_stmt goto 35 - if_stmt goto 38 - non_dcl_stmt goto 306 - labelname goto 39 - name goto 69 - new_name goto 54 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - select_stmt goto 37 - simple_stmt goto 34 - switch_stmt goto 36 - uexpr goto 55 - expr_list goto 49 - stmt_list goto 623 - common_dcl goto 305 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - lconst goto 31 - fnlitdcl goto 76 - -state 579 - case: LCASE expr_or_type_list.':' - case: LCASE expr_or_type_list.'=' expr ':' - case: LCASE expr_or_type_list.LCOLAS expr ':' - expr_or_type_list: expr_or_type_list.',' expr_or_type - - LCOLAS shift 626 - '=' shift 625 - ':' shift 624 - ',' shift 627 - . error - - -state 580 - case: LDEFAULT ':'. (58) - - . 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 796) - - elseif_list goto 628 - -state 582 - pseudocall: pexpr '(' expr_or_type_list LDDD ocomma ')'. (125) - - . reduce 125 (src line 996) - - -state 583 - pexpr_no_paren: pexpr '[' oexpr ':' oexpr ']'. (132) - - . reduce 132 (src line 1032) - - -state 584 - pexpr_no_paren: pexpr '[' oexpr ':' oexpr ':'.oexpr ']' - oexpr: . (290) - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 290 (src line 1961) - - sym goto 123 - expr goto 188 - fnliteral goto 73 - name goto 69 - oexpr goto 629 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 585 - keyval_list: keyval_list ',' keyval. (282) - - . reduce 282 (src line 1934) - - -state 586 - keyval_list: keyval_list ',' bare_complitexpr. (283) - - . reduce 283 (src line 1938) - - -state 587 - keyval: expr ':' complitexpr. (141) - - . reduce 141 (src line 1082) - - -state 588 - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - complitexpr: expr. (144) - - LANDAND shift 134 - LANDNOT shift 149 - LCOMM shift 152 - LEQ shift 135 - LGE shift 139 - LGT shift 140 - LLE shift 138 - LLSH shift 150 - LLT shift 137 - LNE shift 136 - LOROR shift 133 - LRSH shift 151 - '+' shift 141 - '-' shift 142 - '|' shift 143 - '^' shift 144 - '*' shift 145 - '/' shift 146 - '%' shift 147 - '&' shift 148 - . reduce 144 (src line 1108) - - -state 589 - complitexpr: '{'.start_complit braced_keyval_list '}' - start_complit: . (140) - - . reduce 140 (src line 1075) - - start_complit goto 630 - -state 590 - bare_complitexpr: '{' start_complit braced_keyval_list.'}' - - '}' shift 631 - . error - - -state 591 - pexpr_no_paren: '(' expr_or_type ')' '{' start_complit braced_keyval_list.'}' - - '}' shift 632 - . error - - -state 592 - ntype: '(' ntype ')'. (171) - non_recvchantype: '(' ntype ')'. (180) - - 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 611) - - -state 594 - structdcl: '(' embed ')' oliteral. (232) - - . reduce 232 (src line 1656) - - -state 595 - structdcl: '(' '*' embed ')'.oliteral - oliteral: . (302) - - LLITERAL shift 428 - . reduce 302 (src line 1997) - - oliteral goto 633 - -state 596 - structdcl: '*' '(' embed ')'.oliteral - oliteral: . (302) - - LLITERAL shift 428 - . reduce 302 (src line 1997) - - oliteral goto 634 - -state 597 - indcl: '(' oarg_type_list_ocomma ')'.fnres - fnres: . (212) - - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 232 - '*' shift 196 - '(' shift 488 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 212 (src line 1531) - - sym goto 123 - dotname goto 493 - name goto 197 - fnres goto 635 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 492 - recvchantype goto 489 - othertype goto 491 - fnret_type goto 487 - fntype goto 490 - hidden_importsym goto 11 - -state 598 - hidden_type_misc: '[' LLITERAL ']' hidden_type. (320) - - . reduce 320 (src line 2094) - - -state 599 - hidden_type_misc: LMAP '[' hidden_type ']'.hidden_type - - LCHAN shift 347 - LFUNC shift 349 - LINTERFACE shift 345 - LMAP shift 343 - LNAME shift 341 - LSTRUCT shift 344 - LCOMM shift 348 - '*' shift 346 - '[' shift 342 - '@' shift 13 - . error - - hidden_importsym goto 340 - hidden_type goto 636 - hidden_type_misc goto 337 - hidden_type_func goto 339 - hidden_type_recv_chan goto 338 - -state 600 - hidden_type_misc: LSTRUCT '{' ohidden_structdcl_list '}'. (322) - - . reduce 322 (src line 2102) - - -state 601 - hidden_structdcl_list: hidden_structdcl_list ';'.hidden_structdcl - - LNAME shift 10 - '?' shift 12 - '@' shift 13 - . error - - sym goto 546 - hidden_importsym goto 11 - hidden_structdcl goto 637 - -state 602 - hidden_structdcl: sym hidden_type.oliteral - oliteral: . (302) - - LLITERAL shift 428 - . reduce 302 (src line 1997) - - oliteral goto 638 - -state 603 - hidden_type_misc: LINTERFACE '{' ohidden_interfacedcl_list '}'. (323) - - . reduce 323 (src line 2106) - - -state 604 - hidden_interfacedcl_list: hidden_interfacedcl_list ';'.hidden_interfacedcl - - LCHAN shift 347 - LFUNC shift 349 - LINTERFACE shift 345 - LMAP shift 343 - LNAME shift 552 - LSTRUCT shift 344 - LCOMM shift 348 - '*' shift 346 - '[' shift 342 - '?' shift 12 - '@' shift 13 - . error - - sym goto 550 - hidden_importsym goto 553 - hidden_interfacedcl goto 639 - hidden_type goto 551 - hidden_type_misc goto 337 - hidden_type_func goto 339 - hidden_type_recv_chan goto 338 - -state 605 - hidden_interfacedcl: sym '('.ohidden_funarg_list ')' ohidden_funres - ohidden_funarg_list: . (296) - - LNAME shift 10 - '?' shift 12 - '@' shift 13 - . reduce 296 (src line 1979) - - sym goto 357 - hidden_importsym goto 11 - hidden_funarg goto 356 - hidden_funarg_list goto 467 - ohidden_funarg_list goto 640 - -state 606 - hidden_type_misc: LCHAN '(' hidden_type_recv_chan ')'. (326) - - . reduce 326 (src line 2120) - - -state 607 - hidden_type_func: LFUNC '(' ohidden_funarg_list ')'.ohidden_funres - ohidden_funres: . (335) - - LCHAN shift 347 - LFUNC shift 349 - LINTERFACE shift 345 - LMAP shift 343 - LNAME shift 341 - LSTRUCT shift 344 - LCOMM shift 348 - '*' shift 346 - '(' shift 612 - '[' shift 342 - '@' shift 13 - . reduce 335 (src line 2206) - - hidden_importsym goto 340 - hidden_funres goto 611 - ohidden_funres goto 641 - hidden_type goto 613 - hidden_type_misc goto 337 - hidden_type_func goto 339 - hidden_type_recv_chan goto 338 - -state 608 - hidden_constant: '(' hidden_literal '+'.hidden_literal ')' - - LLITERAL shift 460 - LNAME shift 10 - '-' shift 461 - '?' shift 12 - '@' shift 13 - . error - - sym goto 462 - hidden_importsym goto 11 - hidden_literal goto 642 - -state 609 - hidden_import: LCONST hidden_pkg_importsym hidden_type '=' hidden_constant ';'. (307) - - . reduce 307 (src line 2019) - - -state 610 - hidden_fndcl: hidden_pkg_importsym '(' ohidden_funarg_list ')' ohidden_funres. (207) - - . reduce 207 (src line 1467) - - -state 611 - ohidden_funres: hidden_funres. (336) - - . reduce 336 (src line 2210) - - -state 612 - hidden_funres: '('.ohidden_funarg_list ')' - ohidden_funarg_list: . (296) - - LNAME shift 10 - '?' shift 12 - '@' shift 13 - . reduce 296 (src line 1979) - - sym goto 357 - hidden_importsym goto 11 - hidden_funarg goto 356 - hidden_funarg_list goto 467 - ohidden_funarg_list goto 643 - -state 613 - hidden_funres: hidden_type. (338) - - . reduce 338 (src line 2217) - - -state 614 - hidden_fndcl: '(' hidden_funarg_list ')' sym '('.ohidden_funarg_list ')' ohidden_funres - ohidden_funarg_list: . (296) - - LNAME shift 10 - '?' shift 12 - '@' shift 13 - . reduce 296 (src line 1979) - - sym goto 357 - hidden_importsym goto 11 - hidden_funarg goto 356 - hidden_funarg_list goto 467 - ohidden_funarg_list goto 644 - -state 615 - hidden_funarg: sym LDDD hidden_type oliteral. (331) - - . reduce 331 (src line 2156) - - -state 616 - common_dcl: lconst '(' constdcl ';' constdcl_list osemi ')'. (33) - - . reduce 33 (src line 392) - - -state 617 - constdcl_list: constdcl_list ';' constdcl1. (223) - - . reduce 223 (src line 1594) - - -state 618 - fndcl: '(' oarg_type_list_ocomma ')' sym '(' oarg_type_list_ocomma.')' fnres - - ')' shift 645 - . error - - -state 619 - fnres: '(' oarg_type_list_ocomma ')'. (214) - - . reduce 214 (src line 1540) - - -state 620 - loop_body: LBODY $$65 stmt_list '}'. (66) - - . reduce 66 (src line 666) - - -state 621 - for_header: osimple_stmt ';' osimple_stmt ';' osimple_stmt. (70) - - . reduce 70 (src line 692) - - -state 622 - switch_stmt: LSWITCH $$88 if_header $$89 LBODY caseblock_list '}'. (90) - - . reduce 90 (src line 830) - - -state 623 - caseblock: case $$61 stmt_list. (62) - stmt_list: stmt_list.';' stmt - - ';' shift 416 - . reduce 62 (src line 633) - - -state 624 - case: LCASE expr_or_type_list ':'. (55) - - . reduce 55 (src line 535) - - -state 625 - case: LCASE expr_or_type_list '='.expr ':' - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 646 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 626 - case: LCASE expr_or_type_list LCOLAS.expr ':' - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 647 - fnliteral goto 73 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 627 - expr_or_type_list: expr_or_type_list ','.expr_or_type - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 179 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 178 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . error - - sym goto 123 - expr goto 173 - expr_or_type goto 511 - fnliteral goto 73 - name goto 69 - non_expr_type goto 174 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - recvchantype goto 175 - othertype goto 177 - fntype goto 176 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 628 - if_stmt: LIF $$78 if_header $$79 loop_body $$80 elseif_list.else - elseif_list: elseif_list.elseif - else: . (86) - - LELSE shift 650 - . reduce 86 (src line 805) - - elseif goto 649 - else goto 648 - -state 629 - pexpr_no_paren: pexpr '[' oexpr ':' oexpr ':' oexpr.']' - - ']' shift 651 - . error - - -state 630 - complitexpr: '{' start_complit.braced_keyval_list '}' - braced_keyval_list: . (284) - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '{' shift 403 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 284 (src line 1943) - - sym goto 123 - expr goto 402 - bare_complitexpr goto 401 - fnliteral goto 73 - keyval goto 400 - name goto 69 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - uexpr goto 55 - keyval_list goto 399 - braced_keyval_list goto 652 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 631 - bare_complitexpr: '{' start_complit braced_keyval_list '}'. (143) - - . reduce 143 (src line 1102) - - -state 632 - pexpr_no_paren: '(' expr_or_type ')' '{' start_complit braced_keyval_list '}'. (138) - - . reduce 138 (src line 1066) - - -state 633 - structdcl: '(' '*' embed ')' oliteral. (234) - - . reduce 234 (src line 1668) - - -state 634 - structdcl: '*' '(' embed ')' oliteral. (235) - - . reduce 235 (src line 1675) - - -state 635 - indcl: '(' oarg_type_list_ocomma ')' fnres. (242) - - . reduce 242 (src line 1730) - - -state 636 - hidden_type_misc: LMAP '[' hidden_type ']' hidden_type. (321) - - . reduce 321 (src line 2098) - - -state 637 - hidden_structdcl_list: hidden_structdcl_list ';' hidden_structdcl. (349) - - . reduce 349 (src line 2289) - - -state 638 - hidden_structdcl: sym hidden_type oliteral. (332) - - . reduce 332 (src line 2172) - - -state 639 - hidden_interfacedcl_list: hidden_interfacedcl_list ';' hidden_interfacedcl. (351) - - . reduce 351 (src line 2299) - - -state 640 - hidden_interfacedcl: sym '(' ohidden_funarg_list.')' ohidden_funres - - ')' shift 653 - . error - - -state 641 - hidden_type_func: LFUNC '(' ohidden_funarg_list ')' ohidden_funres. (329) - - . reduce 329 (src line 2141) - - -state 642 - hidden_constant: '(' hidden_literal '+' hidden_literal.')' - - ')' shift 654 - . error - - -state 643 - hidden_funres: '(' ohidden_funarg_list.')' - - ')' shift 655 - . error - - -state 644 - hidden_fndcl: '(' hidden_funarg_list ')' sym '(' ohidden_funarg_list.')' ohidden_funres - - ')' shift 656 - . error - - -state 645 - fndcl: '(' oarg_type_list_ocomma ')' sym '(' oarg_type_list_ocomma ')'.fnres - fnres: . (212) - - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 232 - '*' shift 196 - '(' shift 488 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 212 (src line 1531) - - sym goto 123 - dotname goto 493 - name goto 197 - fnres goto 657 - interfacetype goto 81 - structtype goto 80 - ptrtype goto 492 - recvchantype goto 489 - othertype goto 491 - fnret_type goto 487 - fntype goto 490 - hidden_importsym goto 11 - -state 646 - case: LCASE expr_or_type_list '=' expr.':' - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - - LANDAND shift 134 - LANDNOT shift 149 - LCOMM shift 152 - LEQ shift 135 - LGE shift 139 - LGT shift 140 - LLE shift 138 - LLSH shift 150 - LLT shift 137 - LNE shift 136 - LOROR shift 133 - LRSH shift 151 - '+' shift 141 - '-' shift 142 - '|' shift 143 - '^' shift 144 - '*' shift 145 - '/' shift 146 - '%' shift 147 - '&' shift 148 - ':' shift 658 - . error - - -state 647 - case: LCASE expr_or_type_list LCOLAS expr.':' - expr: expr.LOROR expr - expr: expr.LANDAND expr - expr: expr.LEQ expr - expr: expr.LNE expr - expr: expr.LLT expr - expr: expr.LLE expr - expr: expr.LGE expr - expr: expr.LGT expr - expr: expr.'+' expr - expr: expr.'-' expr - expr: expr.'|' expr - expr: expr.'^' expr - expr: expr.'*' expr - expr: expr.'/' expr - expr: expr.'%' expr - expr: expr.'&' expr - expr: expr.LANDNOT expr - expr: expr.LLSH expr - expr: expr.LRSH expr - expr: expr.LCOMM expr - - LANDAND shift 134 - LANDNOT shift 149 - LCOMM shift 152 - LEQ shift 135 - LGE shift 139 - LGT shift 140 - LLE shift 138 - LLSH shift 150 - LLT shift 137 - LNE shift 136 - LOROR shift 133 - LRSH shift 151 - '+' shift 141 - '-' shift 142 - '|' shift 143 - '^' shift 144 - '*' shift 145 - '/' shift 146 - '%' shift 147 - '&' shift 148 - ':' shift 659 - . error - - -state 648 - if_stmt: LIF $$78 if_header $$79 loop_body $$80 elseif_list else. (81) - - . reduce 81 (src line 765) - - -state 649 - elseif_list: elseif_list elseif. (85) - - . reduce 85 (src line 800) - - -state 650 - elseif: LELSE.LIF $$82 if_header loop_body - else: LELSE.compound_stmt - - LIF shift 660 - '{' shift 308 - . error - - compound_stmt goto 661 - -state 651 - pexpr_no_paren: pexpr '[' oexpr ':' oexpr ':' oexpr ']'. (133) - - . reduce 133 (src line 1036) - - -state 652 - complitexpr: '{' start_complit braced_keyval_list.'}' - - '}' shift 662 - . error - - -state 653 - hidden_interfacedcl: sym '(' ohidden_funarg_list ')'.ohidden_funres - ohidden_funres: . (335) - - LCHAN shift 347 - LFUNC shift 349 - LINTERFACE shift 345 - LMAP shift 343 - LNAME shift 341 - LSTRUCT shift 344 - LCOMM shift 348 - '*' shift 346 - '(' shift 612 - '[' shift 342 - '@' shift 13 - . reduce 335 (src line 2206) - - hidden_importsym goto 340 - hidden_funres goto 611 - ohidden_funres goto 663 - hidden_type goto 613 - hidden_type_misc goto 337 - hidden_type_func goto 339 - hidden_type_recv_chan goto 338 - -state 654 - hidden_constant: '(' hidden_literal '+' hidden_literal ')'. (343) - - . reduce 343 (src line 2259) - - -state 655 - hidden_funres: '(' ohidden_funarg_list ')'. (337) - - . reduce 337 (src line 2212) - - -state 656 - hidden_fndcl: '(' hidden_funarg_list ')' sym '(' ohidden_funarg_list ')'.ohidden_funres - ohidden_funres: . (335) - - LCHAN shift 347 - LFUNC shift 349 - LINTERFACE shift 345 - LMAP shift 343 - LNAME shift 341 - LSTRUCT shift 344 - LCOMM shift 348 - '*' shift 346 - '(' shift 612 - '[' shift 342 - '@' shift 13 - . reduce 335 (src line 2206) - - hidden_importsym goto 340 - hidden_funres goto 611 - ohidden_funres goto 664 - hidden_type goto 613 - hidden_type_misc goto 337 - hidden_type_func goto 339 - hidden_type_recv_chan goto 338 - -state 657 - fndcl: '(' oarg_type_list_ocomma ')' sym '(' oarg_type_list_ocomma ')' fnres. (206) - - . reduce 206 (src line 1430) - - -state 658 - case: LCASE expr_or_type_list '=' expr ':'. (56) - - . reduce 56 (src line 559) - - -state 659 - case: LCASE expr_or_type_list LCOLAS expr ':'. (57) - - . reduce 57 (src line 577) - - -state 660 - elseif: LELSE LIF.$$82 if_header loop_body - $$82: . (82) - - . reduce 82 (src line 782) - - $$82 goto 665 - -state 661 - else: LELSE compound_stmt. (87) - - . reduce 87 (src line 809) - - -state 662 - complitexpr: '{' start_complit braced_keyval_list '}'. (145) - - . reduce 145 (src line 1110) - - -state 663 - hidden_interfacedcl: sym '(' ohidden_funarg_list ')' ohidden_funres. (333) - - . reduce 333 (src line 2196) - - -state 664 - hidden_fndcl: '(' hidden_funarg_list ')' sym '(' ohidden_funarg_list ')' ohidden_funres. (208) - - . reduce 208 (src line 1493) - - -state 665 - elseif: LELSE LIF $$82.if_header loop_body - osimple_stmt: . (294) - - LLITERAL shift 68 - LCHAN shift 78 - LFUNC shift 124 - LINTERFACE shift 83 - LMAP shift 79 - LNAME shift 10 - LSTRUCT shift 82 - LCOMM shift 65 - '+' shift 60 - '-' shift 61 - '^' shift 64 - '*' shift 58 - '&' shift 59 - '(' shift 67 - '!' shift 62 - '~' shift 63 - '[' shift 77 - '?' shift 12 - '@' shift 13 - . reduce 294 (src line 1973) - - sym goto 123 - expr goto 48 - fnliteral goto 73 - if_header goto 666 - name goto 69 - osimple_stmt goto 286 - pexpr goto 57 - pexpr_no_paren goto 66 - pseudocall goto 70 - simple_stmt goto 282 - uexpr goto 55 - expr_list goto 49 - convtype goto 71 - comptype goto 72 - interfacetype goto 81 - structtype goto 80 - othertype goto 75 - fntype goto 74 - hidden_importsym goto 11 - fnlitdcl goto 76 - -state 666 - elseif: LELSE LIF $$82 if_header.loop_body - - LBODY shift 382 - . error - - loop_body goto 667 - -state 667 - elseif: LELSE LIF $$82 if_header loop_body. (83) - - . reduce 83 (src line 787) - - -76 terminals, 142 nonterminals -352 grammar rules, 668/2000 states -0 shift/reduce, 0 reduce/reduce conflicts reported -191 working sets used -memory: parser 3749/30000 -446 extra closures -3093 shift entries, 64 exceptions -603 goto entries -1650 entries saved by goto default -Optimizer space used: output 2282/30000 -2282 table entries, 722 zero -maximum spread: 76, maximum offset: 666 diff --git a/src/cmd/internal/ld/ar.go b/src/cmd/internal/ld/ar.go deleted file mode 100644 index 0e59016edb..0000000000 --- a/src/cmd/internal/ld/ar.go +++ /dev/null @@ -1,52 +0,0 @@ -// Inferno utils/include/ar.h -// http://code.google.com/p/inferno-os/source/browse/utils/include/ar.h -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package ld - -const ( - SARMAG = 8 - SARNAME = 16 - SAR_HDR = 16 + 44 -) - -const ( - ARMAG = "!\n" - ARFMAG = "`\n" -) - -type ArHdr struct { - name string - date string - uid string - gid string - mode string - size string - fmag string -} diff --git a/src/cmd/internal/ld/arch.go b/src/cmd/internal/ld/arch.go deleted file mode 100644 index 1b8e1b19ba..0000000000 --- a/src/cmd/internal/ld/arch.go +++ /dev/null @@ -1,70 +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 "encoding/binary" - -var Linkarm = LinkArch{ - ByteOrder: binary.LittleEndian, - Name: "arm", - Thechar: '5', - Minlc: 4, - Ptrsize: 4, - Regsize: 4, -} - -var Linkarm64 = LinkArch{ - ByteOrder: binary.LittleEndian, - Name: "arm64", - Thechar: '7', - Minlc: 4, - Ptrsize: 8, - Regsize: 8, -} - -var Linkamd64 = LinkArch{ - ByteOrder: binary.LittleEndian, - Name: "amd64", - Thechar: '6', - Minlc: 1, - Ptrsize: 8, - Regsize: 8, -} - -var Linkamd64p32 = LinkArch{ - ByteOrder: binary.LittleEndian, - Name: "amd64p32", - Thechar: '6', - Minlc: 1, - Ptrsize: 4, - Regsize: 8, -} - -var Link386 = LinkArch{ - ByteOrder: binary.LittleEndian, - Name: "386", - Thechar: '8', - Minlc: 1, - Ptrsize: 4, - Regsize: 4, -} - -var Linkppc64 = LinkArch{ - ByteOrder: binary.BigEndian, - Name: "ppc64", - Thechar: '9', - Minlc: 4, - Ptrsize: 8, - Regsize: 8, -} - -var Linkppc64le = LinkArch{ - ByteOrder: binary.LittleEndian, - Name: "ppc64le", - Thechar: '9', - Minlc: 4, - Ptrsize: 8, - Regsize: 8, -} diff --git a/src/cmd/internal/ld/data.go b/src/cmd/internal/ld/data.go deleted file mode 100644 index f9aacf0e19..0000000000 --- a/src/cmd/internal/ld/data.go +++ /dev/null @@ -1,1721 +0,0 @@ -// Derived from Inferno utils/6l/obj.c and utils/6l/span.c -// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c -// http://code.google.com/p/inferno-os/source/browse/utils/6l/span.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package ld - -import ( - "cmd/internal/gcprog" - "cmd/internal/obj" - "fmt" - "log" - "os" - "strings" -) - -func Symgrow(ctxt *Link, s *LSym, siz int64) { - if int64(int(siz)) != siz { - log.Fatalf("symgrow size %d too long", siz) - } - if int64(len(s.P)) >= siz { - return - } - for cap(s.P) < int(siz) { - s.P = append(s.P[:len(s.P)], 0) - } - s.P = s.P[:siz] -} - -func Addrel(s *LSym) *Reloc { - s.R = append(s.R, Reloc{}) - return &s.R[len(s.R)-1] -} - -func setuintxx(ctxt *Link, s *LSym, off int64, v uint64, wid int64) int64 { - if s.Type == 0 { - s.Type = obj.SDATA - } - s.Reachable = true - if s.Size < off+wid { - s.Size = off + wid - Symgrow(ctxt, s, s.Size) - } - - switch wid { - case 1: - s.P[off] = uint8(v) - case 2: - ctxt.Arch.ByteOrder.PutUint16(s.P[off:], uint16(v)) - case 4: - ctxt.Arch.ByteOrder.PutUint32(s.P[off:], uint32(v)) - case 8: - ctxt.Arch.ByteOrder.PutUint64(s.P[off:], uint64(v)) - } - - return off + wid -} - -func adduintxx(ctxt *Link, s *LSym, v uint64, wid int) int64 { - off := s.Size - setuintxx(ctxt, s, off, v, int64(wid)) - return off -} - -func Adduint8(ctxt *Link, s *LSym, v uint8) int64 { - return adduintxx(ctxt, s, uint64(v), 1) -} - -func Adduint16(ctxt *Link, s *LSym, v uint16) int64 { - return adduintxx(ctxt, s, uint64(v), 2) -} - -func Adduint32(ctxt *Link, s *LSym, v uint32) int64 { - return adduintxx(ctxt, s, uint64(v), 4) -} - -func Adduint64(ctxt *Link, s *LSym, v uint64) int64 { - return adduintxx(ctxt, s, v, 8) -} - -func adduint(ctxt *Link, s *LSym, v uint64) int64 { - return adduintxx(ctxt, s, v, Thearch.Intsize) -} - -func setuint8(ctxt *Link, s *LSym, r int64, v uint8) int64 { - return setuintxx(ctxt, s, r, uint64(v), 1) -} - -func setuint32(ctxt *Link, s *LSym, r int64, v uint32) int64 { - return setuintxx(ctxt, s, r, uint64(v), 4) -} - -func Addaddrplus(ctxt *Link, s *LSym, t *LSym, add int64) int64 { - if s.Type == 0 { - s.Type = obj.SDATA - } - s.Reachable = true - i := s.Size - s.Size += int64(ctxt.Arch.Ptrsize) - Symgrow(ctxt, s, s.Size) - r := Addrel(s) - r.Sym = t - r.Off = int32(i) - r.Siz = uint8(ctxt.Arch.Ptrsize) - r.Type = obj.R_ADDR - r.Add = add - return i + int64(r.Siz) -} - -func Addpcrelplus(ctxt *Link, s *LSym, t *LSym, add int64) int64 { - if s.Type == 0 { - s.Type = obj.SDATA - } - s.Reachable = true - i := s.Size - s.Size += 4 - Symgrow(ctxt, s, s.Size) - r := Addrel(s) - r.Sym = t - r.Off = int32(i) - r.Add = add - r.Type = obj.R_PCREL - r.Siz = 4 - return i + int64(r.Siz) -} - -func Addaddr(ctxt *Link, s *LSym, t *LSym) int64 { - return Addaddrplus(ctxt, s, t, 0) -} - -func setaddrplus(ctxt *Link, s *LSym, off int64, t *LSym, add int64) int64 { - if s.Type == 0 { - s.Type = obj.SDATA - } - s.Reachable = true - if off+int64(ctxt.Arch.Ptrsize) > s.Size { - s.Size = off + int64(ctxt.Arch.Ptrsize) - Symgrow(ctxt, s, s.Size) - } - - r := Addrel(s) - r.Sym = t - r.Off = int32(off) - r.Siz = uint8(ctxt.Arch.Ptrsize) - r.Type = obj.R_ADDR - r.Add = add - return off + int64(r.Siz) -} - -func setaddr(ctxt *Link, s *LSym, off int64, t *LSym) int64 { - return setaddrplus(ctxt, s, off, t, 0) -} - -func addsize(ctxt *Link, s *LSym, t *LSym) int64 { - if s.Type == 0 { - s.Type = obj.SDATA - } - s.Reachable = true - i := s.Size - s.Size += int64(ctxt.Arch.Ptrsize) - Symgrow(ctxt, s, s.Size) - r := Addrel(s) - r.Sym = t - r.Off = int32(i) - r.Siz = uint8(ctxt.Arch.Ptrsize) - r.Type = obj.R_SIZE - return i + int64(r.Siz) -} - -func addaddrplus4(ctxt *Link, s *LSym, t *LSym, add int64) int64 { - if s.Type == 0 { - s.Type = obj.SDATA - } - s.Reachable = true - i := s.Size - s.Size += 4 - Symgrow(ctxt, s, s.Size) - r := Addrel(s) - r.Sym = t - r.Off = int32(i) - r.Siz = 4 - r.Type = obj.R_ADDR - r.Add = add - return i + int64(r.Siz) -} - -/* - * divide-and-conquer list-link - * sort of LSym* structures. - * Used for the data block. - */ -func datcmp(s1 *LSym, s2 *LSym) int { - if s1.Type != s2.Type { - return int(s1.Type) - int(s2.Type) - } - - // For ppc64, we want to interleave the .got and .toc sections - // from input files. Both are type SELFGOT, so in that case - // fall through to the name comparison (conveniently, .got - // sorts before .toc). - if s1.Type != obj.SELFGOT && s1.Size != s2.Size { - if s1.Size < s2.Size { - return -1 - } - return +1 - } - - return stringsCompare(s1.Name, s2.Name) -} - -func listnextp(s *LSym) **LSym { - return &s.Next -} - -func listsubp(s *LSym) **LSym { - return &s.Sub -} - -func listsort(l *LSym, cmp func(*LSym, *LSym) int, nextp func(*LSym) **LSym) *LSym { - if l == nil || *nextp(l) == nil { - return l - } - - l1 := l - l2 := l - for { - l2 = *nextp(l2) - if l2 == nil { - break - } - l2 = *nextp(l2) - if l2 == nil { - break - } - l1 = *nextp(l1) - } - - l2 = *nextp(l1) - *nextp(l1) = nil - l1 = listsort(l, cmp, nextp) - l2 = listsort(l2, cmp, nextp) - - /* set up lead element */ - if cmp(l1, l2) < 0 { - l = l1 - l1 = *nextp(l1) - } else { - l = l2 - l2 = *nextp(l2) - } - - le := l - - for { - if l1 == nil { - for l2 != nil { - *nextp(le) = l2 - le = l2 - l2 = *nextp(l2) - } - - *nextp(le) = nil - break - } - - if l2 == nil { - for l1 != nil { - *nextp(le) = l1 - le = l1 - l1 = *nextp(l1) - } - - break - } - - if cmp(l1, l2) < 0 { - *nextp(le) = l1 - le = l1 - l1 = *nextp(l1) - } else { - *nextp(le) = l2 - le = l2 - l2 = *nextp(l2) - } - } - - *nextp(le) = nil - return l -} - -func relocsym(s *LSym) { - var r *Reloc - var rs *LSym - var i16 int16 - var off int32 - var siz int32 - var fl int32 - var o int64 - - Ctxt.Cursym = s - for ri := int32(0); ri < int32(len(s.R)); ri++ { - r = &s.R[ri] - r.Done = 1 - off = r.Off - siz = int32(r.Siz) - if off < 0 || off+siz > int32(len(s.P)) { - Diag("%s: invalid relocation %d+%d not in [%d,%d)", s.Name, off, siz, 0, len(s.P)) - continue - } - - if r.Sym != nil && (r.Sym.Type&(obj.SMASK|obj.SHIDDEN) == 0 || r.Sym.Type&obj.SMASK == obj.SXREF) { - // When putting the runtime but not main into a shared library - // these symbols are undefined and that's OK. - if Buildmode == BuildmodeShared && (r.Sym.Name == "main.main" || r.Sym.Name == "main.init") { - r.Sym.Type = obj.SDYNIMPORT - } else { - Diag("%s: not defined", r.Sym.Name) - continue - } - } - - if r.Type >= 256 { - continue - } - if r.Siz == 0 { // informational relocation - no work to do - continue - } - - // We need to be able to reference dynimport symbols when linking against - // shared libraries, and Solaris needs it always - if HEADTYPE != obj.Hsolaris && r.Sym != nil && r.Sym.Type == obj.SDYNIMPORT && !DynlinkingGo() { - Diag("unhandled relocation for %s (type %d rtype %d)", r.Sym.Name, r.Sym.Type, r.Type) - } - if r.Sym != nil && r.Sym.Type != obj.STLSBSS && !r.Sym.Reachable { - Diag("unreachable sym in relocation: %s %s", s.Name, r.Sym.Name) - } - - // Android emulates runtime.tlsg as a regular variable. - if r.Type == obj.R_TLS && goos == "android" { - r.Type = obj.R_ADDR - } - - switch r.Type { - default: - o = 0 - if Thearch.Archreloc(r, s, &o) < 0 { - Diag("unknown reloc %d", r.Type) - } - - case obj.R_TLS: - if Linkmode == LinkExternal && Iself && HEADTYPE != obj.Hopenbsd { - r.Done = 0 - r.Sym = Ctxt.Tlsg - r.Xsym = Ctxt.Tlsg - r.Xadd = r.Add - o = r.Add - break - } - if Linkmode == LinkInternal && Iself && Thearch.Thechar == '5' { - // On ELF ARM, the thread pointer is 8 bytes before - // the start of the thread-local data block, so add 8 - // to the actual TLS offset (r->sym->value). - // This 8 seems to be a fundamental constant of - // ELF on ARM (or maybe Glibc on ARM); it is not - // related to the fact that our own TLS storage happens - // to take up 8 bytes. - o = 8 + r.Sym.Value - - break - } - - r.Done = 0 - o = 0 - if Thearch.Thechar != '6' { - o = r.Add - } - - case obj.R_TLS_LE: - if Linkmode == LinkExternal && Iself && HEADTYPE != obj.Hopenbsd { - r.Done = 0 - r.Sym = Ctxt.Tlsg - r.Xsym = Ctxt.Tlsg - r.Xadd = r.Add - o = 0 - if Thearch.Thechar != '6' { - o = r.Add - } - break - } - - if Iself || Ctxt.Headtype == obj.Hplan9 || Ctxt.Headtype == obj.Hdarwin { - o = int64(Ctxt.Tlsoffset) + r.Add - } else if Ctxt.Headtype == obj.Hwindows { - o = r.Add - } else { - log.Fatalf("unexpected R_TLS_LE relocation for %s", Headstr(Ctxt.Headtype)) - } - - case obj.R_TLS_IE: - if Linkmode == LinkExternal && Iself && HEADTYPE != obj.Hopenbsd { - r.Done = 0 - r.Sym = Ctxt.Tlsg - r.Xsym = Ctxt.Tlsg - r.Xadd = r.Add - o = 0 - if Thearch.Thechar != '6' { - o = r.Add - } - break - } - log.Fatalf("cannot handle R_TLS_IE when linking internally") - - case obj.R_ADDR: - if Linkmode == LinkExternal && r.Sym.Type != obj.SCONST { - r.Done = 0 - - // set up addend for eventual relocation via outer symbol. - rs = r.Sym - - r.Xadd = r.Add - for rs.Outer != nil { - r.Xadd += Symaddr(rs) - Symaddr(rs.Outer) - rs = rs.Outer - } - - if rs.Type != obj.SHOSTOBJ && rs.Type != obj.SDYNIMPORT && rs.Sect == nil { - Diag("missing section for %s", rs.Name) - } - r.Xsym = rs - - o = r.Xadd - if Iself { - if Thearch.Thechar == '6' { - o = 0 - } - } else if HEADTYPE == obj.Hdarwin { - // ld64 for arm64 has a bug where if the address pointed to by o exists in the - // symbol table (dynid >= 0), or is inside a symbol that exists in the symbol - // table, then it will add o twice into the relocated value. - // The workaround is that on arm64 don't ever add symaddr to o and always use - // extern relocation by requiring rs->dynid >= 0. - if rs.Type != obj.SHOSTOBJ { - if Thearch.Thechar == '7' && rs.Dynid < 0 { - Diag("R_ADDR reloc to %s+%d is not supported on darwin/arm64", rs.Name, o) - } - if Thearch.Thechar != '7' { - o += Symaddr(rs) - } - } - } else if HEADTYPE == obj.Hwindows { - // nothing to do - } else { - Diag("unhandled pcrel relocation for %s", headstring) - } - - break - } - - o = Symaddr(r.Sym) + r.Add - - // On amd64, 4-byte offsets will be sign-extended, so it is impossible to - // access more than 2GB of static data; fail at link time is better than - // fail at runtime. See http://golang.org/issue/7980. - // Instead of special casing only amd64, we treat this as an error on all - // 64-bit architectures so as to be future-proof. - if int32(o) < 0 && Thearch.Ptrsize > 4 && siz == 4 { - Diag("non-pc-relative relocation address is too big: %#x (%#x + %#x)", uint64(o), Symaddr(r.Sym), r.Add) - errorexit() - } - - // r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call. - case obj.R_CALL, obj.R_GOTPCREL, obj.R_PCREL: - if Linkmode == LinkExternal && r.Sym != nil && r.Sym.Type != obj.SCONST && (r.Sym.Sect != Ctxt.Cursym.Sect || r.Type == obj.R_GOTPCREL) { - r.Done = 0 - - // set up addend for eventual relocation via outer symbol. - rs = r.Sym - - r.Xadd = r.Add - for rs.Outer != nil { - r.Xadd += Symaddr(rs) - Symaddr(rs.Outer) - rs = rs.Outer - } - - r.Xadd -= int64(r.Siz) // relative to address after the relocated chunk - if rs.Type != obj.SHOSTOBJ && rs.Type != obj.SDYNIMPORT && rs.Sect == nil { - Diag("missing section for %s", rs.Name) - } - r.Xsym = rs - - o = r.Xadd - if Iself { - if Thearch.Thechar == '6' { - o = 0 - } - } 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(r.Off) // relative to section offset, not symbol - } else { - o += int64(r.Siz) - } - } else if HEADTYPE == obj.Hwindows && Thearch.Thechar == '6' { // only amd64 needs PCREL - // PE/COFF's PC32 relocation uses the address after the relocated - // bytes as the base. Compensate by skewing the addend. - 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) - } else { - Diag("unhandled pcrel relocation for %s", headstring) - } - - break - } - - o = 0 - if r.Sym != nil { - o += Symaddr(r.Sym) - } - - // NOTE: The (int32) cast on the next line works around a bug in Plan 9's 8c - // compiler. The expression s->value + r->off + r->siz is int32 + int32 + - // uchar, and Plan 9 8c incorrectly treats the expression as type uint32 - // instead of int32, causing incorrect values when sign extended for adding - // to o. The bug only occurs on Plan 9, because this C program is compiled by - // the standard host compiler (gcc on most other systems). - o += r.Add - (s.Value + int64(r.Off) + int64(int32(r.Siz))) - - case obj.R_SIZE: - o = r.Sym.Size + r.Add - } - - if r.Variant != RV_NONE { - o = Thearch.Archrelocvariant(r, s, o) - } - - if false { - nam := "" - if r.Sym != nil { - nam = r.Sym.Name - } - fmt.Printf("relocate %s %#x (%#x+%#x, size %d) => %s %#x +%#x [type %d/%d, %x]\n", s.Name, s.Value+int64(off), s.Value, r.Off, r.Siz, nam, Symaddr(r.Sym), r.Add, r.Type, r.Variant, o) - } - switch siz { - default: - Ctxt.Cursym = s - Diag("bad reloc size %#x for %s", uint32(siz), r.Sym.Name) - fallthrough - - // TODO(rsc): Remove. - case 1: - s.P[off] = byte(int8(o)) - - case 2: - if o != int64(int16(o)) { - Diag("relocation address is too big: %#x", o) - } - i16 = int16(o) - Ctxt.Arch.ByteOrder.PutUint16(s.P[off:], uint16(i16)) - - case 4: - if r.Type == obj.R_PCREL || r.Type == obj.R_CALL { - if o != int64(int32(o)) { - Diag("pc-relative relocation address is too big: %#x", o) - } - } else { - if o != int64(int32(o)) && o != int64(uint32(o)) { - Diag("non-pc-relative relocation address is too big: %#x", uint64(o)) - } - } - - fl = int32(o) - Ctxt.Arch.ByteOrder.PutUint32(s.P[off:], uint32(fl)) - - case 8: - Ctxt.Arch.ByteOrder.PutUint64(s.P[off:], uint64(o)) - } - } -} - -func reloc() { - if Debug['v'] != 0 { - fmt.Fprintf(&Bso, "%5.2f reloc\n", obj.Cputime()) - } - Bso.Flush() - - for s := Ctxt.Textp; s != nil; s = s.Next { - relocsym(s) - } - for s := datap; s != nil; s = s.Next { - relocsym(s) - } -} - -func dynrelocsym(s *LSym) { - if HEADTYPE == obj.Hwindows && Linkmode != LinkExternal { - rel := Linklookup(Ctxt, ".rel", 0) - if s == rel { - return - } - var r *Reloc - var targ *LSym - for ri := 0; ri < len(s.R); ri++ { - r = &s.R[ri] - targ = r.Sym - if targ == nil { - continue - } - if !targ.Reachable { - Diag("internal inconsistency: dynamic symbol %s is not reachable.", targ.Name) - } - if r.Sym.Plt == -2 && r.Sym.Got != -2 { // make dynimport JMP table for PE object files. - targ.Plt = int32(rel.Size) - r.Sym = rel - r.Add = int64(targ.Plt) - - // jmp *addr - if Thearch.Thechar == '8' { - Adduint8(Ctxt, rel, 0xff) - Adduint8(Ctxt, rel, 0x25) - Addaddr(Ctxt, rel, targ) - Adduint8(Ctxt, rel, 0x90) - Adduint8(Ctxt, rel, 0x90) - } else { - Adduint8(Ctxt, rel, 0xff) - Adduint8(Ctxt, rel, 0x24) - Adduint8(Ctxt, rel, 0x25) - addaddrplus4(Ctxt, rel, targ, 0) - Adduint8(Ctxt, rel, 0x90) - } - } else if r.Sym.Plt >= 0 { - r.Sym = rel - r.Add = int64(targ.Plt) - } - } - - return - } - - var r *Reloc - for ri := 0; ri < len(s.R); ri++ { - r = &s.R[ri] - if r.Sym != nil && r.Sym.Type == obj.SDYNIMPORT || r.Type >= 256 { - if r.Sym != nil && !r.Sym.Reachable { - Diag("internal inconsistency: dynamic symbol %s is not reachable.", r.Sym.Name) - } - Thearch.Adddynrel(s, r) - } - } -} - -func dynreloc() { - // -d suppresses dynamic loader format, so we may as well not - // compute these sections or mark their symbols as reachable. - if Debug['d'] != 0 && HEADTYPE != obj.Hwindows { - return - } - if Debug['v'] != 0 { - fmt.Fprintf(&Bso, "%5.2f reloc\n", obj.Cputime()) - } - Bso.Flush() - - for s := Ctxt.Textp; s != nil; s = s.Next { - dynrelocsym(s) - } - for s := datap; s != nil; s = s.Next { - dynrelocsym(s) - } - if Iself { - elfdynhash() - } -} - -func blk(start *LSym, addr int64, size int64) { - var sym *LSym - - for sym = start; sym != nil; sym = sym.Next { - if sym.Type&obj.SSUB == 0 && sym.Value >= addr { - break - } - } - - eaddr := addr + size - var ep []byte - var p []byte - for ; sym != nil; sym = sym.Next { - if sym.Type&obj.SSUB != 0 { - continue - } - if sym.Value >= eaddr { - break - } - Ctxt.Cursym = sym - if sym.Value < addr { - Diag("phase error: addr=%#x but sym=%#x type=%d", int64(addr), int64(sym.Value), sym.Type) - errorexit() - } - - for ; addr < sym.Value; addr++ { - Cput(0) - } - p = sym.P - ep = p[len(sym.P):] - for -cap(p) < -cap(ep) { - Cput(uint8(p[0])) - p = p[1:] - } - addr += int64(len(sym.P)) - for ; addr < sym.Value+sym.Size; addr++ { - Cput(0) - } - if addr != sym.Value+sym.Size { - Diag("phase error: addr=%#x value+size=%#x", int64(addr), int64(sym.Value)+sym.Size) - errorexit() - } - - if sym.Value+sym.Size >= eaddr { - break - } - } - - for ; addr < eaddr; addr++ { - Cput(0) - } - Cflush() -} - -func Codeblk(addr int64, size int64) { - if Debug['a'] != 0 { - fmt.Fprintf(&Bso, "codeblk [%#x,%#x) at offset %#x\n", addr, addr+size, Cpos()) - } - - blk(Ctxt.Textp, addr, size) - - /* again for printing */ - if Debug['a'] == 0 { - return - } - - var sym *LSym - for sym = Ctxt.Textp; sym != nil; sym = sym.Next { - if !sym.Reachable { - continue - } - if sym.Value >= addr { - break - } - } - - eaddr := addr + size - var n int64 - var q []byte - for ; sym != nil; sym = sym.Next { - if !sym.Reachable { - continue - } - if sym.Value >= eaddr { - break - } - - if addr < sym.Value { - fmt.Fprintf(&Bso, "%-20s %.8x|", "_", uint64(int64(addr))) - for ; addr < sym.Value; addr++ { - fmt.Fprintf(&Bso, " %.2x", 0) - } - fmt.Fprintf(&Bso, "\n") - } - - fmt.Fprintf(&Bso, "%.6x\t%-20s\n", uint64(int64(addr)), sym.Name) - n = sym.Size - q = sym.P - - for n >= 16 { - fmt.Fprintf(&Bso, "%.6x\t%-20.16I\n", uint64(addr), q) - addr += 16 - q = q[16:] - n -= 16 - } - - if n > 0 { - fmt.Fprintf(&Bso, "%.6x\t%-20.*I\n", uint64(addr), int(n), q) - } - addr += n - } - - if addr < eaddr { - fmt.Fprintf(&Bso, "%-20s %.8x|", "_", uint64(int64(addr))) - for ; addr < eaddr; addr++ { - fmt.Fprintf(&Bso, " %.2x", 0) - } - } - - Bso.Flush() -} - -func Datblk(addr int64, size int64) { - if Debug['a'] != 0 { - fmt.Fprintf(&Bso, "datblk [%#x,%#x) at offset %#x\n", addr, addr+size, Cpos()) - } - - blk(datap, addr, size) - - /* again for printing */ - if Debug['a'] == 0 { - return - } - - var sym *LSym - for sym = datap; sym != nil; sym = sym.Next { - if sym.Value >= addr { - break - } - } - - eaddr := addr + size - var ep []byte - var i int64 - var p []byte - var r *Reloc - var rsname string - var typ string - for ; sym != nil; sym = sym.Next { - if sym.Value >= eaddr { - break - } - if addr < sym.Value { - fmt.Fprintf(&Bso, "\t%.8x| 00 ...\n", uint64(addr)) - addr = sym.Value - } - - fmt.Fprintf(&Bso, "%s\n\t%.8x|", sym.Name, uint(addr)) - p = sym.P - ep = p[len(sym.P):] - for -cap(p) < -cap(ep) { - if -cap(p) > -cap(sym.P) && int(-cap(p)+cap(sym.P))%16 == 0 { - fmt.Fprintf(&Bso, "\n\t%.8x|", uint(addr+int64(-cap(p)+cap(sym.P)))) - } - fmt.Fprintf(&Bso, " %.2x", p[0]) - p = p[1:] - } - - addr += int64(len(sym.P)) - for ; addr < sym.Value+sym.Size; addr++ { - fmt.Fprintf(&Bso, " %.2x", 0) - } - fmt.Fprintf(&Bso, "\n") - - if Linkmode == LinkExternal { - for i = 0; i < int64(len(sym.R)); i++ { - r = &sym.R[i] - rsname = "" - if r.Sym != nil { - rsname = r.Sym.Name - } - typ = "?" - switch r.Type { - case obj.R_ADDR: - typ = "addr" - - case obj.R_PCREL: - typ = "pcrel" - - case obj.R_CALL: - typ = "call" - } - - fmt.Fprintf(&Bso, "\treloc %.8x/%d %s %s+%#x [%#x]\n", uint(sym.Value+int64(r.Off)), r.Siz, typ, rsname, int64(r.Add), int64(r.Sym.Value+r.Add)) - } - } - } - - if addr < eaddr { - fmt.Fprintf(&Bso, "\t%.8x| 00 ...\n", uint(addr)) - } - fmt.Fprintf(&Bso, "\t%.8x|\n", uint(eaddr)) -} - -func strnput(s string, n int) { - for ; n > 0 && s != ""; s = s[1:] { - Cput(uint8(s[0])) - n-- - } - - for n > 0 { - Cput(0) - n-- - } -} - -var addstrdata_name string - -func addstrdata1(arg string) { - if strings.HasPrefix(arg, "VALUE:") { - addstrdata(addstrdata_name, arg[6:]) - } else { - addstrdata_name = arg - } -} - -func addstrdata(name string, value string) { - p := fmt.Sprintf("%s.str", name) - sp := Linklookup(Ctxt, p, 0) - - Addstring(sp, value) - sp.Type = obj.SRODATA - - s := Linklookup(Ctxt, name, 0) - s.Size = 0 - s.Dupok = 1 - reachable := s.Reachable - Addaddr(Ctxt, s, sp) - adduintxx(Ctxt, s, uint64(len(value)), Thearch.Ptrsize) - - // addstring, addaddr, etc., mark the symbols as reachable. - // In this case that is not necessarily true, so stick to what - // we know before entering this function. - s.Reachable = reachable - - sp.Reachable = reachable -} - -func Addstring(s *LSym, str string) int64 { - if s.Type == 0 { - s.Type = obj.SNOPTRDATA - } - s.Reachable = true - r := int32(s.Size) - n := len(str) + 1 - if s.Name == ".shstrtab" { - elfsetstring(str, int(r)) - } - Symgrow(Ctxt, s, int64(r)+int64(n)) - copy(s.P[r:], str) - s.P[int(r)+len(str)] = 0 - s.Size += int64(n) - 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) - sp.Type = obj.SINITARR - sp.Size = 0 - sp.Dupok = 1 - Addaddr(Ctxt, sp, s) -} - -func dosymtype() { - for s := Ctxt.Allsym; s != nil; s = s.Allsym { - if len(s.P) > 0 { - if s.Type == obj.SBSS { - s.Type = obj.SDATA - } - if s.Type == obj.SNOPTRBSS { - s.Type = obj.SNOPTRDATA - } - } - // Create a new entry in the .init_array section that points to the - // library initializer function. - switch Buildmode { - case BuildmodeCArchive, BuildmodeCShared: - if s.Name == INITENTRY { - addinitarrdata(s) - } - } - } -} - -func symalign(s *LSym) int32 { - if s.Align != 0 { - return s.Align - } - - align := int32(Thearch.Maxalign) - for int64(align) > s.Size && align > 1 { - align >>= 1 - } - if align < s.Align { - align = s.Align - } - return align -} - -func aligndatsize(datsize int64, s *LSym) int64 { - return Rnd(datsize, int64(symalign(s))) -} - -// maxalign returns the maximum required alignment for -// the list of symbols s; the list stops when s->type exceeds type. -func maxalign(s *LSym, type_ int) int32 { - var align int32 - - max := int32(0) - for ; s != nil && int(s.Type) <= type_; s = s.Next { - align = symalign(s) - if max < align { - max = align - } - } - - return max -} - -const debugGCProg = false - -type GCProg struct { - sym *LSym - w gcprog.Writer -} - -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) - } -} - -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") - } -} - -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 - } - - 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) { - datsize := *datsizep - if s.Size < 0 { - Diag("negative size (datsize = %d, s->size = %d)", datsize, s.Size) - } - if datsize+s.Size < datsize { - Diag("symbol too large (datsize = %d, s->size = %d)", datsize, s.Size) - } - *datsizep = datsize + s.Size -} - -func dodata() { - if Debug['v'] != 0 { - fmt.Fprintf(&Bso, "%5.2f dodata\n", obj.Cputime()) - } - Bso.Flush() - - var last *LSym - datap = nil - - for s := Ctxt.Allsym; s != nil; s = s.Allsym { - if !s.Reachable || s.Special != 0 { - continue - } - if obj.STEXT < s.Type && s.Type < obj.SXREF { - if s.Onlist != 0 { - log.Fatalf("symbol %s listed multiple times", s.Name) - } - s.Onlist = 1 - if last == nil { - datap = s - } else { - last.Next = s - } - s.Next = nil - last = s - } - } - - for s := datap; s != nil; s = s.Next { - if int64(len(s.P)) > s.Size { - Diag("%s: initialize bounds (%d < %d)", s.Name, int64(s.Size), len(s.P)) - } - } - - /* - * now that we have the datap list, but before we start - * to assign addresses, record all the necessary - * dynamic relocations. these will grow the relocation - * symbol, which is itself data. - * - * on darwin, we need the symbol table numbers for dynreloc. - */ - if HEADTYPE == obj.Hdarwin { - machosymorder() - } - dynreloc() - - /* some symbols may no longer belong in datap (Mach-O) */ - var l **LSym - var s *LSym - for l = &datap; ; { - s = *l - if s == nil { - break - } - - if s.Type <= obj.STEXT || obj.SXREF <= s.Type { - *l = s.Next - } else { - l = &s.Next - } - } - - *l = nil - - datap = listsort(datap, datcmp, listnextp) - - if Iself { - // Make .rela and .rela.plt contiguous, the ELF ABI requires this - // and Solaris actually cares. - var relplt *LSym - for l = &datap; *l != nil; l = &(*l).Next { - if (*l).Name == ".rel.plt" || (*l).Name == ".rela.plt" { - relplt = (*l) - *l = (*l).Next - break - } - } - if relplt != nil { - for s = datap; s != nil; s = s.Next { - if s.Name == ".rel" || s.Name == ".rela" { - relplt.Next = s.Next - s.Next = relplt - } - } - } - } - - /* - * allocate sections. list is sorted by type, - * so we can just walk it for each piece we want to emit. - * segdata is processed before segtext, because we need - * to see all symbols in the .data and .bss sections in order - * to generate garbage collection information. - */ - - /* begin segdata */ - - /* skip symbols belonging to segtext */ - s = datap - - for ; s != nil && s.Type < obj.SELFSECT; s = s.Next { - } - - /* writable ELF sections */ - datsize := int64(0) - - var sect *Section - for ; s != nil && s.Type < obj.SELFGOT; s = s.Next { - sect = addsection(&Segdata, s.Name, 06) - sect.Align = symalign(s) - datsize = Rnd(datsize, int64(sect.Align)) - sect.Vaddr = uint64(datsize) - s.Sect = sect - s.Type = obj.SDATA - s.Value = int64(uint64(datsize) - sect.Vaddr) - growdatsize(&datsize, s) - sect.Length = uint64(datsize) - sect.Vaddr - } - - /* .got (and .toc on ppc64) */ - if s.Type == obj.SELFGOT { - sect := addsection(&Segdata, ".got", 06) - sect.Align = maxalign(s, obj.SELFGOT) - datsize = Rnd(datsize, int64(sect.Align)) - sect.Vaddr = uint64(datsize) - var toc *LSym - for ; s != nil && s.Type == obj.SELFGOT; s = s.Next { - datsize = aligndatsize(datsize, s) - s.Sect = sect - s.Type = obj.SDATA - s.Value = int64(uint64(datsize) - sect.Vaddr) - - // Resolve .TOC. symbol for this object file (ppc64) - toc = Linkrlookup(Ctxt, ".TOC.", int(s.Version)) - - if toc != nil { - toc.Sect = sect - toc.Outer = s - toc.Sub = s.Sub - s.Sub = toc - - toc.Value = 0x8000 - } - - growdatsize(&datsize, s) - } - - sect.Length = uint64(datsize) - sect.Vaddr - } - - /* pointer-free data */ - sect = addsection(&Segdata, ".noptrdata", 06) - - sect.Align = maxalign(s, obj.SINITARR-1) - datsize = Rnd(datsize, int64(sect.Align)) - sect.Vaddr = uint64(datsize) - Linklookup(Ctxt, "runtime.noptrdata", 0).Sect = sect - Linklookup(Ctxt, "runtime.enoptrdata", 0).Sect = sect - for ; s != nil && s.Type < obj.SINITARR; s = s.Next { - datsize = aligndatsize(datsize, s) - s.Sect = sect - s.Type = obj.SDATA - s.Value = int64(uint64(datsize) - sect.Vaddr) - growdatsize(&datsize, s) - } - - sect.Length = uint64(datsize) - sect.Vaddr - - hasinitarr := Linkshared - - /* shared library initializer */ - switch Buildmode { - case BuildmodeCArchive, BuildmodeCShared, BuildmodeShared: - hasinitarr = true - } - - if hasinitarr { - sect := addsection(&Segdata, ".init_array", 06) - sect.Align = maxalign(s, obj.SINITARR) - datsize = Rnd(datsize, int64(sect.Align)) - sect.Vaddr = uint64(datsize) - for ; s != nil && s.Type == obj.SINITARR; s = s.Next { - datsize = aligndatsize(datsize, s) - s.Sect = sect - s.Value = int64(uint64(datsize) - sect.Vaddr) - growdatsize(&datsize, s) - } - - sect.Length = uint64(datsize) - sect.Vaddr - } - - /* 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 - 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 - Diag("unexpected symbol type %d", s.Type) - } - - s.Sect = sect - s.Type = obj.SDATA - datsize = aligndatsize(datsize, s) - s.Value = int64(uint64(datsize) - sect.Vaddr) - gc.AddSym(s) - growdatsize(&datsize, s) - } - sect.Length = uint64(datsize) - sect.Vaddr - 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 - 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) - gc.AddSym(s) - growdatsize(&datsize, s) - } - sect.Length = uint64(datsize) - sect.Vaddr - gc.End(int64(sect.Length)) - - /* pointer-free bss */ - sect = addsection(&Segdata, ".noptrbss", 06) - - sect.Align = maxalign(s, obj.SNOPTRBSS) - datsize = Rnd(datsize, int64(sect.Align)) - sect.Vaddr = uint64(datsize) - Linklookup(Ctxt, "runtime.noptrbss", 0).Sect = sect - Linklookup(Ctxt, "runtime.enoptrbss", 0).Sect = sect - for ; s != nil && s.Type == obj.SNOPTRBSS; s = s.Next { - datsize = aligndatsize(datsize, s) - s.Sect = sect - s.Value = int64(uint64(datsize) - sect.Vaddr) - growdatsize(&datsize, s) - } - - sect.Length = uint64(datsize) - sect.Vaddr - Linklookup(Ctxt, "runtime.end", 0).Sect = sect - - // 6g uses 4-byte relocation offsets, so the entire segment must fit in 32 bits. - if datsize != int64(uint32(datsize)) { - Diag("data or bss segment too large") - } - - if Iself && Linkmode == LinkExternal && s != nil && s.Type == obj.STLSBSS && HEADTYPE != obj.Hopenbsd { - sect := addsection(&Segdata, ".tbss", 06) - sect.Align = int32(Thearch.Ptrsize) - sect.Vaddr = 0 - datsize = 0 - for ; s != nil && s.Type == obj.STLSBSS; s = s.Next { - datsize = aligndatsize(datsize, s) - s.Sect = sect - s.Value = int64(uint64(datsize) - sect.Vaddr) - growdatsize(&datsize, s) - } - - sect.Length = uint64(datsize) - } else { - // Might be internal linking but still using cgo. - // In that case, the only possible STLSBSS symbol is runtime.tlsg. - // Give it offset 0, because it's the only thing here. - if s != nil && s.Type == obj.STLSBSS && s.Name == "runtime.tlsg" { - s.Value = 0 - s = s.Next - } - } - - if s != nil { - Ctxt.Cursym = nil - Diag("unexpected symbol type %d for %s", s.Type, s.Name) - } - - /* - * We finished data, begin read-only data. - * Not all systems support a separate read-only non-executable data section. - * ELF systems do. - * OS X and Plan 9 do not. - * Windows PE may, but if so we have not implemented it. - * And if we're using external linking mode, the point is moot, - * since it's not our decision; that code expects the sections in - * segtext. - */ - var segro *Segment - if Iself && Linkmode == LinkInternal { - segro = &Segrodata - } else { - segro = &Segtext - } - - s = datap - - datsize = 0 - - /* read-only executable ELF, Mach-O sections */ - for ; s != nil && s.Type < obj.STYPE; s = s.Next { - sect = addsection(&Segtext, s.Name, 04) - sect.Align = symalign(s) - datsize = Rnd(datsize, int64(sect.Align)) - sect.Vaddr = uint64(datsize) - s.Sect = sect - s.Type = obj.SRODATA - s.Value = int64(uint64(datsize) - sect.Vaddr) - growdatsize(&datsize, s) - sect.Length = uint64(datsize) - sect.Vaddr - } - - /* read-only data */ - sect = addsection(segro, ".rodata", 04) - - sect.Align = maxalign(s, obj.STYPELINK-1) - datsize = Rnd(datsize, int64(sect.Align)) - sect.Vaddr = 0 - Linklookup(Ctxt, "runtime.rodata", 0).Sect = sect - Linklookup(Ctxt, "runtime.erodata", 0).Sect = sect - for ; s != nil && s.Type < obj.STYPELINK; s = s.Next { - datsize = aligndatsize(datsize, s) - s.Sect = sect - s.Type = obj.SRODATA - s.Value = int64(uint64(datsize) - sect.Vaddr) - growdatsize(&datsize, s) - } - - sect.Length = uint64(datsize) - sect.Vaddr - - /* typelink */ - sect = addsection(segro, ".typelink", 04) - - sect.Align = maxalign(s, obj.STYPELINK) - datsize = Rnd(datsize, int64(sect.Align)) - sect.Vaddr = uint64(datsize) - Linklookup(Ctxt, "runtime.typelink", 0).Sect = sect - Linklookup(Ctxt, "runtime.etypelink", 0).Sect = sect - for ; s != nil && s.Type == obj.STYPELINK; s = s.Next { - datsize = aligndatsize(datsize, s) - s.Sect = sect - s.Type = obj.SRODATA - s.Value = int64(uint64(datsize) - sect.Vaddr) - growdatsize(&datsize, s) - } - - sect.Length = uint64(datsize) - sect.Vaddr - - /* gosymtab */ - sect = addsection(segro, ".gosymtab", 04) - - sect.Align = maxalign(s, obj.SPCLNTAB-1) - datsize = Rnd(datsize, int64(sect.Align)) - sect.Vaddr = uint64(datsize) - Linklookup(Ctxt, "runtime.symtab", 0).Sect = sect - Linklookup(Ctxt, "runtime.esymtab", 0).Sect = sect - for ; s != nil && s.Type < obj.SPCLNTAB; s = s.Next { - datsize = aligndatsize(datsize, s) - s.Sect = sect - s.Type = obj.SRODATA - s.Value = int64(uint64(datsize) - sect.Vaddr) - growdatsize(&datsize, s) - } - - sect.Length = uint64(datsize) - sect.Vaddr - - /* gopclntab */ - sect = addsection(segro, ".gopclntab", 04) - - sect.Align = maxalign(s, obj.SELFROSECT-1) - datsize = Rnd(datsize, int64(sect.Align)) - sect.Vaddr = uint64(datsize) - Linklookup(Ctxt, "runtime.pclntab", 0).Sect = sect - Linklookup(Ctxt, "runtime.epclntab", 0).Sect = sect - for ; s != nil && s.Type < obj.SELFROSECT; s = s.Next { - datsize = aligndatsize(datsize, s) - s.Sect = sect - s.Type = obj.SRODATA - s.Value = int64(uint64(datsize) - sect.Vaddr) - growdatsize(&datsize, s) - } - - sect.Length = uint64(datsize) - sect.Vaddr - - /* read-only ELF, Mach-O sections */ - for ; s != nil && s.Type < obj.SELFSECT; s = s.Next { - sect = addsection(segro, s.Name, 04) - sect.Align = symalign(s) - datsize = Rnd(datsize, int64(sect.Align)) - sect.Vaddr = uint64(datsize) - s.Sect = sect - s.Type = obj.SRODATA - s.Value = int64(uint64(datsize) - sect.Vaddr) - growdatsize(&datsize, s) - sect.Length = uint64(datsize) - sect.Vaddr - } - - // 6g uses 4-byte relocation offsets, so the entire segment must fit in 32 bits. - if datsize != int64(uint32(datsize)) { - Diag("read-only data segment too large") - } - - /* number the sections */ - n := int32(1) - - for sect := Segtext.Sect; sect != nil; sect = sect.Next { - sect.Extnum = int16(n) - n++ - } - for sect := Segrodata.Sect; sect != nil; sect = sect.Next { - sect.Extnum = int16(n) - n++ - } - for sect := Segdata.Sect; sect != nil; sect = sect.Next { - sect.Extnum = int16(n) - n++ - } -} - -// assign addresses to text -func textaddress() { - var sub *LSym - - addsection(&Segtext, ".text", 05) - - // Assign PCs in text segment. - // Could parallelize, by assigning to text - // and then letting threads copy down, but probably not worth it. - sect := Segtext.Sect - - sect.Align = int32(Funcalign) - Linklookup(Ctxt, "runtime.text", 0).Sect = sect - Linklookup(Ctxt, "runtime.etext", 0).Sect = sect - va := uint64(INITTEXT) - sect.Vaddr = va - for sym := Ctxt.Textp; sym != nil; sym = sym.Next { - sym.Sect = sect - if sym.Type&obj.SSUB != 0 { - continue - } - if sym.Align != 0 { - va = uint64(Rnd(int64(va), int64(sym.Align))) - } else { - va = uint64(Rnd(int64(va), int64(Funcalign))) - } - sym.Value = 0 - for sub = sym; sub != nil; sub = sub.Sub { - sub.Value += int64(va) - } - if sym.Size == 0 && sym.Sub != nil { - Ctxt.Cursym = sym - } - if sym.Size < MINFUNC { - va += MINFUNC // spacing required for findfunctab - } else { - va += uint64(sym.Size) - } - } - - sect.Length = va - sect.Vaddr -} - -// assign addresses -func address() { - va := uint64(INITTEXT) - Segtext.Rwx = 05 - Segtext.Vaddr = va - Segtext.Fileoff = uint64(HEADR) - for s := Segtext.Sect; s != nil; s = s.Next { - va = uint64(Rnd(int64(va), int64(s.Align))) - s.Vaddr = va - va += s.Length - } - - Segtext.Length = va - uint64(INITTEXT) - Segtext.Filelen = Segtext.Length - if HEADTYPE == obj.Hnacl { - va += 32 // room for the "halt sled" - } - - if Segrodata.Sect != nil { - // align to page boundary so as not to mix - // rodata and executable text. - va = uint64(Rnd(int64(va), int64(INITRND))) - - Segrodata.Rwx = 04 - Segrodata.Vaddr = va - Segrodata.Fileoff = va - Segtext.Vaddr + Segtext.Fileoff - Segrodata.Filelen = 0 - for s := Segrodata.Sect; s != nil; s = s.Next { - va = uint64(Rnd(int64(va), int64(s.Align))) - s.Vaddr = va - va += s.Length - } - - Segrodata.Length = va - Segrodata.Vaddr - Segrodata.Filelen = Segrodata.Length - } - - va = uint64(Rnd(int64(va), int64(INITRND))) - Segdata.Rwx = 06 - Segdata.Vaddr = va - Segdata.Fileoff = va - Segtext.Vaddr + Segtext.Fileoff - Segdata.Filelen = 0 - if HEADTYPE == obj.Hwindows { - Segdata.Fileoff = Segtext.Fileoff + uint64(Rnd(int64(Segtext.Length), PEFILEALIGN)) - } - if HEADTYPE == obj.Hplan9 { - Segdata.Fileoff = Segtext.Fileoff + Segtext.Filelen - } - var data *Section - var noptr *Section - var bss *Section - var noptrbss *Section - var vlen int64 - for s := Segdata.Sect; s != nil; s = s.Next { - vlen = int64(s.Length) - if s.Next != nil { - vlen = int64(s.Next.Vaddr - s.Vaddr) - } - s.Vaddr = va - va += uint64(vlen) - Segdata.Length = va - Segdata.Vaddr - if s.Name == ".data" { - data = s - } - if s.Name == ".noptrdata" { - noptr = s - } - if s.Name == ".bss" { - bss = s - } - if s.Name == ".noptrbss" { - noptrbss = s - } - } - - Segdata.Filelen = bss.Vaddr - Segdata.Vaddr - - text := Segtext.Sect - var rodata *Section - if Segrodata.Sect != nil { - rodata = Segrodata.Sect - } else { - rodata = text.Next - } - typelink := rodata.Next - symtab := typelink.Next - pclntab := symtab.Next - - var sub *LSym - for sym := datap; sym != nil; sym = sym.Next { - Ctxt.Cursym = sym - if sym.Sect != nil { - sym.Value += int64((sym.Sect.(*Section)).Vaddr) - } - for sub = sym.Sub; sub != nil; sub = sub.Sub { - sub.Value += sym.Value - } - } - - 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)) - xdefine("runtime.erodata", obj.SRODATA, int64(rodata.Vaddr+rodata.Length)) - xdefine("runtime.typelink", obj.SRODATA, int64(typelink.Vaddr)) - xdefine("runtime.etypelink", obj.SRODATA, int64(typelink.Vaddr+typelink.Length)) - - sym := Linklookup(Ctxt, "runtime.gcdata", 0) - sym.Local = true - xdefine("runtime.egcdata", obj.SRODATA, Symaddr(sym)+sym.Size) - Linklookup(Ctxt, "runtime.egcdata", 0).Sect = sym.Sect - - sym = Linklookup(Ctxt, "runtime.gcbss", 0) - sym.Local = true - xdefine("runtime.egcbss", obj.SRODATA, Symaddr(sym)+sym.Size) - Linklookup(Ctxt, "runtime.egcbss", 0).Sect = sym.Sect - - xdefine("runtime.symtab", obj.SRODATA, int64(symtab.Vaddr)) - xdefine("runtime.esymtab", obj.SRODATA, int64(symtab.Vaddr+symtab.Length)) - xdefine("runtime.pclntab", obj.SRODATA, int64(pclntab.Vaddr)) - xdefine("runtime.epclntab", obj.SRODATA, int64(pclntab.Vaddr+pclntab.Length)) - xdefine("runtime.noptrdata", obj.SNOPTRDATA, int64(noptr.Vaddr)) - xdefine("runtime.enoptrdata", obj.SNOPTRDATA, int64(noptr.Vaddr+noptr.Length)) - xdefine("runtime.bss", obj.SBSS, int64(bss.Vaddr)) - xdefine("runtime.ebss", obj.SBSS, int64(bss.Vaddr+bss.Length)) - xdefine("runtime.data", obj.SDATA, int64(data.Vaddr)) - xdefine("runtime.edata", obj.SDATA, int64(data.Vaddr+data.Length)) - xdefine("runtime.noptrbss", obj.SNOPTRBSS, int64(noptrbss.Vaddr)) - xdefine("runtime.enoptrbss", obj.SNOPTRBSS, int64(noptrbss.Vaddr+noptrbss.Length)) - xdefine("runtime.end", obj.SBSS, int64(Segdata.Vaddr+Segdata.Length)) -} diff --git a/src/cmd/internal/ld/decodesym.go b/src/cmd/internal/ld/decodesym.go deleted file mode 100644 index fcc664dde7..0000000000 --- a/src/cmd/internal/ld/decodesym.go +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright 2012 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 "cmd/internal/obj" - -// Decoding the type.* symbols. This has to be in sync with -// ../../runtime/type.go, or more specifically, with what -// ../gc/reflect.c stuffs in these. - -func decode_reloc(s *LSym, off int32) *Reloc { - for i := 0; i < len(s.R); i++ { - if s.R[i].Off == off { - return &s.R[i:][0] - } - } - return nil -} - -func decode_reloc_sym(s *LSym, off int32) *LSym { - r := decode_reloc(s, off) - if r == nil { - return nil - } - return r.Sym -} - -func decode_inuxi(p []byte, sz int) uint64 { - switch sz { - case 2: - return uint64(Ctxt.Arch.ByteOrder.Uint16(p)) - case 4: - return uint64(Ctxt.Arch.ByteOrder.Uint32(p)) - case 8: - return Ctxt.Arch.ByteOrder.Uint64(p) - default: - Exitf("dwarf: decode inuxi %d", sz) - panic("unreachable") - } -} - -// commonsize returns the size of the common prefix for all type -// structures (runtime._type). -func commonsize() int { - return 8*Thearch.Ptrsize + 8 -} - -// Type.commonType.kind -func decodetype_kind(s *LSym) uint8 { - return uint8(s.P[2*Thearch.Ptrsize+7] & obj.KindMask) // 0x13 / 0x1f -} - -// Type.commonType.kind -func decodetype_noptr(s *LSym) uint8 { - return uint8(s.P[2*Thearch.Ptrsize+7] & obj.KindNoPointers) // 0x13 / 0x1f -} - -// Type.commonType.kind -func decodetype_usegcprog(s *LSym) uint8 { - return uint8(s.P[2*Thearch.Ptrsize+7] & obj.KindGCProg) // 0x13 / 0x1f -} - -// Type.commonType.size -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 { - // The gcprog for "type.$name" is calle "type..gcprog.$name". - x := "type..gcprog." + s.Name[5:] - return Linklookup(Ctxt, x, 0) - } - return decode_reloc_sym(s, 2*int32(Thearch.Ptrsize)+8+1*int32(Thearch.Ptrsize)) -} - -func decodetype_gcprog_shlib(s *LSym) uint64 { - return decode_inuxi(s.P[2*int32(Thearch.Ptrsize)+8+1*int32(Thearch.Ptrsize):], Thearch.Ptrsize) -} - -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 - } - mask := decode_reloc_sym(s, 2*int32(Thearch.Ptrsize)+8+1*int32(Thearch.Ptrsize)) - return mask.P -} - -// Type.ArrayType.elem and Type.SliceType.Elem -func decodetype_arrayelem(s *LSym) *LSym { - return decode_reloc_sym(s, int32(commonsize())) // 0x1c / 0x30 -} - -func decodetype_arraylen(s *LSym) int64 { - return int64(decode_inuxi(s.P[commonsize()+2*Thearch.Ptrsize:], Thearch.Ptrsize)) -} - -// Type.PtrType.elem -func decodetype_ptrelem(s *LSym) *LSym { - return decode_reloc_sym(s, int32(commonsize())) // 0x1c / 0x30 -} - -// Type.MapType.key, elem -func decodetype_mapkey(s *LSym) *LSym { - return decode_reloc_sym(s, int32(commonsize())) // 0x1c / 0x30 -} - -func decodetype_mapvalue(s *LSym) *LSym { - return decode_reloc_sym(s, int32(commonsize())+int32(Thearch.Ptrsize)) // 0x20 / 0x38 -} - -// Type.ChanType.elem -func decodetype_chanelem(s *LSym) *LSym { - return decode_reloc_sym(s, int32(commonsize())) // 0x1c / 0x30 -} - -// Type.FuncType.dotdotdot -func decodetype_funcdotdotdot(s *LSym) int { - return int(s.P[commonsize()]) -} - -// Type.FuncType.in.length -func decodetype_funcincount(s *LSym) int { - return int(decode_inuxi(s.P[commonsize()+2*Thearch.Ptrsize:], Thearch.Intsize)) -} - -func decodetype_funcoutcount(s *LSym) int { - return int(decode_inuxi(s.P[commonsize()+3*Thearch.Ptrsize+2*Thearch.Intsize:], Thearch.Intsize)) -} - -func decodetype_funcintype(s *LSym, i int) *LSym { - r := decode_reloc(s, int32(commonsize())+int32(Thearch.Ptrsize)) - if r == nil { - return nil - } - return decode_reloc_sym(r.Sym, int32(r.Add+int64(int32(i)*int32(Thearch.Ptrsize)))) -} - -func decodetype_funcouttype(s *LSym, i int) *LSym { - r := decode_reloc(s, int32(commonsize())+2*int32(Thearch.Ptrsize)+2*int32(Thearch.Intsize)) - if r == nil { - return nil - } - return decode_reloc_sym(r.Sym, int32(r.Add+int64(int32(i)*int32(Thearch.Ptrsize)))) -} - -// Type.StructType.fields.Slice::length -func decodetype_structfieldcount(s *LSym) int { - return int(decode_inuxi(s.P[commonsize()+Thearch.Ptrsize:], Thearch.Intsize)) -} - -func structfieldsize() int { - return 5 * Thearch.Ptrsize -} - -// Type.StructType.fields[]-> name, typ and offset. -func decodetype_structfieldname(s *LSym, i int) string { - // go.string."foo" 0x28 / 0x40 - s = decode_reloc_sym(s, int32(commonsize())+int32(Thearch.Ptrsize)+2*int32(Thearch.Intsize)+int32(i)*int32(structfieldsize())) - - if s == nil { // embedded structs have a nil name. - return "" - } - r := decode_reloc(s, 0) // s has a pointer to the string data at offset 0 - if r == nil { // shouldn't happen. - return "" - } - return cstring(r.Sym.P[r.Add:]) -} - -func decodetype_structfieldtype(s *LSym, i int) *LSym { - return decode_reloc_sym(s, int32(commonsize())+int32(Thearch.Ptrsize)+2*int32(Thearch.Intsize)+int32(i)*int32(structfieldsize())+2*int32(Thearch.Ptrsize)) -} - -func decodetype_structfieldoffs(s *LSym, i int) int64 { - return int64(decode_inuxi(s.P[commonsize()+Thearch.Ptrsize+2*Thearch.Intsize+i*structfieldsize()+4*Thearch.Ptrsize:], Thearch.Intsize)) -} - -// InterfaceType.methods.length -func decodetype_ifacemethodcount(s *LSym) int64 { - return int64(decode_inuxi(s.P[commonsize()+Thearch.Ptrsize:], Thearch.Intsize)) -} diff --git a/src/cmd/internal/ld/dwarf.go b/src/cmd/internal/ld/dwarf.go deleted file mode 100644 index b8fb2e6b55..0000000000 --- a/src/cmd/internal/ld/dwarf.go +++ /dev/null @@ -1,2621 +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. - -// TODO/NICETOHAVE: -// - eliminate DW_CLS_ if not used -// - package info in compilation units -// - assign global variables and types to their packages -// - gdb uses c syntax, meaning clumsy quoting is needed for go identifiers. eg -// ptype struct '[]uint8' and qualifiers need to be quoted away -// - lexical scoping is lost, so gdb gets confused as to which 'main.i' you mean. -// - file:line info for variables -// - make strings a typedef so prettyprinters can see the underlying string type - -package ld - -import ( - "cmd/internal/obj" - "fmt" - "os" - "strings" -) - -/* - * Offsets and sizes of the debug_* sections in the cout file. - */ -var abbrevo int64 - -var abbrevsize int64 - -var abbrevsym *LSym - -var abbrevsympos int64 - -var lineo int64 - -var linesize int64 - -var linesym *LSym - -var linesympos int64 - -var infoo int64 // also the base for DWDie->offs and reference attributes. - -var infosize int64 - -var infosym *LSym - -var infosympos int64 - -var frameo int64 - -var framesize int64 - -var framesym *LSym - -var framesympos int64 - -var pubnameso int64 - -var pubnamessize int64 - -var pubtypeso int64 - -var pubtypessize int64 - -var arangeso int64 - -var arangessize int64 - -var gdbscripto int64 - -var gdbscriptsize int64 - -var infosec *LSym - -var inforeloco int64 - -var inforelocsize int64 - -var arangessec *LSym - -var arangesreloco int64 - -var arangesrelocsize int64 - -var linesec *LSym - -var linereloco int64 - -var linerelocsize int64 - -var framesec *LSym - -var framereloco int64 - -var framerelocsize int64 - -var gdbscript string - -/* - * Basic I/O - */ -func addrput(addr int64) { - switch Thearch.Ptrsize { - case 4: - Thearch.Lput(uint32(addr)) - - case 8: - Thearch.Vput(uint64(addr)) - } -} - -func uleb128enc(v uint64, dst []byte) int { - var c uint8 - - length := uint8(0) - for { - c = uint8(v & 0x7f) - v >>= 7 - if v != 0 { - c |= 0x80 - } - if dst != nil { - dst[0] = byte(c) - dst = dst[1:] - } - length++ - if c&0x80 == 0 { - break - } - } - - return int(length) -} - -func sleb128enc(v int64, dst []byte) int { - var c uint8 - var s uint8 - - length := uint8(0) - for { - c = uint8(v & 0x7f) - s = uint8(v & 0x40) - v >>= 7 - if (v != -1 || s == 0) && (v != 0 || s != 0) { - c |= 0x80 - } - if dst != nil { - dst[0] = byte(c) - dst = dst[1:] - } - length++ - if c&0x80 == 0 { - break - } - } - - return int(length) -} - -var encbuf [10]byte - -func uleb128put(v int64) { - n := uleb128enc(uint64(v), encbuf[:]) - Cwrite(encbuf[:n]) -} - -func sleb128put(v int64) { - n := sleb128enc(v, encbuf[:]) - Cwrite(encbuf[:n]) -} - -/* - * Defining Abbrevs. This is hardcoded, and there will be - * only a handful of them. The DWARF spec places no restriction on - * the ordering of attributes in the Abbrevs and DIEs, and we will - * always write them out in the order of declaration in the abbrev. - */ -type DWAttrForm struct { - attr uint16 - form uint8 -} - -// Go-specific type attributes. -const ( - DW_AT_go_kind = 0x2900 - DW_AT_go_key = 0x2901 - DW_AT_go_elem = 0x2902 - - DW_AT_internal_location = 253 // params and locals; not emitted -) - -// Index into the abbrevs table below. -// Keep in sync with ispubname() and ispubtype() below. -// ispubtype considers >= NULLTYPE public -const ( - DW_ABRV_NULL = iota - DW_ABRV_COMPUNIT - DW_ABRV_FUNCTION - DW_ABRV_VARIABLE - DW_ABRV_AUTO - DW_ABRV_PARAM - DW_ABRV_STRUCTFIELD - DW_ABRV_FUNCTYPEPARAM - DW_ABRV_DOTDOTDOT - DW_ABRV_ARRAYRANGE - DW_ABRV_NULLTYPE - DW_ABRV_BASETYPE - DW_ABRV_ARRAYTYPE - DW_ABRV_CHANTYPE - DW_ABRV_FUNCTYPE - DW_ABRV_IFACETYPE - DW_ABRV_MAPTYPE - DW_ABRV_PTRTYPE - DW_ABRV_BARE_PTRTYPE // only for void*, no DW_AT_type attr to please gdb 6. - DW_ABRV_SLICETYPE - DW_ABRV_STRINGTYPE - DW_ABRV_STRUCTTYPE - DW_ABRV_TYPEDECL - DW_NABRV -) - -type DWAbbrev struct { - tag uint8 - children uint8 - attr []DWAttrForm -} - -var abbrevs = [DW_NABRV]DWAbbrev{ - /* The mandatory DW_ABRV_NULL entry. */ - {0, 0, []DWAttrForm{}}, - - /* COMPUNIT */ - { - DW_TAG_compile_unit, - DW_CHILDREN_yes, - []DWAttrForm{ - {DW_AT_name, DW_FORM_string}, - {DW_AT_language, DW_FORM_data1}, - {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}, - }, - }, - - /* FUNCTION */ - { - DW_TAG_subprogram, - DW_CHILDREN_yes, - []DWAttrForm{ - {DW_AT_name, DW_FORM_string}, - {DW_AT_low_pc, DW_FORM_addr}, - {DW_AT_high_pc, DW_FORM_addr}, - {DW_AT_external, DW_FORM_flag}, - }, - }, - - /* VARIABLE */ - { - DW_TAG_variable, - DW_CHILDREN_no, - []DWAttrForm{ - {DW_AT_name, DW_FORM_string}, - {DW_AT_location, DW_FORM_block1}, - {DW_AT_type, DW_FORM_ref_addr}, - {DW_AT_external, DW_FORM_flag}, - }, - }, - - /* AUTO */ - { - DW_TAG_variable, - DW_CHILDREN_no, - []DWAttrForm{ - {DW_AT_name, DW_FORM_string}, - {DW_AT_location, DW_FORM_block1}, - {DW_AT_type, DW_FORM_ref_addr}, - }, - }, - - /* PARAM */ - { - DW_TAG_formal_parameter, - DW_CHILDREN_no, - []DWAttrForm{ - {DW_AT_name, DW_FORM_string}, - {DW_AT_location, DW_FORM_block1}, - {DW_AT_type, DW_FORM_ref_addr}, - }, - }, - - /* STRUCTFIELD */ - { - DW_TAG_member, - DW_CHILDREN_no, - []DWAttrForm{ - {DW_AT_name, DW_FORM_string}, - {DW_AT_data_member_location, DW_FORM_block1}, - {DW_AT_type, DW_FORM_ref_addr}, - }, - }, - - /* FUNCTYPEPARAM */ - { - DW_TAG_formal_parameter, - DW_CHILDREN_no, - - // No name! - []DWAttrForm{ - {DW_AT_type, DW_FORM_ref_addr}, - }, - }, - - /* DOTDOTDOT */ - { - DW_TAG_unspecified_parameters, - DW_CHILDREN_no, - []DWAttrForm{}, - }, - - /* ARRAYRANGE */ - { - DW_TAG_subrange_type, - DW_CHILDREN_no, - - // No name! - []DWAttrForm{ - {DW_AT_type, DW_FORM_ref_addr}, - {DW_AT_count, DW_FORM_udata}, - }, - }, - - // Below here are the types considered public by ispubtype - /* NULLTYPE */ - { - DW_TAG_unspecified_type, - DW_CHILDREN_no, - []DWAttrForm{ - {DW_AT_name, DW_FORM_string}, - }, - }, - - /* BASETYPE */ - { - DW_TAG_base_type, - DW_CHILDREN_no, - []DWAttrForm{ - {DW_AT_name, DW_FORM_string}, - {DW_AT_encoding, DW_FORM_data1}, - {DW_AT_byte_size, DW_FORM_data1}, - {DW_AT_go_kind, DW_FORM_data1}, - }, - }, - - /* ARRAYTYPE */ - // child is subrange with upper bound - { - DW_TAG_array_type, - DW_CHILDREN_yes, - []DWAttrForm{ - {DW_AT_name, DW_FORM_string}, - {DW_AT_type, DW_FORM_ref_addr}, - {DW_AT_byte_size, DW_FORM_udata}, - {DW_AT_go_kind, DW_FORM_data1}, - }, - }, - - /* CHANTYPE */ - { - DW_TAG_typedef, - DW_CHILDREN_no, - []DWAttrForm{ - {DW_AT_name, DW_FORM_string}, - {DW_AT_type, DW_FORM_ref_addr}, - {DW_AT_go_kind, DW_FORM_data1}, - {DW_AT_go_elem, DW_FORM_ref_addr}, - }, - }, - - /* FUNCTYPE */ - { - DW_TAG_subroutine_type, - DW_CHILDREN_yes, - []DWAttrForm{ - {DW_AT_name, DW_FORM_string}, - // {DW_AT_type, DW_FORM_ref_addr}, - {DW_AT_go_kind, DW_FORM_data1}, - }, - }, - - /* IFACETYPE */ - { - DW_TAG_typedef, - DW_CHILDREN_yes, - []DWAttrForm{ - {DW_AT_name, DW_FORM_string}, - {DW_AT_type, DW_FORM_ref_addr}, - {DW_AT_go_kind, DW_FORM_data1}, - }, - }, - - /* MAPTYPE */ - { - DW_TAG_typedef, - DW_CHILDREN_no, - []DWAttrForm{ - {DW_AT_name, DW_FORM_string}, - {DW_AT_type, DW_FORM_ref_addr}, - {DW_AT_go_kind, DW_FORM_data1}, - {DW_AT_go_key, DW_FORM_ref_addr}, - {DW_AT_go_elem, DW_FORM_ref_addr}, - }, - }, - - /* PTRTYPE */ - { - DW_TAG_pointer_type, - DW_CHILDREN_no, - []DWAttrForm{ - {DW_AT_name, DW_FORM_string}, - {DW_AT_type, DW_FORM_ref_addr}, - {DW_AT_go_kind, DW_FORM_data1}, - }, - }, - - /* BARE_PTRTYPE */ - { - DW_TAG_pointer_type, - DW_CHILDREN_no, - []DWAttrForm{ - {DW_AT_name, DW_FORM_string}, - }, - }, - - /* SLICETYPE */ - { - DW_TAG_structure_type, - DW_CHILDREN_yes, - []DWAttrForm{ - {DW_AT_name, DW_FORM_string}, - {DW_AT_byte_size, DW_FORM_udata}, - {DW_AT_go_kind, DW_FORM_data1}, - {DW_AT_go_elem, DW_FORM_ref_addr}, - }, - }, - - /* STRINGTYPE */ - { - DW_TAG_structure_type, - DW_CHILDREN_yes, - []DWAttrForm{ - {DW_AT_name, DW_FORM_string}, - {DW_AT_byte_size, DW_FORM_udata}, - {DW_AT_go_kind, DW_FORM_data1}, - }, - }, - - /* STRUCTTYPE */ - { - DW_TAG_structure_type, - DW_CHILDREN_yes, - []DWAttrForm{ - {DW_AT_name, DW_FORM_string}, - {DW_AT_byte_size, DW_FORM_udata}, - {DW_AT_go_kind, DW_FORM_data1}, - }, - }, - - /* TYPEDECL */ - { - DW_TAG_typedef, - DW_CHILDREN_no, - []DWAttrForm{ - {DW_AT_name, DW_FORM_string}, - {DW_AT_type, DW_FORM_ref_addr}, - }, - }, -} - -func writeabbrev() { - abbrevo = Cpos() - for i := 1; i < DW_NABRV; i++ { - // See section 7.5.3 - uleb128put(int64(i)) - - uleb128put(int64(abbrevs[i].tag)) - Cput(abbrevs[i].children) - for _, f := range abbrevs[i].attr { - uleb128put(int64(f.attr)) - uleb128put(int64(f.form)) - } - uleb128put(0) - uleb128put(0) - } - - Cput(0) - abbrevsize = Cpos() - abbrevo -} - -/* - * Debugging Information Entries and their attributes. - */ -const ( - HASHSIZE = 107 -) - -func dwarfhashstr(s string) uint32 { - h := uint32(0) - for s != "" { - h = h + h + h + uint32(s[0]) - s = s[1:] - } - return h % HASHSIZE -} - -// For DW_CLS_string and _block, value should contain the length, and -// data the data, for _reference, value is 0 and data is a DWDie* to -// the referenced instance, for all others, value is the whole thing -// and data is null. - -type DWAttr struct { - link *DWAttr - atr uint16 // DW_AT_ - cls uint8 // DW_CLS_ - value int64 - data interface{} -} - -type DWDie struct { - abbrev int - link *DWDie - child *DWDie - attr *DWAttr - // offset into .debug_info section, i.e relative to - // infoo. only valid after call to putdie() - offs int64 - hash []*DWDie // optional index of children by name, enabled by mkindex() - hlink *DWDie // bucket chain in parent's index -} - -/* - * Root DIEs for compilation units, types and global variables. - */ -var dwroot DWDie - -var dwtypes DWDie - -var dwglobals DWDie - -func newattr(die *DWDie, attr uint16, cls int, value int64, data interface{}) *DWAttr { - a := new(DWAttr) - a.link = die.attr - die.attr = a - a.atr = attr - a.cls = uint8(cls) - a.value = value - a.data = data - return a -} - -// Each DIE (except the root ones) has at least 1 attribute: its -// name. getattr moves the desired one to the front so -// frequently searched ones are found faster. -func getattr(die *DWDie, attr uint16) *DWAttr { - if die.attr.atr == attr { - return die.attr - } - - a := die.attr - b := a.link - for b != nil { - if b.atr == attr { - a.link = b.link - b.link = die.attr - die.attr = b - return b - } - - a = b - b = b.link - } - - return nil -} - -// Every DIE has at least a DW_AT_name attribute (but it will only be -// written out if it is listed in the abbrev). If its parent is -// keeping an index, the new DIE will be inserted there. -func newdie(parent *DWDie, abbrev int, name string) *DWDie { - die := new(DWDie) - die.abbrev = abbrev - die.link = parent.child - parent.child = die - - newattr(die, DW_AT_name, DW_CLS_STRING, int64(len(name)), name) - - if parent.hash != nil { - h := int(dwarfhashstr(name)) - die.hlink = parent.hash[h] - parent.hash[h] = die - } - - return die -} - -func mkindex(die *DWDie) { - die.hash = make([]*DWDie, HASHSIZE) -} - -func walktypedef(die *DWDie) *DWDie { - // Resolve typedef if present. - if die.abbrev == DW_ABRV_TYPEDECL { - for attr := die.attr; attr != nil; attr = attr.link { - if attr.atr == DW_AT_type && attr.cls == DW_CLS_REFERENCE && attr.data != nil { - return attr.data.(*DWDie) - } - } - } - - return die -} - -// Find child by AT_name using hashtable if available or linear scan -// if not. -func find(die *DWDie, name string) *DWDie { - var a *DWDie - var b *DWDie - var die2 *DWDie - var h int - -top: - if die.hash == nil { - for a = die.child; a != nil; a = a.link { - if name == getattr(a, DW_AT_name).data { - return a - } - } - goto notfound - } - - h = int(dwarfhashstr(name)) - a = die.hash[h] - - if a == nil { - goto notfound - } - - if name == getattr(a, DW_AT_name).data { - return a - } - - // Move found ones to head of the list. - b = a.hlink - - for b != nil { - if name == getattr(b, DW_AT_name).data { - a.hlink = b.hlink - b.hlink = die.hash[h] - die.hash[h] = b - return b - } - - a = b - b = b.hlink - } - -notfound: - die2 = walktypedef(die) - if die2 != die { - die = die2 - goto top - } - - return nil -} - -func mustFind(die *DWDie, name string) *DWDie { - r := find(die, name) - if r == nil { - Exitf("dwarf find: %s %p has no %s", getattr(die, DW_AT_name).data, die, name) - } - return r -} - -func adddwarfrel(sec *LSym, sym *LSym, offsetbase int64, siz int, addend int64) { - r := Addrel(sec) - r.Sym = sym - r.Xsym = sym - r.Off = int32(Cpos() - offsetbase) - r.Siz = uint8(siz) - r.Type = obj.R_ADDR - r.Add = addend - r.Xadd = addend - if Iself && Thearch.Thechar == '6' { - addend = 0 - } - if HEADTYPE == obj.Hdarwin { - addend += sym.Value - } - switch siz { - case 4: - Thearch.Lput(uint32(addend)) - - case 8: - Thearch.Vput(uint64(addend)) - - default: - Diag("bad size in adddwarfrel") - } -} - -func newrefattr(die *DWDie, attr uint16, ref *DWDie) *DWAttr { - if ref == nil { - return nil - } - return newattr(die, attr, DW_CLS_REFERENCE, 0, ref) -} - -var fwdcount int - -func putattr(abbrev int, form int, cls int, value int64, data interface{}) { - switch form { - case DW_FORM_addr: // address - if Linkmode == LinkExternal { - value -= (data.(*LSym)).Value - adddwarfrel(infosec, data.(*LSym), infoo, Thearch.Ptrsize, value) - break - } - - addrput(value) - - case DW_FORM_block1: // block - if cls == DW_CLS_ADDRESS { - Cput(uint8(1 + Thearch.Ptrsize)) - Cput(DW_OP_addr) - if Linkmode == LinkExternal { - value -= (data.(*LSym)).Value - adddwarfrel(infosec, data.(*LSym), infoo, Thearch.Ptrsize, value) - break - } - - addrput(value) - break - } - - value &= 0xff - Cput(uint8(value)) - p := data.([]byte) - for i := 0; int64(i) < value; i++ { - Cput(uint8(p[i])) - } - - case DW_FORM_block2: // block - value &= 0xffff - - Thearch.Wput(uint16(value)) - p := data.([]byte) - for i := 0; int64(i) < value; i++ { - Cput(uint8(p[i])) - } - - case DW_FORM_block4: // block - value &= 0xffffffff - - Thearch.Lput(uint32(value)) - p := data.([]byte) - for i := 0; int64(i) < value; i++ { - Cput(uint8(p[i])) - } - - case DW_FORM_block: // block - uleb128put(value) - - p := data.([]byte) - for i := 0; int64(i) < value; i++ { - Cput(uint8(p[i])) - } - - case DW_FORM_data1: // constant - Cput(uint8(value)) - - case DW_FORM_data2: // constant - Thearch.Wput(uint16(value)) - - case DW_FORM_data4: // constant, {line,loclist,mac,rangelist}ptr - if Linkmode == LinkExternal && cls == DW_CLS_PTR { - adddwarfrel(infosec, linesym, infoo, 4, value) - break - } - - Thearch.Lput(uint32(value)) - - case DW_FORM_data8: // constant, {line,loclist,mac,rangelist}ptr - Thearch.Vput(uint64(value)) - - case DW_FORM_sdata: // constant - sleb128put(value) - - case DW_FORM_udata: // constant - uleb128put(value) - - case DW_FORM_string: // string - strnput(data.(string), int(value+1)) - - case DW_FORM_flag: // flag - if value != 0 { - Cput(1) - } else { - Cput(0) - } - - // In DWARF 2 (which is what we claim to generate), - // the ref_addr is the same size as a normal address. - // In DWARF 3 it is always 32 bits, unless emitting a large - // (> 4 GB of debug info aka "64-bit") unit, which we don't implement. - case DW_FORM_ref_addr: // reference to a DIE in the .info section - if data == nil { - Diag("dwarf: null reference in %d", abbrev) - if Thearch.Ptrsize == 8 { - Thearch.Vput(0) // invalid dwarf, gdb will complain. - } else { - Thearch.Lput(0) // invalid dwarf, gdb will complain. - } - } else { - off := (data.(*DWDie)).offs - if off == 0 { - fwdcount++ - } - if Linkmode == LinkExternal { - adddwarfrel(infosec, infosym, infoo, Thearch.Ptrsize, off) - break - } - - addrput(off) - } - - case DW_FORM_ref1, // reference within the compilation unit - DW_FORM_ref2, // reference - DW_FORM_ref4, // reference - DW_FORM_ref8, // reference - DW_FORM_ref_udata, // reference - - DW_FORM_strp, // string - DW_FORM_indirect: // (see Section 7.5.3) - fallthrough - default: - Exitf("dwarf: unsupported attribute form %d / class %d", form, cls) - } -} - -// Note that we can (and do) add arbitrary attributes to a DIE, but -// only the ones actually listed in the Abbrev will be written out. -func putattrs(abbrev int, attr *DWAttr) { -Outer: - for _, f := range abbrevs[abbrev].attr { - for ap := attr; ap != nil; ap = ap.link { - if ap.atr == f.attr { - putattr(abbrev, int(f.form), int(ap.cls), ap.value, ap.data) - continue Outer - } - } - - putattr(abbrev, int(f.form), 0, 0, nil) - } -} - -func putdies(die *DWDie) { - for ; die != nil; die = die.link { - putdie(die) - } -} - -func putdie(die *DWDie) { - die.offs = Cpos() - infoo - uleb128put(int64(die.abbrev)) - putattrs(die.abbrev, die.attr) - if abbrevs[die.abbrev].children != 0 { - putdies(die.child) - Cput(0) - } -} - -func reverselist(list **DWDie) { - curr := *list - var prev *DWDie - for curr != nil { - var next *DWDie = curr.link - curr.link = prev - prev = curr - curr = next - } - - *list = prev -} - -func reversetree(list **DWDie) { - reverselist(list) - for die := *list; die != nil; die = die.link { - if abbrevs[die.abbrev].children != 0 { - reversetree(&die.child) - } - } -} - -func newmemberoffsetattr(die *DWDie, offs int32) { - var block [20]byte - - i := 0 - block[i] = DW_OP_plus_uconst - i++ - i += uleb128enc(uint64(offs), block[i:]) - newattr(die, DW_AT_data_member_location, DW_CLS_BLOCK, int64(i), block[:i]) -} - -// GDB doesn't like DW_FORM_addr for DW_AT_location, so emit a -// location expression that evals to a const. -func newabslocexprattr(die *DWDie, addr int64, sym *LSym) { - newattr(die, DW_AT_location, DW_CLS_ADDRESS, addr, sym) - // below -} - -// Lookup predefined types -func lookup_or_diag(n string) *LSym { - s := Linkrlookup(Ctxt, n, 0) - if s == nil || s.Size == 0 { - Exitf("dwarf: missing type: %s", n) - } - - return s -} - -func dotypedef(parent *DWDie, name string, def *DWDie) { - // Only emit typedefs for real names. - if strings.HasPrefix(name, "map[") { - return - } - if strings.HasPrefix(name, "struct {") { - return - } - if strings.HasPrefix(name, "chan ") { - return - } - if name[0] == '[' || name[0] == '*' { - return - } - if def == nil { - Diag("dwarf: bad def in dotypedef") - } - - // The typedef entry must be created after the def, - // so that future lookups will find the typedef instead - // of the real definition. This hooks the typedef into any - // circular definition loops, so that gdb can understand them. - die := newdie(parent, DW_ABRV_TYPEDECL, name) - - newrefattr(die, DW_AT_type, def) -} - -// Define gotype, for composite ones recurse into constituents. -func defgotype(gotype *LSym) *DWDie { - if gotype == nil { - return mustFind(&dwtypes, "") - } - - if !strings.HasPrefix(gotype.Name, "type.") { - Diag("dwarf: type name doesn't start with \".type\": %s", gotype.Name) - return mustFind(&dwtypes, "") - } - - name := gotype.Name[5:] // could also decode from Type.string - - die := find(&dwtypes, name) - - if die != nil { - return die - } - - if false && Debug['v'] > 2 { - fmt.Printf("new type: %v\n", gotype) - } - - kind := decodetype_kind(gotype) - bytesize := decodetype_size(gotype) - - switch kind { - case obj.KindBool: - die = newdie(&dwtypes, DW_ABRV_BASETYPE, name) - newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_boolean, 0) - newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) - - case obj.KindInt, - obj.KindInt8, - obj.KindInt16, - obj.KindInt32, - obj.KindInt64: - die = newdie(&dwtypes, DW_ABRV_BASETYPE, name) - newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_signed, 0) - newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) - - case obj.KindUint, - obj.KindUint8, - obj.KindUint16, - obj.KindUint32, - obj.KindUint64, - obj.KindUintptr: - die = newdie(&dwtypes, DW_ABRV_BASETYPE, name) - newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_unsigned, 0) - newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) - - case obj.KindFloat32, - obj.KindFloat64: - die = newdie(&dwtypes, DW_ABRV_BASETYPE, name) - newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_float, 0) - newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) - - case obj.KindComplex64, - obj.KindComplex128: - die = newdie(&dwtypes, DW_ABRV_BASETYPE, name) - newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_complex_float, 0) - newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) - - case obj.KindArray: - die = newdie(&dwtypes, DW_ABRV_ARRAYTYPE, name) - dotypedef(&dwtypes, name, die) - newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) - s := decodetype_arrayelem(gotype) - newrefattr(die, DW_AT_type, defgotype(s)) - fld := newdie(die, DW_ABRV_ARRAYRANGE, "range") - - // use actual length not upper bound; correct for 0-length arrays. - newattr(fld, DW_AT_count, DW_CLS_CONSTANT, decodetype_arraylen(gotype), 0) - - newrefattr(fld, DW_AT_type, mustFind(&dwtypes, "uintptr")) - - case obj.KindChan: - die = newdie(&dwtypes, DW_ABRV_CHANTYPE, name) - newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) - s := decodetype_chanelem(gotype) - newrefattr(die, DW_AT_go_elem, defgotype(s)) - - case obj.KindFunc: - die = newdie(&dwtypes, DW_ABRV_FUNCTYPE, name) - dotypedef(&dwtypes, name, die) - newrefattr(die, DW_AT_type, mustFind(&dwtypes, "void")) - nfields := decodetype_funcincount(gotype) - var fld *DWDie - var s *LSym - for i := 0; i < nfields; i++ { - s = decodetype_funcintype(gotype, i) - fld = newdie(die, DW_ABRV_FUNCTYPEPARAM, s.Name[5:]) - newrefattr(fld, DW_AT_type, defgotype(s)) - } - - if decodetype_funcdotdotdot(gotype) != 0 { - newdie(die, DW_ABRV_DOTDOTDOT, "...") - } - nfields = decodetype_funcoutcount(gotype) - for i := 0; i < nfields; i++ { - s = decodetype_funcouttype(gotype, i) - fld = newdie(die, DW_ABRV_FUNCTYPEPARAM, s.Name[5:]) - newrefattr(fld, DW_AT_type, defptrto(defgotype(s))) - } - - case obj.KindInterface: - die = newdie(&dwtypes, DW_ABRV_IFACETYPE, name) - dotypedef(&dwtypes, name, die) - newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) - nfields := int(decodetype_ifacemethodcount(gotype)) - var s *LSym - if nfields == 0 { - s = lookup_or_diag("type.runtime.eface") - } else { - s = lookup_or_diag("type.runtime.iface") - } - newrefattr(die, DW_AT_type, defgotype(s)) - - case obj.KindMap: - die = newdie(&dwtypes, DW_ABRV_MAPTYPE, name) - s := decodetype_mapkey(gotype) - newrefattr(die, DW_AT_go_key, defgotype(s)) - s = decodetype_mapvalue(gotype) - newrefattr(die, DW_AT_go_elem, defgotype(s)) - - case obj.KindPtr: - die = newdie(&dwtypes, DW_ABRV_PTRTYPE, name) - dotypedef(&dwtypes, name, die) - s := decodetype_ptrelem(gotype) - newrefattr(die, DW_AT_type, defgotype(s)) - - case obj.KindSlice: - die = newdie(&dwtypes, DW_ABRV_SLICETYPE, name) - dotypedef(&dwtypes, name, die) - newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) - s := decodetype_arrayelem(gotype) - newrefattr(die, DW_AT_go_elem, defgotype(s)) - - case obj.KindString: - die = newdie(&dwtypes, DW_ABRV_STRINGTYPE, name) - newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) - - case obj.KindStruct: - die = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, name) - dotypedef(&dwtypes, name, die) - newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) - nfields := decodetype_structfieldcount(gotype) - var f string - var fld *DWDie - var s *LSym - for i := 0; i < nfields; i++ { - f = decodetype_structfieldname(gotype, i) - s = decodetype_structfieldtype(gotype, i) - if f == "" { - f = s.Name[5:] // skip "type." - } - fld = newdie(die, DW_ABRV_STRUCTFIELD, f) - newrefattr(fld, DW_AT_type, defgotype(s)) - newmemberoffsetattr(fld, int32(decodetype_structfieldoffs(gotype, i))) - } - - case obj.KindUnsafePointer: - die = newdie(&dwtypes, DW_ABRV_BARE_PTRTYPE, name) - - default: - Diag("dwarf: definition of unknown kind %d: %s", kind, gotype.Name) - die = newdie(&dwtypes, DW_ABRV_TYPEDECL, name) - newrefattr(die, DW_AT_type, mustFind(&dwtypes, "")) - } - - newattr(die, DW_AT_go_kind, DW_CLS_CONSTANT, int64(kind), 0) - - return die -} - -// Find or construct *T given T. -func defptrto(dwtype *DWDie) *DWDie { - ptrname := fmt.Sprintf("*%s", getattr(dwtype, DW_AT_name).data) - die := find(&dwtypes, ptrname) - if die == nil { - die = newdie(&dwtypes, DW_ABRV_PTRTYPE, ptrname) - newrefattr(die, DW_AT_type, dwtype) - } - - return die -} - -// Copies src's children into dst. Copies attributes by value. -// DWAttr.data is copied as pointer only. If except is one of -// the top-level children, it will not be copied. -func copychildrenexcept(dst *DWDie, src *DWDie, except *DWDie) { - var c *DWDie - var a *DWAttr - - for src = src.child; src != nil; src = src.link { - if src == except { - continue - } - c = newdie(dst, src.abbrev, getattr(src, DW_AT_name).data.(string)) - for a = src.attr; a != nil; a = a.link { - newattr(c, a.atr, int(a.cls), a.value, a.data) - } - copychildrenexcept(c, src, nil) - } - - reverselist(&dst.child) -} - -func copychildren(dst *DWDie, src *DWDie) { - copychildrenexcept(dst, src, nil) -} - -// Search children (assumed to have DW_TAG_member) for the one named -// field and set its DW_AT_type to dwtype -func substitutetype(structdie *DWDie, field string, dwtype *DWDie) { - child := mustFind(structdie, field) - if child == nil { - return - } - - a := getattr(child, DW_AT_type) - if a != nil { - a.data = dwtype - } else { - newrefattr(child, DW_AT_type, dwtype) - } -} - -func synthesizestringtypes(die *DWDie) { - prototype := walktypedef(defgotype(lookup_or_diag("type.runtime._string"))) - if prototype == nil { - return - } - - for ; die != nil; die = die.link { - if die.abbrev != DW_ABRV_STRINGTYPE { - continue - } - copychildren(die, prototype) - } -} - -func synthesizeslicetypes(die *DWDie) { - prototype := walktypedef(defgotype(lookup_or_diag("type.runtime.slice"))) - if prototype == nil { - return - } - - var elem *DWDie - for ; die != nil; die = die.link { - if die.abbrev != DW_ABRV_SLICETYPE { - continue - } - copychildren(die, prototype) - elem = getattr(die, DW_AT_go_elem).data.(*DWDie) - substitutetype(die, "array", defptrto(elem)) - } -} - -func mkinternaltypename(base string, arg1 string, arg2 string) string { - var buf string - - if arg2 == "" { - buf = fmt.Sprintf("%s<%s>", base, arg1) - } else { - buf = fmt.Sprintf("%s<%s,%s>", base, arg1, arg2) - } - n := buf - return n -} - -// synthesizemaptypes is way too closely married to runtime/hashmap.c -const ( - MaxKeySize = 128 - MaxValSize = 128 - BucketSize = 8 -) - -func synthesizemaptypes(die *DWDie) { - hash := walktypedef(defgotype(lookup_or_diag("type.runtime.hmap"))) - bucket := walktypedef(defgotype(lookup_or_diag("type.runtime.bmap"))) - - if hash == nil { - return - } - - var a *DWAttr - var dwh *DWDie - var dwhb *DWDie - var dwhk *DWDie - var dwhv *DWDie - var fld *DWDie - var indirect_key int - var indirect_val int - var keysize int - var keytype *DWDie - var t *DWDie - var valsize int - var valtype *DWDie - for ; die != nil; die = die.link { - if die.abbrev != DW_ABRV_MAPTYPE { - continue - } - - keytype = walktypedef(getattr(die, DW_AT_go_key).data.(*DWDie)) - valtype = walktypedef(getattr(die, DW_AT_go_elem).data.(*DWDie)) - - // compute size info like hashmap.c does. - a = getattr(keytype, DW_AT_byte_size) - - if a != nil { - keysize = int(a.value) - } else { - keysize = Thearch.Ptrsize - } - a = getattr(valtype, DW_AT_byte_size) - if a != nil { - valsize = int(a.value) - } else { - valsize = Thearch.Ptrsize - } - indirect_key = 0 - indirect_val = 0 - if keysize > MaxKeySize { - keysize = Thearch.Ptrsize - indirect_key = 1 - } - - if valsize > MaxValSize { - valsize = Thearch.Ptrsize - indirect_val = 1 - } - - // Construct type to represent an array of BucketSize keys - dwhk = newdie(&dwtypes, DW_ABRV_ARRAYTYPE, mkinternaltypename("[]key", getattr(keytype, DW_AT_name).data.(string), "")) - - newattr(dwhk, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize*int64(keysize), 0) - t = keytype - if indirect_key != 0 { - t = defptrto(keytype) - } - newrefattr(dwhk, DW_AT_type, t) - fld = newdie(dwhk, DW_ABRV_ARRAYRANGE, "size") - newattr(fld, DW_AT_count, DW_CLS_CONSTANT, BucketSize, 0) - newrefattr(fld, DW_AT_type, mustFind(&dwtypes, "uintptr")) - - // Construct type to represent an array of BucketSize values - dwhv = newdie(&dwtypes, DW_ABRV_ARRAYTYPE, mkinternaltypename("[]val", getattr(valtype, DW_AT_name).data.(string), "")) - - newattr(dwhv, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize*int64(valsize), 0) - t = valtype - if indirect_val != 0 { - t = defptrto(valtype) - } - newrefattr(dwhv, DW_AT_type, t) - fld = newdie(dwhv, DW_ABRV_ARRAYRANGE, "size") - newattr(fld, DW_AT_count, DW_CLS_CONSTANT, BucketSize, 0) - newrefattr(fld, DW_AT_type, mustFind(&dwtypes, "uintptr")) - - // Construct bucket - dwhb = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, mkinternaltypename("bucket", getattr(keytype, DW_AT_name).data.(string), getattr(valtype, DW_AT_name).data.(string))) - - // Copy over all fields except the field "data" from the generic bucket. - // "data" will be replaced with keys/values below. - copychildrenexcept(dwhb, bucket, find(bucket, "data")) - - fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "keys") - newrefattr(fld, DW_AT_type, dwhk) - newmemberoffsetattr(fld, BucketSize) - fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "values") - newrefattr(fld, DW_AT_type, dwhv) - newmemberoffsetattr(fld, BucketSize+BucketSize*int32(keysize)) - fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "overflow") - newrefattr(fld, DW_AT_type, defptrto(dwhb)) - newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))) - if Thearch.Regsize > Thearch.Ptrsize { - fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "pad") - newrefattr(fld, DW_AT_type, mustFind(&dwtypes, "uintptr")) - newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))+int32(Thearch.Ptrsize)) - } - - newattr(dwhb, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize+BucketSize*int64(keysize)+BucketSize*int64(valsize)+int64(Thearch.Regsize), 0) - - // Construct hash - dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, mkinternaltypename("hash", getattr(keytype, DW_AT_name).data.(string), getattr(valtype, DW_AT_name).data.(string))) - - copychildren(dwh, hash) - substitutetype(dwh, "buckets", defptrto(dwhb)) - substitutetype(dwh, "oldbuckets", defptrto(dwhb)) - newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, getattr(hash, DW_AT_byte_size).value, nil) - - // make map type a pointer to hash - newrefattr(die, DW_AT_type, defptrto(dwh)) - } -} - -func synthesizechantypes(die *DWDie) { - sudog := walktypedef(defgotype(lookup_or_diag("type.runtime.sudog"))) - waitq := walktypedef(defgotype(lookup_or_diag("type.runtime.waitq"))) - hchan := walktypedef(defgotype(lookup_or_diag("type.runtime.hchan"))) - if sudog == nil || waitq == nil || hchan == nil { - return - } - - sudogsize := int(getattr(sudog, DW_AT_byte_size).value) - - var a *DWAttr - var dwh *DWDie - var dws *DWDie - var dww *DWDie - var elemsize int - var elemtype *DWDie - for ; die != nil; die = die.link { - if die.abbrev != DW_ABRV_CHANTYPE { - continue - } - elemtype = getattr(die, DW_AT_go_elem).data.(*DWDie) - a = getattr(elemtype, DW_AT_byte_size) - if a != nil { - elemsize = int(a.value) - } else { - elemsize = Thearch.Ptrsize - } - - // sudog - dws = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, mkinternaltypename("sudog", getattr(elemtype, DW_AT_name).data.(string), "")) - - copychildren(dws, sudog) - substitutetype(dws, "elem", elemtype) - if elemsize > 8 { - elemsize -= 8 - } else { - elemsize = 0 - } - newattr(dws, DW_AT_byte_size, DW_CLS_CONSTANT, int64(sudogsize)+int64(elemsize), nil) - - // waitq - dww = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, mkinternaltypename("waitq", getattr(elemtype, DW_AT_name).data.(string), "")) - - copychildren(dww, waitq) - substitutetype(dww, "first", defptrto(dws)) - substitutetype(dww, "last", defptrto(dws)) - newattr(dww, DW_AT_byte_size, DW_CLS_CONSTANT, getattr(waitq, DW_AT_byte_size).value, nil) - - // hchan - dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, mkinternaltypename("hchan", getattr(elemtype, DW_AT_name).data.(string), "")) - - copychildren(dwh, hchan) - substitutetype(dwh, "recvq", dww) - substitutetype(dwh, "sendq", dww) - newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, getattr(hchan, DW_AT_byte_size).value, nil) - - newrefattr(die, DW_AT_type, defptrto(dwh)) - } -} - -// For use with pass.c::genasmsym -func defdwsymb(sym *LSym, s string, t int, v int64, size int64, ver int, gotype *LSym) { - if strings.HasPrefix(s, "go.string.") { - return - } - - if strings.HasPrefix(s, "type.") && s != "type.*" && !strings.HasPrefix(s, "type..") { - defgotype(sym) - return - } - - var dv *DWDie - - var dt *DWDie - switch t { - default: - return - - case 'd', 'b', 'D', 'B': - dv = newdie(&dwglobals, DW_ABRV_VARIABLE, s) - newabslocexprattr(dv, v, sym) - if ver == 0 { - newattr(dv, DW_AT_external, DW_CLS_FLAG, 1, 0) - } - fallthrough - - case 'a', 'p': - dt = defgotype(gotype) - } - - if dv != nil { - newrefattr(dv, DW_AT_type, dt) - } -} - -func movetomodule(parent *DWDie) { - die := dwroot.child.child - for die.link != nil { - die = die.link - } - die.link = parent.child -} - -// If the pcln table contains runtime/runtime.go, use that to set gdbscript path. -func finddebugruntimepath(s *LSym) { - if gdbscript != "" { - return - } - - var f *LSym - var p string - for i := 0; i < s.Pcln.Nfile; i++ { - f = s.Pcln.File[i] - _ = p - if i := strings.Index(f.Name, "runtime/runtime.go"); i >= 0 { - gdbscript = f.Name[:i] + "runtime/runtime-gdb.py" - break - } - } -} - -/* - * Generate short opcodes when possible, long ones when necessary. - * See section 6.2.5 - */ -const ( - LINE_BASE = -1 - LINE_RANGE = 4 - OPCODE_BASE = 10 -) - -func putpclcdelta(delta_pc int64, delta_lc int64) { - if LINE_BASE <= delta_lc && delta_lc < LINE_BASE+LINE_RANGE { - var opcode int64 = OPCODE_BASE + (delta_lc - LINE_BASE) + (LINE_RANGE * delta_pc) - if OPCODE_BASE <= opcode && opcode < 256 { - Cput(uint8(opcode)) - return - } - } - - if delta_pc != 0 { - Cput(DW_LNS_advance_pc) - sleb128put(delta_pc) - } - - Cput(DW_LNS_advance_line) - sleb128put(delta_lc) - Cput(DW_LNS_copy) -} - -func newcfaoffsetattr(die *DWDie, offs int32) { - var block [20]byte - - i := 0 - - block[i] = DW_OP_call_frame_cfa - i++ - if offs != 0 { - block[i] = DW_OP_consts - i++ - i += sleb128enc(int64(offs), block[i:]) - block[i] = DW_OP_plus - i++ - } - - newattr(die, DW_AT_location, DW_CLS_BLOCK, int64(i), block[:i]) -} - -func mkvarname(name string, da int) string { - buf := fmt.Sprintf("%s#%d", name, da) - n := buf - return n -} - -/* - * Walk prog table, emit line program and build DIE tree. - */ - -// flush previous compilation unit. -func flushunit(dwinfo *DWDie, pc int64, pcsym *LSym, unitstart int64, header_length int32) { - if dwinfo != nil && pc != 0 { - newattr(dwinfo, DW_AT_high_pc, DW_CLS_ADDRESS, pc+1, pcsym) - } - - if unitstart >= 0 { - Cput(0) // start extended opcode - uleb128put(1) - Cput(DW_LNE_end_sequence) - - here := Cpos() - Cseek(unitstart) - Thearch.Lput(uint32(here - unitstart - 4)) // unit_length - Thearch.Wput(2) // dwarf version - Thearch.Lput(uint32(header_length)) // header length starting here - Cseek(here) - } -} - -func getCompilationDir() string { - if dir, err := os.Getwd(); err == nil { - return dir - } - return "/" -} - -func writelines() { - if linesec == nil { - linesec = Linklookup(Ctxt, ".dwarfline", 0) - } - linesec.R = linesec.R[:0] - - unitstart := int64(-1) - headerend := int64(-1) - epc := int64(0) - var epcs *LSym - lineo = Cpos() - var dwinfo *DWDie - - flushunit(dwinfo, epc, epcs, unitstart, int32(headerend-unitstart-10)) - unitstart = Cpos() - - lang := DW_LANG_Go - - s := Ctxt.Textp - - dwinfo = newdie(&dwroot, DW_ABRV_COMPUNIT, "go") - 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 - Thearch.Lput(0) // unit_length (*), will be filled in by flushunit. - Thearch.Wput(2) // dwarf version (appendix F) - Thearch.Lput(0) // header_length (*), filled in by flushunit. - - // cpos == unitstart + 4 + 2 + 4 - Cput(1) // minimum_instruction_length - Cput(1) // default_is_stmt - Cput(LINE_BASE & 0xFF) // line_base - Cput(LINE_RANGE) // line_range - Cput(OPCODE_BASE) // opcode_base - Cput(0) // standard_opcode_lengths[1] - Cput(1) // standard_opcode_lengths[2] - Cput(1) // standard_opcode_lengths[3] - Cput(1) // standard_opcode_lengths[4] - Cput(1) // standard_opcode_lengths[5] - Cput(0) // standard_opcode_lengths[6] - Cput(0) // standard_opcode_lengths[7] - Cput(0) // standard_opcode_lengths[8] - Cput(1) // standard_opcode_lengths[9] - Cput(0) // include_directories (empty) - - files := make([]*LSym, Ctxt.Nhistfile) - - for f := Ctxt.Filesyms; f != nil; f = f.Next { - files[f.Value-1] = f - } - - for i := 0; int32(i) < Ctxt.Nhistfile; i++ { - strnput(files[i].Name, len(files[i].Name)+4) - } - - // 4 zeros: the string termination + 3 fields. - Cput(0) - // terminate file_names. - headerend = Cpos() - - Cput(0) // start extended opcode - uleb128put(1 + int64(Thearch.Ptrsize)) - Cput(DW_LNE_set_address) - - pc := s.Value - line := 1 - file := 1 - if Linkmode == LinkExternal { - adddwarfrel(linesec, s, lineo, Thearch.Ptrsize, 0) - } else { - addrput(pc) - } - - var a *Auto - var da int - var dt int - var dwfunc *DWDie - var dws **DWDie - var dwvar *DWDie - var n string - var nn string - var offs int64 - var pcfile Pciter - var pcline Pciter - var varhash [HASHSIZE]*DWDie - for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { - s = Ctxt.Cursym - - dwfunc = newdie(dwinfo, DW_ABRV_FUNCTION, s.Name) - newattr(dwfunc, DW_AT_low_pc, DW_CLS_ADDRESS, s.Value, s) - epc = s.Value + s.Size - epcs = s - newattr(dwfunc, DW_AT_high_pc, DW_CLS_ADDRESS, epc, s) - if s.Version == 0 { - newattr(dwfunc, DW_AT_external, DW_CLS_FLAG, 1, 0) - } - - if s.Pcln == nil { - continue - } - - finddebugruntimepath(s) - - pciterinit(Ctxt, &pcfile, &s.Pcln.Pcfile) - pciterinit(Ctxt, &pcline, &s.Pcln.Pcline) - epc = pc - for pcfile.done == 0 && pcline.done == 0 { - if epc-s.Value >= int64(pcfile.nextpc) { - pciternext(&pcfile) - continue - } - - if epc-s.Value >= int64(pcline.nextpc) { - pciternext(&pcline) - continue - } - - if int32(file) != pcfile.value { - Cput(DW_LNS_set_file) - uleb128put(int64(pcfile.value)) - file = int(pcfile.value) - } - - putpclcdelta(s.Value+int64(pcline.pc)-pc, int64(pcline.value)-int64(line)) - - pc = s.Value + int64(pcline.pc) - line = int(pcline.value) - if pcfile.nextpc < pcline.nextpc { - epc = int64(pcfile.nextpc) - } else { - epc = int64(pcline.nextpc) - } - epc += s.Value - } - - da = 0 - dwfunc.hash = varhash[:] // enable indexing of children by name - varhash = [HASHSIZE]*DWDie{} - for a = s.Autom; a != nil; a = a.Link { - switch a.Name { - case obj.A_AUTO: - dt = DW_ABRV_AUTO - 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 - } - - if strings.Contains(a.Asym.Name, ".autotmp_") { - continue - } - if find(dwfunc, a.Asym.Name) != nil { - n = mkvarname(a.Asym.Name, da) - } else { - n = a.Asym.Name - } - - // Drop the package prefix from locals and arguments. - _ = nn - if i := strings.LastIndex(n, "."); i >= 0 { - n = n[i+1:] - } - - dwvar = newdie(dwfunc, dt, n) - newcfaoffsetattr(dwvar, int32(offs)) - newrefattr(dwvar, DW_AT_type, defgotype(a.Gotype)) - - // push dwvar down dwfunc->child to preserve order - newattr(dwvar, DW_AT_internal_location, DW_CLS_CONSTANT, offs, nil) - - dwfunc.child = dwvar.link // take dwvar out from the top of the list - for dws = &dwfunc.child; *dws != nil; dws = &(*dws).link { - if offs > getattr(*dws, DW_AT_internal_location).value { - break - } - } - dwvar.link = *dws - *dws = dwvar - - da++ - } - - dwfunc.hash = nil - } - - flushunit(dwinfo, epc, epcs, unitstart, int32(headerend-unitstart-10)) - linesize = Cpos() - lineo -} - -/* - * Emit .debug_frame - */ -const ( - CIERESERVE = 16 - DATAALIGNMENTFACTOR = -4 -) - -func putpccfadelta(deltapc int64, cfa int64) { - Cput(DW_CFA_def_cfa_offset_sf) - sleb128put(cfa / DATAALIGNMENTFACTOR) - - if deltapc < 0x40 { - Cput(uint8(DW_CFA_advance_loc + deltapc)) - } else if deltapc < 0x100 { - Cput(DW_CFA_advance_loc1) - Cput(uint8(deltapc)) - } else if deltapc < 0x10000 { - Cput(DW_CFA_advance_loc2) - Thearch.Wput(uint16(deltapc)) - } else { - Cput(DW_CFA_advance_loc4) - Thearch.Lput(uint32(deltapc)) - } -} - -func writeframes() { - if framesec == nil { - framesec = Linklookup(Ctxt, ".dwarfframe", 0) - } - framesec.R = framesec.R[:0] - 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(int64(Thearch.Dwarfreglr)) // return_address_register - - Cput(DW_CFA_def_cfa) - - uleb128put(int64(Thearch.Dwarfregsp)) // register SP (**ABI-dependent, defined in l.h) - if haslinkregister() { - uleb128put(int64(0)) // offset - } else { - uleb128put(int64(Thearch.Ptrsize)) // offset - } - - 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() - - if pad < 0 { - Exitf("dwarf: CIERESERVE too small by %d bytes.", -pad) - } - - strnput("", int(pad)) - - var fdeo int64 - var fdesize int64 - var nextpc uint32 - var pcsp Pciter - var s *LSym - for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { - s = Ctxt.Cursym - if s.Pcln == nil { - continue - } - - fdeo = Cpos() - - // Emit a FDE, Section 6.4.1, starting wit a placeholder. - Thearch.Lput(0) // length, must be multiple of thearch.ptrsize - Thearch.Lput(0) // Pointer to the CIE above, at offset 0 - addrput(0) // initial location - addrput(0) // address range - - for pciterinit(Ctxt, &pcsp, &s.Pcln.Pcsp); pcsp.done == 0; pciternext(&pcsp) { - nextpc = pcsp.nextpc - - // pciterinit goes up to the end of the function, - // but DWARF expects us to stop just before the end. - if int64(nextpc) == s.Size { - nextpc-- - if nextpc < pcsp.pc { - continue - } - } - - 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. - pad = Rnd(fdesize, int64(Thearch.Ptrsize)) - fdesize - strnput("", int(pad)) - fdesize += pad - - // Emit the FDE header for real, Section 6.4.1. - Cseek(fdeo) - - Thearch.Lput(uint32(fdesize)) - if Linkmode == LinkExternal { - adddwarfrel(framesec, framesym, frameo, 4, 0) - adddwarfrel(framesec, s, frameo, Thearch.Ptrsize, 0) - } else { - Thearch.Lput(0) - addrput(s.Value) - } - - addrput(s.Size) - Cseek(fdeo + 4 + fdesize) - } - - Cflush() - framesize = Cpos() - frameo -} - -/* - * Walk DWarfDebugInfoEntries, and emit .debug_info - */ -const ( - COMPUNITHEADERSIZE = 4 + 2 + 4 + 1 -) - -func writeinfo() { - fwdcount = 0 - if infosec == nil { - infosec = Linklookup(Ctxt, ".dwarfinfo", 0) - } - infosec.R = infosec.R[:0] - - if arangessec == nil { - arangessec = Linklookup(Ctxt, ".dwarfaranges", 0) - } - arangessec.R = arangessec.R[:0] - - var here int64 - var unitstart int64 - for compunit := dwroot.child; compunit != nil; compunit = compunit.link { - unitstart = Cpos() - - // Write .debug_info Compilation Unit Header (sec 7.5.1) - // Fields marked with (*) must be changed for 64-bit dwarf - // This must match COMPUNITHEADERSIZE above. - Thearch.Lput(0) // unit_length (*), will be filled in later. - Thearch.Wput(2) // dwarf version (appendix F) - - // debug_abbrev_offset (*) - if Linkmode == LinkExternal { - adddwarfrel(infosec, abbrevsym, infoo, 4, 0) - } else { - Thearch.Lput(0) - } - - Cput(uint8(Thearch.Ptrsize)) // address_size - - putdie(compunit) - - here = Cpos() - Cseek(unitstart) - Thearch.Lput(uint32(here - unitstart - 4)) // exclude the length field. - Cseek(here) - } - - Cflush() -} - -/* - * Emit .debug_pubnames/_types. _info must have been written before, - * because we need die->offs and infoo/infosize; - */ -func ispubname(die *DWDie) bool { - switch die.abbrev { - case DW_ABRV_FUNCTION, DW_ABRV_VARIABLE: - a := getattr(die, DW_AT_external) - return a != nil && a.value != 0 - } - - return false -} - -func ispubtype(die *DWDie) bool { - return die.abbrev >= DW_ABRV_NULLTYPE -} - -func writepub(ispub func(*DWDie) bool) int64 { - var die *DWDie - var dwa *DWAttr - var unitstart int64 - var unitend int64 - var here int64 - - sectionstart := Cpos() - - for compunit := dwroot.child; compunit != nil; compunit = compunit.link { - unitstart = compunit.offs - COMPUNITHEADERSIZE - if compunit.link != nil { - unitend = compunit.link.offs - COMPUNITHEADERSIZE - } else { - unitend = infoo + infosize - } - - // Write .debug_pubnames/types Header (sec 6.1.1) - Thearch.Lput(0) // unit_length (*), will be filled in later. - Thearch.Wput(2) // dwarf version (appendix F) - Thearch.Lput(uint32(unitstart)) // debug_info_offset (of the Comp unit Header) - Thearch.Lput(uint32(unitend - unitstart)) // debug_info_length - - for die = compunit.child; die != nil; die = die.link { - if !ispub(die) { - continue - } - Thearch.Lput(uint32(die.offs - unitstart)) - dwa = getattr(die, DW_AT_name) - strnput(dwa.data.(string), int(dwa.value+1)) - } - - Thearch.Lput(0) - - here = Cpos() - Cseek(sectionstart) - Thearch.Lput(uint32(here - sectionstart - 4)) // exclude the length field. - Cseek(here) - } - - return sectionstart -} - -/* - * emit .debug_aranges. _info must have been written before, - * because we need die->offs of dw_globals. - */ -func writearanges() int64 { - var b *DWAttr - var e *DWAttr - var value int64 - - sectionstart := Cpos() - headersize := int(Rnd(4+2+4+1+1, int64(Thearch.Ptrsize))) // don't count unit_length field itself - - for compunit := dwroot.child; compunit != nil; compunit = compunit.link { - b = getattr(compunit, DW_AT_low_pc) - if b == nil { - continue - } - e = getattr(compunit, DW_AT_high_pc) - if e == nil { - continue - } - - // Write .debug_aranges Header + entry (sec 6.1.2) - Thearch.Lput(uint32(headersize) + 4*uint32(Thearch.Ptrsize) - 4) // unit_length (*) - Thearch.Wput(2) // dwarf version (appendix F) - - value = compunit.offs - COMPUNITHEADERSIZE // debug_info_offset - if Linkmode == LinkExternal { - adddwarfrel(arangessec, infosym, sectionstart, 4, value) - } else { - Thearch.Lput(uint32(value)) - } - - Cput(uint8(Thearch.Ptrsize)) // address_size - Cput(0) // segment_size - strnput("", headersize-(4+2+4+1+1)) // align to thearch.ptrsize - - if Linkmode == LinkExternal { - adddwarfrel(arangessec, b.data.(*LSym), sectionstart, Thearch.Ptrsize, b.value-(b.data.(*LSym)).Value) - } else { - addrput(b.value) - } - - addrput(e.value - b.value) - addrput(0) - addrput(0) - } - - Cflush() - return sectionstart -} - -func writegdbscript() int64 { - sectionstart := Cpos() - - if gdbscript != "" { - Cput(1) // magic 1 byte? - strnput(gdbscript, len(gdbscript)+1) - Cflush() - } - - return sectionstart -} - -func align(size int64) { - if HEADTYPE == obj.Hwindows { // Only Windows PE need section align. - strnput("", int(Rnd(size, PEFILEALIGN)-size)) - } -} - -func writedwarfreloc(s *LSym) int64 { - var i int - var r *Reloc - - start := Cpos() - for ri := 0; ri < len(s.R); ri++ { - r = &s.R[ri] - if Iself { - i = Thearch.Elfreloc1(r, int64(r.Off)) - } else if HEADTYPE == obj.Hdarwin { - i = Thearch.Machoreloc1(r, int64(r.Off)) - } else { - i = -1 - } - if i < 0 { - Diag("unsupported obj reloc %d/%d to %s", r.Type, r.Siz, r.Sym.Name) - } - } - - 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 - * the per-compilation unit part of the DIE tree, while simultaneously - * emitting the debug_line section. When the final tree contains - * forward references, it will write the debug_info section in 2 - * passes. - * - */ -func Dwarfemitdebugsections() { - if Debug['w'] != 0 { // disable dwarf - 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. - newattr(&dwtypes, DW_AT_name, DW_CLS_STRING, int64(len("dwtypes")), "dwtypes") - - mkindex(&dwroot) - mkindex(&dwtypes) - mkindex(&dwglobals) - - // Some types that must exist to define other ones. - newdie(&dwtypes, DW_ABRV_NULLTYPE, "") - - newdie(&dwtypes, DW_ABRV_NULLTYPE, "void") - newdie(&dwtypes, DW_ABRV_BARE_PTRTYPE, "unsafe.Pointer") - - die := newdie(&dwtypes, DW_ABRV_BASETYPE, "uintptr") // needed for array size - newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_unsigned, 0) - newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, int64(Thearch.Ptrsize), 0) - newattr(die, DW_AT_go_kind, DW_CLS_CONSTANT, obj.KindUintptr, 0) - - // Needed by the prettyprinter code for interface inspection. - defgotype(lookup_or_diag("type.runtime._type")) - - defgotype(lookup_or_diag("type.runtime.interfacetype")) - defgotype(lookup_or_diag("type.runtime.itab")) - - genasmsym(defdwsymb) - - writeabbrev() - align(abbrevsize) - writelines() - align(linesize) - writeframes() - align(framesize) - - synthesizestringtypes(dwtypes.child) - synthesizeslicetypes(dwtypes.child) - synthesizemaptypes(dwtypes.child) - synthesizechantypes(dwtypes.child) - - reversetree(&dwroot.child) - reversetree(&dwtypes.child) - reversetree(&dwglobals.child) - - movetomodule(&dwtypes) - movetomodule(&dwglobals) - - infoo = Cpos() - writeinfo() - infoe := Cpos() - pubnameso = infoe - pubtypeso = infoe - arangeso = infoe - gdbscripto = infoe - - if fwdcount > 0 { - if Debug['v'] != 0 { - fmt.Fprintf(&Bso, "%5.2f dwarf pass 2.\n", obj.Cputime()) - } - Cseek(infoo) - writeinfo() - if fwdcount > 0 { - Exitf("dwarf: unresolved references after first dwarf info pass") - } - - if infoe != Cpos() { - Exitf("dwarf: inconsistent second dwarf info pass") - } - } - - infosize = infoe - infoo - align(infosize) - - pubnameso = writepub(ispubname) - pubnamessize = Cpos() - pubnameso - align(pubnamessize) - - pubtypeso = writepub(ispubtype) - pubtypessize = Cpos() - pubtypeso - align(pubtypessize) - - arangeso = writearanges() - arangessize = Cpos() - arangeso - align(arangessize) - - gdbscripto = writegdbscript() - gdbscriptsize = Cpos() - gdbscripto - align(gdbscriptsize) - - 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) - - arangesreloco = writedwarfreloc(arangessec) - arangesrelocsize = Cpos() - arangesreloco - align(arangesrelocsize) - - linereloco = writedwarfreloc(linesec) - linerelocsize = Cpos() - linereloco - align(linerelocsize) - - framereloco = writedwarfreloc(framesec) - framerelocsize = Cpos() - framereloco - align(framerelocsize) -} - -/* - * Elf. - */ -const ( - ElfStrDebugAbbrev = iota - ElfStrDebugAranges - ElfStrDebugFrame - ElfStrDebugInfo - ElfStrDebugLine - ElfStrDebugLoc - ElfStrDebugMacinfo - ElfStrDebugPubNames - ElfStrDebugPubTypes - ElfStrDebugRanges - ElfStrDebugStr - ElfStrGDBScripts - ElfStrRelDebugInfo - ElfStrRelDebugAranges - ElfStrRelDebugLine - ElfStrRelDebugFrame - NElfStrDbg -) - -var elfstrdbg [NElfStrDbg]int64 - -func dwarfaddshstrings(shstrtab *LSym) { - if Debug['w'] != 0 { // disable dwarf - return - } - - elfstrdbg[ElfStrDebugAbbrev] = Addstring(shstrtab, ".debug_abbrev") - elfstrdbg[ElfStrDebugAranges] = Addstring(shstrtab, ".debug_aranges") - elfstrdbg[ElfStrDebugFrame] = Addstring(shstrtab, ".debug_frame") - elfstrdbg[ElfStrDebugInfo] = Addstring(shstrtab, ".debug_info") - elfstrdbg[ElfStrDebugLine] = Addstring(shstrtab, ".debug_line") - elfstrdbg[ElfStrDebugLoc] = Addstring(shstrtab, ".debug_loc") - elfstrdbg[ElfStrDebugMacinfo] = Addstring(shstrtab, ".debug_macinfo") - elfstrdbg[ElfStrDebugPubNames] = Addstring(shstrtab, ".debug_pubnames") - elfstrdbg[ElfStrDebugPubTypes] = Addstring(shstrtab, ".debug_pubtypes") - elfstrdbg[ElfStrDebugRanges] = Addstring(shstrtab, ".debug_ranges") - elfstrdbg[ElfStrDebugStr] = Addstring(shstrtab, ".debug_str") - elfstrdbg[ElfStrGDBScripts] = Addstring(shstrtab, ".debug_gdb_scripts") - if Linkmode == LinkExternal { - switch Thearch.Thechar { - case '6', '7', '9': - elfstrdbg[ElfStrRelDebugInfo] = Addstring(shstrtab, ".rela.debug_info") - elfstrdbg[ElfStrRelDebugAranges] = Addstring(shstrtab, ".rela.debug_aranges") - elfstrdbg[ElfStrRelDebugLine] = Addstring(shstrtab, ".rela.debug_line") - elfstrdbg[ElfStrRelDebugFrame] = Addstring(shstrtab, ".rela.debug_frame") - default: - elfstrdbg[ElfStrRelDebugInfo] = Addstring(shstrtab, ".rel.debug_info") - elfstrdbg[ElfStrRelDebugAranges] = Addstring(shstrtab, ".rel.debug_aranges") - 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 - } -} - -// Add section symbols for DWARF debug info. This is called before -// dwarfaddelfheaders. -func dwarfaddelfsectionsyms() { - if infosym != nil { - infosympos = Cpos() - putelfsectionsym(infosym, 0) - } - - if abbrevsym != nil { - abbrevsympos = Cpos() - putelfsectionsym(abbrevsym, 0) - } - - if linesym != nil { - linesympos = Cpos() - putelfsectionsym(linesym, 0) - } - - if framesym != nil { - framesympos = Cpos() - putelfsectionsym(framesym, 0) - } -} - -func dwarfaddelfrelocheader(elfstr int, shdata *ElfShdr, off int64, size int64) { - sh := newElfShdr(elfstrdbg[elfstr]) - switch Thearch.Thechar { - case '6', '7', '9': - sh.type_ = SHT_RELA - default: - sh.type_ = SHT_REL - } - - sh.entsize = uint64(Thearch.Ptrsize) * 2 - if sh.type_ == SHT_RELA { - sh.entsize += uint64(Thearch.Ptrsize) - } - sh.link = uint32(elfshname(".symtab").shnum) - sh.info = uint32(shdata.shnum) - sh.off = uint64(off) - sh.size = uint64(size) - sh.addralign = uint64(Thearch.Ptrsize) -} - -func dwarfaddelfheaders() { - if Debug['w'] != 0 { // disable dwarf - return - } - - sh := newElfShdr(elfstrdbg[ElfStrDebugAbbrev]) - sh.type_ = SHT_PROGBITS - sh.off = uint64(abbrevo) - sh.size = uint64(abbrevsize) - sh.addralign = 1 - if abbrevsympos > 0 { - putelfsymshndx(abbrevsympos, sh.shnum) - } - - sh = newElfShdr(elfstrdbg[ElfStrDebugLine]) - sh.type_ = SHT_PROGBITS - sh.off = uint64(lineo) - sh.size = uint64(linesize) - sh.addralign = 1 - if linesympos > 0 { - putelfsymshndx(linesympos, sh.shnum) - } - shline := sh - - sh = newElfShdr(elfstrdbg[ElfStrDebugFrame]) - sh.type_ = SHT_PROGBITS - sh.off = uint64(frameo) - sh.size = uint64(framesize) - sh.addralign = 1 - if framesympos > 0 { - putelfsymshndx(framesympos, sh.shnum) - } - shframe := sh - - sh = newElfShdr(elfstrdbg[ElfStrDebugInfo]) - sh.type_ = SHT_PROGBITS - sh.off = uint64(infoo) - sh.size = uint64(infosize) - sh.addralign = 1 - if infosympos > 0 { - putelfsymshndx(infosympos, sh.shnum) - } - shinfo := sh - - if pubnamessize > 0 { - sh := newElfShdr(elfstrdbg[ElfStrDebugPubNames]) - sh.type_ = SHT_PROGBITS - sh.off = uint64(pubnameso) - sh.size = uint64(pubnamessize) - sh.addralign = 1 - } - - if pubtypessize > 0 { - sh := newElfShdr(elfstrdbg[ElfStrDebugPubTypes]) - sh.type_ = SHT_PROGBITS - sh.off = uint64(pubtypeso) - sh.size = uint64(pubtypessize) - sh.addralign = 1 - } - - var sharanges *ElfShdr - if arangessize != 0 { - sh := newElfShdr(elfstrdbg[ElfStrDebugAranges]) - sh.type_ = SHT_PROGBITS - sh.off = uint64(arangeso) - sh.size = uint64(arangessize) - sh.addralign = 1 - sharanges = sh - } - - if gdbscriptsize != 0 { - sh := newElfShdr(elfstrdbg[ElfStrGDBScripts]) - sh.type_ = SHT_PROGBITS - sh.off = uint64(gdbscripto) - sh.size = uint64(gdbscriptsize) - sh.addralign = 1 - } - - if inforelocsize != 0 { - dwarfaddelfrelocheader(ElfStrRelDebugInfo, shinfo, inforeloco, inforelocsize) - } - - if arangesrelocsize != 0 { - dwarfaddelfrelocheader(ElfStrRelDebugAranges, sharanges, arangesreloco, arangesrelocsize) - } - - if linerelocsize != 0 { - dwarfaddelfrelocheader(ElfStrRelDebugLine, shline, linereloco, linerelocsize) - } - - if framerelocsize != 0 { - dwarfaddelfrelocheader(ElfStrRelDebugFrame, shframe, framereloco, framerelocsize) - } -} - -/* - * Macho - */ -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 := Rnd(int64(Segdwarf.Fileoff), 0x1000) - addr := Segdata.Vaddr + Segdata.Length - - nsect := 4 - if pubnamessize > 0 { - nsect++ - } - if pubtypessize > 0 { - nsect++ - } - if arangessize > 0 { - nsect++ - } - if gdbscriptsize > 0 { - nsect++ - } - - 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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) - if gdbscriptsize > 0 { - msect := newMachoSect(ms, "__debug_gdb_scripts", "__DWARF") - msect.off = uint32(gdbscripto) - msect.size = uint64(gdbscriptsize) - msect.addr = addr - addr += msect.size - msect.flag = 0x02000000 - } -} - -/* - * Windows PE - */ -func dwarfaddpeheaders() { - if Debug['w'] != 0 { // disable dwarf - return - } - - newPEDWARFSection(".debug_abbrev", abbrevsize) - newPEDWARFSection(".debug_line", linesize) - newPEDWARFSection(".debug_frame", framesize) - newPEDWARFSection(".debug_info", infosize) - newPEDWARFSection(".debug_pubnames", pubnamessize) - newPEDWARFSection(".debug_pubtypes", pubtypessize) - newPEDWARFSection(".debug_aranges", arangessize) - newPEDWARFSection(".debug_gdb_scripts", gdbscriptsize) -} diff --git a/src/cmd/internal/ld/dwarf_defs.go b/src/cmd/internal/ld/dwarf_defs.go deleted file mode 100644 index 61389d9218..0000000000 --- a/src/cmd/internal/ld/dwarf_defs.go +++ /dev/null @@ -1,516 +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. - -package ld - -// TODO/NICETOHAVE: -// - eliminate DW_CLS_ if not used -// - package info in compilation units -// - assign global variables and types to their packages -// - gdb uses c syntax, meaning clumsy quoting is needed for go identifiers. eg -// ptype struct '[]uint8' and qualifiers need to be quoted away -// - lexical scoping is lost, so gdb gets confused as to which 'obj.i' you mean. -// - file:line info for variables -// - make strings a typedef so prettyprinters can see the underlying string type -// -// 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. - -/* - * Emit debug_abbrevs, debug_info and debug_line sections to current - * offset in cout. - */ - -/* - * Add the dwarf section names to the ELF - * s[ection]h[eader]str[ing]tab. Prerequisite for - * dwarfaddelfheaders(). - */ - -/* - * Add section headers pointing to the sections emitted in - * dwarfemitdebugsections. - */ -// 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. - -// Cut, pasted, tr-and-awk'ed from tables in -// http://dwarfstd.org/doc/Dwarf3.pdf - -// Table 18 -const ( - DW_TAG_array_type = 0x01 - DW_TAG_class_type = 0x02 - DW_TAG_entry_point = 0x03 - DW_TAG_enumeration_type = 0x04 - DW_TAG_formal_parameter = 0x05 - DW_TAG_imported_declaration = 0x08 - DW_TAG_label = 0x0a - DW_TAG_lexical_block = 0x0b - DW_TAG_member = 0x0d - DW_TAG_pointer_type = 0x0f - DW_TAG_reference_type = 0x10 - DW_TAG_compile_unit = 0x11 - DW_TAG_string_type = 0x12 - DW_TAG_structure_type = 0x13 - DW_TAG_subroutine_type = 0x15 - DW_TAG_typedef = 0x16 - DW_TAG_union_type = 0x17 - DW_TAG_unspecified_parameters = 0x18 - DW_TAG_variant = 0x19 - DW_TAG_common_block = 0x1a - DW_TAG_common_inclusion = 0x1b - DW_TAG_inheritance = 0x1c - DW_TAG_inlined_subroutine = 0x1d - DW_TAG_module = 0x1e - DW_TAG_ptr_to_member_type = 0x1f - DW_TAG_set_type = 0x20 - DW_TAG_subrange_type = 0x21 - DW_TAG_with_stmt = 0x22 - DW_TAG_access_declaration = 0x23 - DW_TAG_base_type = 0x24 - DW_TAG_catch_block = 0x25 - DW_TAG_const_type = 0x26 - DW_TAG_constant = 0x27 - DW_TAG_enumerator = 0x28 - DW_TAG_file_type = 0x29 - DW_TAG_friend = 0x2a - DW_TAG_namelist = 0x2b - DW_TAG_namelist_item = 0x2c - DW_TAG_packed_type = 0x2d - DW_TAG_subprogram = 0x2e - DW_TAG_template_type_parameter = 0x2f - DW_TAG_template_value_parameter = 0x30 - DW_TAG_thrown_type = 0x31 - DW_TAG_try_block = 0x32 - DW_TAG_variant_part = 0x33 - DW_TAG_variable = 0x34 - DW_TAG_volatile_type = 0x35 - // Dwarf3 - DW_TAG_dwarf_procedure = 0x36 - DW_TAG_restrict_type = 0x37 - DW_TAG_interface_type = 0x38 - DW_TAG_namespace = 0x39 - DW_TAG_imported_module = 0x3a - DW_TAG_unspecified_type = 0x3b - DW_TAG_partial_unit = 0x3c - DW_TAG_imported_unit = 0x3d - DW_TAG_condition = 0x3f - DW_TAG_shared_type = 0x40 - // Dwarf4 - DW_TAG_type_unit = 0x41 - DW_TAG_rvalue_reference_type = 0x42 - DW_TAG_template_alias = 0x43 - - // User defined - DW_TAG_lo_user = 0x4080 - DW_TAG_hi_user = 0xffff -) - -// Table 19 -const ( - DW_CHILDREN_no = 0x00 - DW_CHILDREN_yes = 0x01 -) - -// Not from the spec, but logicaly belongs here -const ( - DW_CLS_ADDRESS = 0x01 + iota - DW_CLS_BLOCK - DW_CLS_CONSTANT - DW_CLS_FLAG - DW_CLS_PTR // lineptr, loclistptr, macptr, rangelistptr - DW_CLS_REFERENCE - DW_CLS_ADDRLOC - DW_CLS_STRING -) - -// Table 20 -const ( - DW_AT_sibling = 0x01 // reference - DW_AT_location = 0x02 // block, loclistptr - DW_AT_name = 0x03 // string - DW_AT_ordering = 0x09 // constant - DW_AT_byte_size = 0x0b // block, constant, reference - DW_AT_bit_offset = 0x0c // block, constant, reference - DW_AT_bit_size = 0x0d // block, constant, reference - DW_AT_stmt_list = 0x10 // lineptr - DW_AT_low_pc = 0x11 // address - DW_AT_high_pc = 0x12 // address - DW_AT_language = 0x13 // constant - DW_AT_discr = 0x15 // reference - DW_AT_discr_value = 0x16 // constant - DW_AT_visibility = 0x17 // constant - DW_AT_import = 0x18 // reference - DW_AT_string_length = 0x19 // block, loclistptr - DW_AT_common_reference = 0x1a // reference - DW_AT_comp_dir = 0x1b // string - DW_AT_const_value = 0x1c // block, constant, string - DW_AT_containing_type = 0x1d // reference - DW_AT_default_value = 0x1e // reference - DW_AT_inline = 0x20 // constant - DW_AT_is_optional = 0x21 // flag - DW_AT_lower_bound = 0x22 // block, constant, reference - DW_AT_producer = 0x25 // string - DW_AT_prototyped = 0x27 // flag - DW_AT_return_addr = 0x2a // block, loclistptr - DW_AT_start_scope = 0x2c // constant - DW_AT_bit_stride = 0x2e // constant - DW_AT_upper_bound = 0x2f // block, constant, reference - DW_AT_abstract_origin = 0x31 // reference - DW_AT_accessibility = 0x32 // constant - DW_AT_address_class = 0x33 // constant - DW_AT_artificial = 0x34 // flag - DW_AT_base_types = 0x35 // reference - DW_AT_calling_convention = 0x36 // constant - DW_AT_count = 0x37 // block, constant, reference - DW_AT_data_member_location = 0x38 // block, constant, loclistptr - DW_AT_decl_column = 0x39 // constant - DW_AT_decl_file = 0x3a // constant - DW_AT_decl_line = 0x3b // constant - DW_AT_declaration = 0x3c // flag - DW_AT_discr_list = 0x3d // block - DW_AT_encoding = 0x3e // constant - DW_AT_external = 0x3f // flag - DW_AT_frame_base = 0x40 // block, loclistptr - DW_AT_friend = 0x41 // reference - DW_AT_identifier_case = 0x42 // constant - DW_AT_macro_info = 0x43 // macptr - DW_AT_namelist_item = 0x44 // block - DW_AT_priority = 0x45 // reference - DW_AT_segment = 0x46 // block, loclistptr - DW_AT_specification = 0x47 // reference - DW_AT_static_link = 0x48 // block, loclistptr - DW_AT_type = 0x49 // reference - DW_AT_use_location = 0x4a // block, loclistptr - DW_AT_variable_parameter = 0x4b // flag - DW_AT_virtuality = 0x4c // constant - DW_AT_vtable_elem_location = 0x4d // block, loclistptr - // Dwarf3 - DW_AT_allocated = 0x4e // block, constant, reference - DW_AT_associated = 0x4f // block, constant, reference - DW_AT_data_location = 0x50 // block - DW_AT_byte_stride = 0x51 // block, constant, reference - DW_AT_entry_pc = 0x52 // address - DW_AT_use_UTF8 = 0x53 // flag - DW_AT_extension = 0x54 // reference - DW_AT_ranges = 0x55 // rangelistptr - DW_AT_trampoline = 0x56 // address, flag, reference, string - DW_AT_call_column = 0x57 // constant - DW_AT_call_file = 0x58 // constant - DW_AT_call_line = 0x59 // constant - DW_AT_description = 0x5a // string - DW_AT_binary_scale = 0x5b // constant - DW_AT_decimal_scale = 0x5c // constant - DW_AT_small = 0x5d // reference - DW_AT_decimal_sign = 0x5e // constant - DW_AT_digit_count = 0x5f // constant - DW_AT_picture_string = 0x60 // string - DW_AT_mutable = 0x61 // flag - DW_AT_threads_scaled = 0x62 // flag - DW_AT_explicit = 0x63 // flag - DW_AT_object_pointer = 0x64 // reference - DW_AT_endianity = 0x65 // constant - DW_AT_elemental = 0x66 // flag - DW_AT_pure = 0x67 // flag - DW_AT_recursive = 0x68 // flag - - DW_AT_lo_user = 0x2000 // --- - DW_AT_hi_user = 0x3fff // --- -) - -// Table 21 -const ( - DW_FORM_addr = 0x01 // address - DW_FORM_block2 = 0x03 // block - DW_FORM_block4 = 0x04 // block - DW_FORM_data2 = 0x05 // constant - DW_FORM_data4 = 0x06 // constant, lineptr, loclistptr, macptr, rangelistptr - DW_FORM_data8 = 0x07 // constant, lineptr, loclistptr, macptr, rangelistptr - DW_FORM_string = 0x08 // string - DW_FORM_block = 0x09 // block - DW_FORM_block1 = 0x0a // block - DW_FORM_data1 = 0x0b // constant - DW_FORM_flag = 0x0c // flag - DW_FORM_sdata = 0x0d // constant - DW_FORM_strp = 0x0e // string - DW_FORM_udata = 0x0f // constant - DW_FORM_ref_addr = 0x10 // reference - DW_FORM_ref1 = 0x11 // reference - DW_FORM_ref2 = 0x12 // reference - DW_FORM_ref4 = 0x13 // reference - DW_FORM_ref8 = 0x14 // reference - DW_FORM_ref_udata = 0x15 // reference - DW_FORM_indirect = 0x16 // (see Section 7.5.3) -) - -// Table 24 (#operands, notes) -const ( - DW_OP_addr = 0x03 // 1 constant address (size target specific) - DW_OP_deref = 0x06 // 0 - DW_OP_const1u = 0x08 // 1 1-byte constant - DW_OP_const1s = 0x09 // 1 1-byte constant - DW_OP_const2u = 0x0a // 1 2-byte constant - DW_OP_const2s = 0x0b // 1 2-byte constant - DW_OP_const4u = 0x0c // 1 4-byte constant - DW_OP_const4s = 0x0d // 1 4-byte constant - DW_OP_const8u = 0x0e // 1 8-byte constant - DW_OP_const8s = 0x0f // 1 8-byte constant - DW_OP_constu = 0x10 // 1 ULEB128 constant - DW_OP_consts = 0x11 // 1 SLEB128 constant - DW_OP_dup = 0x12 // 0 - DW_OP_drop = 0x13 // 0 - DW_OP_over = 0x14 // 0 - DW_OP_pick = 0x15 // 1 1-byte stack index - DW_OP_swap = 0x16 // 0 - DW_OP_rot = 0x17 // 0 - DW_OP_xderef = 0x18 // 0 - DW_OP_abs = 0x19 // 0 - DW_OP_and = 0x1a // 0 - DW_OP_div = 0x1b // 0 - DW_OP_minus = 0x1c // 0 - DW_OP_mod = 0x1d // 0 - DW_OP_mul = 0x1e // 0 - DW_OP_neg = 0x1f // 0 - DW_OP_not = 0x20 // 0 - DW_OP_or = 0x21 // 0 - DW_OP_plus = 0x22 // 0 - DW_OP_plus_uconst = 0x23 // 1 ULEB128 addend - DW_OP_shl = 0x24 // 0 - DW_OP_shr = 0x25 // 0 - DW_OP_shra = 0x26 // 0 - DW_OP_xor = 0x27 // 0 - DW_OP_skip = 0x2f // 1 signed 2-byte constant - DW_OP_bra = 0x28 // 1 signed 2-byte constant - DW_OP_eq = 0x29 // 0 - DW_OP_ge = 0x2a // 0 - DW_OP_gt = 0x2b // 0 - DW_OP_le = 0x2c // 0 - DW_OP_lt = 0x2d // 0 - DW_OP_ne = 0x2e // 0 - DW_OP_lit0 = 0x30 // 0 ... - DW_OP_lit31 = 0x4f // 0 literals 0..31 = (DW_OP_lit0 + literal) - DW_OP_reg0 = 0x50 // 0 .. - DW_OP_reg31 = 0x6f // 0 reg 0..31 = (DW_OP_reg0 + regnum) - DW_OP_breg0 = 0x70 // 1 ... - DW_OP_breg31 = 0x8f // 1 SLEB128 offset base register 0..31 = (DW_OP_breg0 + regnum) - DW_OP_regx = 0x90 // 1 ULEB128 register - DW_OP_fbreg = 0x91 // 1 SLEB128 offset - DW_OP_bregx = 0x92 // 2 ULEB128 register followed by SLEB128 offset - DW_OP_piece = 0x93 // 1 ULEB128 size of piece addressed - DW_OP_deref_size = 0x94 // 1 1-byte size of data retrieved - DW_OP_xderef_size = 0x95 // 1 1-byte size of data retrieved - DW_OP_nop = 0x96 // 0 - DW_OP_push_object_address = 0x97 // 0 - DW_OP_call2 = 0x98 // 1 2-byte offset of DIE - DW_OP_call4 = 0x99 // 1 4-byte offset of DIE - DW_OP_call_ref = 0x9a // 1 4- or 8-byte offset of DIE - DW_OP_form_tls_address = 0x9b // 0 - DW_OP_call_frame_cfa = 0x9c // 0 - DW_OP_bit_piece = 0x9d // 2 - DW_OP_lo_user = 0xe0 - DW_OP_hi_user = 0xff -) - -// Table 25 -const ( - DW_ATE_address = 0x01 - DW_ATE_boolean = 0x02 - DW_ATE_complex_float = 0x03 - DW_ATE_float = 0x04 - DW_ATE_signed = 0x05 - DW_ATE_signed_char = 0x06 - DW_ATE_unsigned = 0x07 - DW_ATE_unsigned_char = 0x08 - DW_ATE_imaginary_float = 0x09 - DW_ATE_packed_decimal = 0x0a - DW_ATE_numeric_string = 0x0b - DW_ATE_edited = 0x0c - DW_ATE_signed_fixed = 0x0d - DW_ATE_unsigned_fixed = 0x0e - DW_ATE_decimal_float = 0x0f - DW_ATE_lo_user = 0x80 - DW_ATE_hi_user = 0xff -) - -// Table 26 -const ( - DW_DS_unsigned = 0x01 - DW_DS_leading_overpunch = 0x02 - DW_DS_trailing_overpunch = 0x03 - DW_DS_leading_separate = 0x04 - DW_DS_trailing_separate = 0x05 -) - -// Table 27 -const ( - DW_END_default = 0x00 - DW_END_big = 0x01 - DW_END_little = 0x02 - DW_END_lo_user = 0x40 - DW_END_hi_user = 0xff -) - -// Table 28 -const ( - DW_ACCESS_public = 0x01 - DW_ACCESS_protected = 0x02 - DW_ACCESS_private = 0x03 -) - -// Table 29 -const ( - DW_VIS_local = 0x01 - DW_VIS_exported = 0x02 - DW_VIS_qualified = 0x03 -) - -// Table 30 -const ( - DW_VIRTUALITY_none = 0x00 - DW_VIRTUALITY_virtual = 0x01 - DW_VIRTUALITY_pure_virtual = 0x02 -) - -// Table 31 -const ( - DW_LANG_C89 = 0x0001 - DW_LANG_C = 0x0002 - DW_LANG_Ada83 = 0x0003 - DW_LANG_C_plus_plus = 0x0004 - DW_LANG_Cobol74 = 0x0005 - DW_LANG_Cobol85 = 0x0006 - DW_LANG_Fortran77 = 0x0007 - DW_LANG_Fortran90 = 0x0008 - DW_LANG_Pascal83 = 0x0009 - DW_LANG_Modula2 = 0x000a - // Dwarf3 - DW_LANG_Java = 0x000b - DW_LANG_C99 = 0x000c - DW_LANG_Ada95 = 0x000d - DW_LANG_Fortran95 = 0x000e - DW_LANG_PLI = 0x000f - DW_LANG_ObjC = 0x0010 - DW_LANG_ObjC_plus_plus = 0x0011 - DW_LANG_UPC = 0x0012 - DW_LANG_D = 0x0013 - // Dwarf4 - DW_LANG_Python = 0x0014 - // Dwarf5 - DW_LANG_Go = 0x0016 - - DW_LANG_lo_user = 0x8000 - DW_LANG_hi_user = 0xffff -) - -// Table 32 -const ( - DW_ID_case_sensitive = 0x00 - DW_ID_up_case = 0x01 - DW_ID_down_case = 0x02 - DW_ID_case_insensitive = 0x03 -) - -// Table 33 -const ( - DW_CC_normal = 0x01 - DW_CC_program = 0x02 - DW_CC_nocall = 0x03 - DW_CC_lo_user = 0x40 - DW_CC_hi_user = 0xff -) - -// Table 34 -const ( - DW_INL_not_inlined = 0x00 - DW_INL_inlined = 0x01 - DW_INL_declared_not_inlined = 0x02 - DW_INL_declared_inlined = 0x03 -) - -// Table 35 -const ( - DW_ORD_row_major = 0x00 - DW_ORD_col_major = 0x01 -) - -// Table 36 -const ( - DW_DSC_label = 0x00 - DW_DSC_range = 0x01 -) - -// Table 37 -const ( - DW_LNS_copy = 0x01 - DW_LNS_advance_pc = 0x02 - DW_LNS_advance_line = 0x03 - DW_LNS_set_file = 0x04 - DW_LNS_set_column = 0x05 - DW_LNS_negate_stmt = 0x06 - DW_LNS_set_basic_block = 0x07 - DW_LNS_const_add_pc = 0x08 - DW_LNS_fixed_advance_pc = 0x09 - // Dwarf3 - DW_LNS_set_prologue_end = 0x0a - DW_LNS_set_epilogue_begin = 0x0b - DW_LNS_set_isa = 0x0c -) - -// Table 38 -const ( - DW_LNE_end_sequence = 0x01 - DW_LNE_set_address = 0x02 - DW_LNE_define_file = 0x03 - DW_LNE_lo_user = 0x80 - DW_LNE_hi_user = 0xff -) - -// Table 39 -const ( - DW_MACINFO_define = 0x01 - DW_MACINFO_undef = 0x02 - DW_MACINFO_start_file = 0x03 - DW_MACINFO_end_file = 0x04 - DW_MACINFO_vendor_ext = 0xff -) - -// Table 40. -const ( - // operand,... - DW_CFA_nop = 0x00 - DW_CFA_set_loc = 0x01 // address - DW_CFA_advance_loc1 = 0x02 // 1-byte delta - DW_CFA_advance_loc2 = 0x03 // 2-byte delta - DW_CFA_advance_loc4 = 0x04 // 4-byte delta - DW_CFA_offset_extended = 0x05 // ULEB128 register, ULEB128 offset - DW_CFA_restore_extended = 0x06 // ULEB128 register - DW_CFA_undefined = 0x07 // ULEB128 register - DW_CFA_same_value = 0x08 // ULEB128 register - DW_CFA_register = 0x09 // ULEB128 register, ULEB128 register - DW_CFA_remember_state = 0x0a - DW_CFA_restore_state = 0x0b - - DW_CFA_def_cfa = 0x0c // ULEB128 register, ULEB128 offset - DW_CFA_def_cfa_register = 0x0d // ULEB128 register - DW_CFA_def_cfa_offset = 0x0e // ULEB128 offset - DW_CFA_def_cfa_expression = 0x0f // BLOCK - DW_CFA_expression = 0x10 // ULEB128 register, BLOCK - DW_CFA_offset_extended_sf = 0x11 // ULEB128 register, SLEB128 offset - DW_CFA_def_cfa_sf = 0x12 // ULEB128 register, SLEB128 offset - DW_CFA_def_cfa_offset_sf = 0x13 // SLEB128 offset - DW_CFA_val_offset = 0x14 // ULEB128, ULEB128 - DW_CFA_val_offset_sf = 0x15 // ULEB128, SLEB128 - DW_CFA_val_expression = 0x16 // ULEB128, BLOCK - - DW_CFA_lo_user = 0x1c - DW_CFA_hi_user = 0x3f - - // Opcodes that take an addend operand. - DW_CFA_advance_loc = 0x1 << 6 // +delta - DW_CFA_offset = 0x2 << 6 // +register (ULEB128 offset) - DW_CFA_restore = 0x3 << 6 // +register -) diff --git a/src/cmd/internal/ld/elf.go b/src/cmd/internal/ld/elf.go deleted file mode 100644 index b73a75b59b..0000000000 --- a/src/cmd/internal/ld/elf.go +++ /dev/null @@ -1,2493 +0,0 @@ -// Copyright 2009 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 ( - "cmd/internal/obj" - "encoding/binary" - "fmt" -) - -/* - * Derived from: - * $FreeBSD: src/sys/sys/elf32.h,v 1.8.14.1 2005/12/30 22:13:58 marcel Exp $ - * $FreeBSD: src/sys/sys/elf64.h,v 1.10.14.1 2005/12/30 22:13:58 marcel Exp $ - * $FreeBSD: src/sys/sys/elf_common.h,v 1.15.8.1 2005/12/30 22:13:58 marcel Exp $ - * $FreeBSD: src/sys/alpha/include/elf.h,v 1.14 2003/09/25 01:10:22 peter Exp $ - * $FreeBSD: src/sys/amd64/include/elf.h,v 1.18 2004/08/03 08:21:48 dfr Exp $ - * $FreeBSD: src/sys/arm/include/elf.h,v 1.5.2.1 2006/06/30 21:42:52 cognet Exp $ - * $FreeBSD: src/sys/i386/include/elf.h,v 1.16 2004/08/02 19:12:17 dfr Exp $ - * $FreeBSD: src/sys/powerpc/include/elf.h,v 1.7 2004/11/02 09:47:01 ssouhlal Exp $ - * $FreeBSD: src/sys/sparc64/include/elf.h,v 1.12 2003/09/25 01:10:26 peter Exp $ - * - * Copyright (c) 1996-1998 John D. Polstra. All rights reserved. - * Copyright (c) 2001 David E. O'Brien - * Portions Copyright 2009 The Go 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: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. - * - */ - -/* - * ELF definitions that are independent of architecture or word size. - */ - -/* - * Note header. The ".note" section contains an array of notes. Each - * begins with this header, aligned to a word boundary. Immediately - * following the note header is n_namesz bytes of name, padded to the - * next word boundary. Then comes n_descsz bytes of descriptor, again - * padded to a word boundary. The values of n_namesz and n_descsz do - * not include the padding. - */ -type Elf_Note struct { - n_namesz uint32 - n_descsz uint32 - n_type uint32 -} - -const ( - EI_MAG0 = 0 - EI_MAG1 = 1 - EI_MAG2 = 2 - EI_MAG3 = 3 - EI_CLASS = 4 - EI_DATA = 5 - EI_VERSION = 6 - EI_OSABI = 7 - EI_ABIVERSION = 8 - OLD_EI_BRAND = 8 - EI_PAD = 9 - EI_NIDENT = 16 - ELFMAG0 = 0x7f - ELFMAG1 = 'E' - ELFMAG2 = 'L' - ELFMAG3 = 'F' - SELFMAG = 4 - EV_NONE = 0 - EV_CURRENT = 1 - ELFCLASSNONE = 0 - ELFCLASS32 = 1 - ELFCLASS64 = 2 - ELFDATANONE = 0 - ELFDATA2LSB = 1 - ELFDATA2MSB = 2 - ELFOSABI_NONE = 0 - ELFOSABI_HPUX = 1 - ELFOSABI_NETBSD = 2 - ELFOSABI_LINUX = 3 - ELFOSABI_HURD = 4 - ELFOSABI_86OPEN = 5 - ELFOSABI_SOLARIS = 6 - ELFOSABI_AIX = 7 - ELFOSABI_IRIX = 8 - ELFOSABI_FREEBSD = 9 - ELFOSABI_TRU64 = 10 - ELFOSABI_MODESTO = 11 - ELFOSABI_OPENBSD = 12 - ELFOSABI_OPENVMS = 13 - ELFOSABI_NSK = 14 - ELFOSABI_ARM = 97 - ELFOSABI_STANDALONE = 255 - ELFOSABI_SYSV = ELFOSABI_NONE - ELFOSABI_MONTEREY = ELFOSABI_AIX - ET_NONE = 0 - ET_REL = 1 - ET_EXEC = 2 - ET_DYN = 3 - ET_CORE = 4 - ET_LOOS = 0xfe00 - ET_HIOS = 0xfeff - ET_LOPROC = 0xff00 - ET_HIPROC = 0xffff - EM_NONE = 0 - EM_M32 = 1 - EM_SPARC = 2 - EM_386 = 3 - EM_68K = 4 - EM_88K = 5 - EM_860 = 7 - EM_MIPS = 8 - EM_S370 = 9 - EM_MIPS_RS3_LE = 10 - EM_PARISC = 15 - EM_VPP500 = 17 - EM_SPARC32PLUS = 18 - EM_960 = 19 - EM_PPC = 20 - EM_PPC64 = 21 - EM_S390 = 22 - EM_V800 = 36 - EM_FR20 = 37 - EM_RH32 = 38 - EM_RCE = 39 - EM_ARM = 40 - EM_SH = 42 - EM_SPARCV9 = 43 - EM_TRICORE = 44 - EM_ARC = 45 - EM_H8_300 = 46 - EM_H8_300H = 47 - EM_H8S = 48 - EM_H8_500 = 49 - EM_IA_64 = 50 - EM_MIPS_X = 51 - EM_COLDFIRE = 52 - EM_68HC12 = 53 - EM_MMA = 54 - EM_PCP = 55 - EM_NCPU = 56 - EM_NDR1 = 57 - EM_STARCORE = 58 - EM_ME16 = 59 - EM_ST100 = 60 - EM_TINYJ = 61 - EM_X86_64 = 62 - EM_AARCH64 = 183 - EM_486 = 6 - EM_MIPS_RS4_BE = 10 - EM_ALPHA_STD = 41 - EM_ALPHA = 0x9026 - SHN_UNDEF = 0 - SHN_LORESERVE = 0xff00 - SHN_LOPROC = 0xff00 - SHN_HIPROC = 0xff1f - SHN_LOOS = 0xff20 - SHN_HIOS = 0xff3f - SHN_ABS = 0xfff1 - SHN_COMMON = 0xfff2 - SHN_XINDEX = 0xffff - SHN_HIRESERVE = 0xffff - SHT_NULL = 0 - SHT_PROGBITS = 1 - SHT_SYMTAB = 2 - SHT_STRTAB = 3 - SHT_RELA = 4 - SHT_HASH = 5 - SHT_DYNAMIC = 6 - SHT_NOTE = 7 - SHT_NOBITS = 8 - SHT_REL = 9 - SHT_SHLIB = 10 - SHT_DYNSYM = 11 - SHT_INIT_ARRAY = 14 - SHT_FINI_ARRAY = 15 - SHT_PREINIT_ARRAY = 16 - SHT_GROUP = 17 - SHT_SYMTAB_SHNDX = 18 - SHT_LOOS = 0x60000000 - SHT_HIOS = 0x6fffffff - SHT_GNU_VERDEF = 0x6ffffffd - SHT_GNU_VERNEED = 0x6ffffffe - SHT_GNU_VERSYM = 0x6fffffff - SHT_LOPROC = 0x70000000 - SHT_HIPROC = 0x7fffffff - SHT_LOUSER = 0x80000000 - SHT_HIUSER = 0xffffffff - SHF_WRITE = 0x1 - SHF_ALLOC = 0x2 - SHF_EXECINSTR = 0x4 - SHF_MERGE = 0x10 - SHF_STRINGS = 0x20 - SHF_INFO_LINK = 0x40 - SHF_LINK_ORDER = 0x80 - SHF_OS_NONCONFORMING = 0x100 - SHF_GROUP = 0x200 - SHF_TLS = 0x400 - SHF_MASKOS = 0x0ff00000 - SHF_MASKPROC = 0xf0000000 - PT_NULL = 0 - PT_LOAD = 1 - PT_DYNAMIC = 2 - PT_INTERP = 3 - PT_NOTE = 4 - PT_SHLIB = 5 - PT_PHDR = 6 - PT_TLS = 7 - PT_LOOS = 0x60000000 - PT_HIOS = 0x6fffffff - PT_LOPROC = 0x70000000 - PT_HIPROC = 0x7fffffff - PT_GNU_STACK = 0x6474e551 - PT_PAX_FLAGS = 0x65041580 - PF_X = 0x1 - PF_W = 0x2 - PF_R = 0x4 - PF_MASKOS = 0x0ff00000 - PF_MASKPROC = 0xf0000000 - DT_NULL = 0 - DT_NEEDED = 1 - DT_PLTRELSZ = 2 - DT_PLTGOT = 3 - DT_HASH = 4 - DT_STRTAB = 5 - DT_SYMTAB = 6 - DT_RELA = 7 - DT_RELASZ = 8 - DT_RELAENT = 9 - DT_STRSZ = 10 - DT_SYMENT = 11 - DT_INIT = 12 - DT_FINI = 13 - DT_SONAME = 14 - DT_RPATH = 15 - DT_SYMBOLIC = 16 - DT_REL = 17 - DT_RELSZ = 18 - DT_RELENT = 19 - DT_PLTREL = 20 - DT_DEBUG = 21 - DT_TEXTREL = 22 - DT_JMPREL = 23 - DT_BIND_NOW = 24 - DT_INIT_ARRAY = 25 - DT_FINI_ARRAY = 26 - DT_INIT_ARRAYSZ = 27 - DT_FINI_ARRAYSZ = 28 - DT_RUNPATH = 29 - DT_FLAGS = 30 - DT_ENCODING = 32 - DT_PREINIT_ARRAY = 32 - DT_PREINIT_ARRAYSZ = 33 - DT_LOOS = 0x6000000d - DT_HIOS = 0x6ffff000 - DT_LOPROC = 0x70000000 - DT_HIPROC = 0x7fffffff - DT_VERNEED = 0x6ffffffe - DT_VERNEEDNUM = 0x6fffffff - DT_VERSYM = 0x6ffffff0 - DT_PPC64_GLINK = DT_LOPROC + 0 - DT_PPC64_OPT = DT_LOPROC + 3 - DF_ORIGIN = 0x0001 - DF_SYMBOLIC = 0x0002 - DF_TEXTREL = 0x0004 - DF_BIND_NOW = 0x0008 - DF_STATIC_TLS = 0x0010 - NT_PRSTATUS = 1 - NT_FPREGSET = 2 - NT_PRPSINFO = 3 - STB_LOCAL = 0 - STB_GLOBAL = 1 - STB_WEAK = 2 - STB_LOOS = 10 - STB_HIOS = 12 - STB_LOPROC = 13 - STB_HIPROC = 15 - STT_NOTYPE = 0 - STT_OBJECT = 1 - STT_FUNC = 2 - STT_SECTION = 3 - STT_FILE = 4 - STT_COMMON = 5 - STT_TLS = 6 - STT_LOOS = 10 - STT_HIOS = 12 - STT_LOPROC = 13 - STT_HIPROC = 15 - STV_DEFAULT = 0x0 - STV_INTERNAL = 0x1 - STV_HIDDEN = 0x2 - STV_PROTECTED = 0x3 - STN_UNDEF = 0 -) - -/* For accessing the fields of r_info. */ - -/* For constructing r_info from field values. */ - -/* - * Relocation types. - */ -const ( - R_X86_64_NONE = 0 - R_X86_64_64 = 1 - R_X86_64_PC32 = 2 - R_X86_64_GOT32 = 3 - R_X86_64_PLT32 = 4 - R_X86_64_COPY = 5 - R_X86_64_GLOB_DAT = 6 - R_X86_64_JMP_SLOT = 7 - R_X86_64_RELATIVE = 8 - R_X86_64_GOTPCREL = 9 - R_X86_64_32 = 10 - R_X86_64_32S = 11 - R_X86_64_16 = 12 - R_X86_64_PC16 = 13 - R_X86_64_8 = 14 - R_X86_64_PC8 = 15 - R_X86_64_DTPMOD64 = 16 - R_X86_64_DTPOFF64 = 17 - R_X86_64_TPOFF64 = 18 - R_X86_64_TLSGD = 19 - R_X86_64_TLSLD = 20 - R_X86_64_DTPOFF32 = 21 - R_X86_64_GOTTPOFF = 22 - R_X86_64_TPOFF32 = 23 - R_X86_64_COUNT = 24 - R_AARCH64_ABS64 = 257 - R_AARCH64_ABS32 = 258 - R_AARCH64_CALL26 = 283 - R_AARCH64_ADR_PREL_PG_HI21 = 275 - R_AARCH64_ADD_ABS_LO12_NC = 277 - R_ALPHA_NONE = 0 - R_ALPHA_REFLONG = 1 - R_ALPHA_REFQUAD = 2 - R_ALPHA_GPREL32 = 3 - R_ALPHA_LITERAL = 4 - R_ALPHA_LITUSE = 5 - R_ALPHA_GPDISP = 6 - R_ALPHA_BRADDR = 7 - R_ALPHA_HINT = 8 - R_ALPHA_SREL16 = 9 - R_ALPHA_SREL32 = 10 - R_ALPHA_SREL64 = 11 - R_ALPHA_OP_PUSH = 12 - R_ALPHA_OP_STORE = 13 - R_ALPHA_OP_PSUB = 14 - R_ALPHA_OP_PRSHIFT = 15 - R_ALPHA_GPVALUE = 16 - R_ALPHA_GPRELHIGH = 17 - R_ALPHA_GPRELLOW = 18 - R_ALPHA_IMMED_GP_16 = 19 - R_ALPHA_IMMED_GP_HI32 = 20 - R_ALPHA_IMMED_SCN_HI32 = 21 - R_ALPHA_IMMED_BR_HI32 = 22 - R_ALPHA_IMMED_LO32 = 23 - R_ALPHA_COPY = 24 - R_ALPHA_GLOB_DAT = 25 - R_ALPHA_JMP_SLOT = 26 - R_ALPHA_RELATIVE = 27 - R_ALPHA_COUNT = 28 - R_ARM_NONE = 0 - R_ARM_PC24 = 1 - R_ARM_ABS32 = 2 - R_ARM_REL32 = 3 - R_ARM_PC13 = 4 - R_ARM_ABS16 = 5 - R_ARM_ABS12 = 6 - R_ARM_THM_ABS5 = 7 - R_ARM_ABS8 = 8 - R_ARM_SBREL32 = 9 - R_ARM_THM_PC22 = 10 - R_ARM_THM_PC8 = 11 - R_ARM_AMP_VCALL9 = 12 - R_ARM_SWI24 = 13 - R_ARM_THM_SWI8 = 14 - R_ARM_XPC25 = 15 - R_ARM_THM_XPC22 = 16 - R_ARM_COPY = 20 - R_ARM_GLOB_DAT = 21 - R_ARM_JUMP_SLOT = 22 - R_ARM_RELATIVE = 23 - R_ARM_GOTOFF = 24 - R_ARM_GOTPC = 25 - R_ARM_GOT32 = 26 - R_ARM_PLT32 = 27 - R_ARM_CALL = 28 - R_ARM_JUMP24 = 29 - R_ARM_V4BX = 40 - R_ARM_GOT_PREL = 96 - R_ARM_GNU_VTENTRY = 100 - R_ARM_GNU_VTINHERIT = 101 - R_ARM_TLS_IE32 = 107 - R_ARM_TLS_LE32 = 108 - R_ARM_RSBREL32 = 250 - R_ARM_THM_RPC22 = 251 - R_ARM_RREL32 = 252 - R_ARM_RABS32 = 253 - R_ARM_RPC24 = 254 - R_ARM_RBASE = 255 - R_ARM_COUNT = 38 - R_386_NONE = 0 - R_386_32 = 1 - R_386_PC32 = 2 - R_386_GOT32 = 3 - R_386_PLT32 = 4 - R_386_COPY = 5 - R_386_GLOB_DAT = 6 - R_386_JMP_SLOT = 7 - R_386_RELATIVE = 8 - R_386_GOTOFF = 9 - R_386_GOTPC = 10 - R_386_TLS_TPOFF = 14 - R_386_TLS_IE = 15 - R_386_TLS_GOTIE = 16 - R_386_TLS_LE = 17 - R_386_TLS_GD = 18 - R_386_TLS_LDM = 19 - R_386_TLS_GD_32 = 24 - R_386_TLS_GD_PUSH = 25 - R_386_TLS_GD_CALL = 26 - R_386_TLS_GD_POP = 27 - R_386_TLS_LDM_32 = 28 - R_386_TLS_LDM_PUSH = 29 - R_386_TLS_LDM_CALL = 30 - R_386_TLS_LDM_POP = 31 - R_386_TLS_LDO_32 = 32 - R_386_TLS_IE_32 = 33 - R_386_TLS_LE_32 = 34 - R_386_TLS_DTPMOD32 = 35 - R_386_TLS_DTPOFF32 = 36 - R_386_TLS_TPOFF32 = 37 - R_386_COUNT = 38 - R_PPC_NONE = 0 - R_PPC_ADDR32 = 1 - R_PPC_ADDR24 = 2 - R_PPC_ADDR16 = 3 - R_PPC_ADDR16_LO = 4 - R_PPC_ADDR16_HI = 5 - R_PPC_ADDR16_HA = 6 - R_PPC_ADDR14 = 7 - R_PPC_ADDR14_BRTAKEN = 8 - R_PPC_ADDR14_BRNTAKEN = 9 - R_PPC_REL24 = 10 - R_PPC_REL14 = 11 - R_PPC_REL14_BRTAKEN = 12 - R_PPC_REL14_BRNTAKEN = 13 - R_PPC_GOT16 = 14 - R_PPC_GOT16_LO = 15 - R_PPC_GOT16_HI = 16 - R_PPC_GOT16_HA = 17 - R_PPC_PLTREL24 = 18 - R_PPC_COPY = 19 - R_PPC_GLOB_DAT = 20 - R_PPC_JMP_SLOT = 21 - R_PPC_RELATIVE = 22 - R_PPC_LOCAL24PC = 23 - R_PPC_UADDR32 = 24 - R_PPC_UADDR16 = 25 - R_PPC_REL32 = 26 - R_PPC_PLT32 = 27 - R_PPC_PLTREL32 = 28 - R_PPC_PLT16_LO = 29 - R_PPC_PLT16_HI = 30 - R_PPC_PLT16_HA = 31 - R_PPC_SDAREL16 = 32 - R_PPC_SECTOFF = 33 - R_PPC_SECTOFF_LO = 34 - R_PPC_SECTOFF_HI = 35 - R_PPC_SECTOFF_HA = 36 - R_PPC_COUNT = 37 - R_PPC_TLS = 67 - R_PPC_DTPMOD32 = 68 - R_PPC_TPREL16 = 69 - R_PPC_TPREL16_LO = 70 - R_PPC_TPREL16_HI = 71 - R_PPC_TPREL16_HA = 72 - R_PPC_TPREL32 = 73 - R_PPC_DTPREL16 = 74 - R_PPC_DTPREL16_LO = 75 - R_PPC_DTPREL16_HI = 76 - R_PPC_DTPREL16_HA = 77 - R_PPC_DTPREL32 = 78 - R_PPC_GOT_TLSGD16 = 79 - R_PPC_GOT_TLSGD16_LO = 80 - R_PPC_GOT_TLSGD16_HI = 81 - R_PPC_GOT_TLSGD16_HA = 82 - R_PPC_GOT_TLSLD16 = 83 - R_PPC_GOT_TLSLD16_LO = 84 - R_PPC_GOT_TLSLD16_HI = 85 - R_PPC_GOT_TLSLD16_HA = 86 - R_PPC_GOT_TPREL16 = 87 - R_PPC_GOT_TPREL16_LO = 88 - R_PPC_GOT_TPREL16_HI = 89 - R_PPC_GOT_TPREL16_HA = 90 - R_PPC_EMB_NADDR32 = 101 - R_PPC_EMB_NADDR16 = 102 - R_PPC_EMB_NADDR16_LO = 103 - R_PPC_EMB_NADDR16_HI = 104 - R_PPC_EMB_NADDR16_HA = 105 - R_PPC_EMB_SDAI16 = 106 - R_PPC_EMB_SDA2I16 = 107 - R_PPC_EMB_SDA2REL = 108 - R_PPC_EMB_SDA21 = 109 - R_PPC_EMB_MRKREF = 110 - R_PPC_EMB_RELSEC16 = 111 - R_PPC_EMB_RELST_LO = 112 - R_PPC_EMB_RELST_HI = 113 - R_PPC_EMB_RELST_HA = 114 - R_PPC_EMB_BIT_FLD = 115 - R_PPC_EMB_RELSDA = 116 - R_PPC_EMB_COUNT = R_PPC_EMB_RELSDA - R_PPC_EMB_NADDR32 + 1 - R_PPC64_REL24 = R_PPC_REL24 - R_PPC64_JMP_SLOT = R_PPC_JMP_SLOT - R_PPC64_ADDR64 = 38 - R_PPC64_TOC16 = 47 - R_PPC64_TOC16_LO = 48 - R_PPC64_TOC16_HI = 49 - R_PPC64_TOC16_HA = 50 - R_PPC64_TOC16_DS = 63 - R_PPC64_TOC16_LO_DS = 64 - R_PPC64_REL16_LO = 250 - R_PPC64_REL16_HI = 251 - R_PPC64_REL16_HA = 252 - R_SPARC_NONE = 0 - R_SPARC_8 = 1 - R_SPARC_16 = 2 - R_SPARC_32 = 3 - R_SPARC_DISP8 = 4 - R_SPARC_DISP16 = 5 - R_SPARC_DISP32 = 6 - R_SPARC_WDISP30 = 7 - R_SPARC_WDISP22 = 8 - R_SPARC_HI22 = 9 - R_SPARC_22 = 10 - R_SPARC_13 = 11 - R_SPARC_LO10 = 12 - R_SPARC_GOT10 = 13 - R_SPARC_GOT13 = 14 - R_SPARC_GOT22 = 15 - R_SPARC_PC10 = 16 - R_SPARC_PC22 = 17 - R_SPARC_WPLT30 = 18 - R_SPARC_COPY = 19 - R_SPARC_GLOB_DAT = 20 - R_SPARC_JMP_SLOT = 21 - R_SPARC_RELATIVE = 22 - R_SPARC_UA32 = 23 - R_SPARC_PLT32 = 24 - R_SPARC_HIPLT22 = 25 - R_SPARC_LOPLT10 = 26 - R_SPARC_PCPLT32 = 27 - R_SPARC_PCPLT22 = 28 - R_SPARC_PCPLT10 = 29 - R_SPARC_10 = 30 - R_SPARC_11 = 31 - R_SPARC_64 = 32 - R_SPARC_OLO10 = 33 - R_SPARC_HH22 = 34 - R_SPARC_HM10 = 35 - R_SPARC_LM22 = 36 - R_SPARC_PC_HH22 = 37 - R_SPARC_PC_HM10 = 38 - R_SPARC_PC_LM22 = 39 - R_SPARC_WDISP16 = 40 - R_SPARC_WDISP19 = 41 - R_SPARC_GLOB_JMP = 42 - R_SPARC_7 = 43 - R_SPARC_5 = 44 - R_SPARC_6 = 45 - R_SPARC_DISP64 = 46 - R_SPARC_PLT64 = 47 - R_SPARC_HIX22 = 48 - R_SPARC_LOX10 = 49 - R_SPARC_H44 = 50 - R_SPARC_M44 = 51 - R_SPARC_L44 = 52 - R_SPARC_REGISTER = 53 - R_SPARC_UA64 = 54 - R_SPARC_UA16 = 55 - ARM_MAGIC_TRAMP_NUMBER = 0x5c000003 -) - -/* - * Symbol table entries. - */ - -/* For accessing the fields of st_info. */ - -/* For constructing st_info from field values. */ - -/* For accessing the fields of st_other. */ - -/* - * ELF header. - */ -type ElfEhdr struct { - ident [EI_NIDENT]uint8 - type_ uint16 - machine uint16 - version uint32 - entry uint64 - phoff uint64 - shoff uint64 - flags uint32 - ehsize uint16 - phentsize uint16 - phnum uint16 - shentsize uint16 - shnum uint16 - shstrndx uint16 -} - -/* - * Section header. - */ -type ElfShdr struct { - name uint32 - type_ uint32 - flags uint64 - addr uint64 - off uint64 - size uint64 - link uint32 - info uint32 - addralign uint64 - entsize uint64 - shnum int - secsym *LSym -} - -/* - * Program header. - */ -type ElfPhdr struct { - type_ uint32 - flags uint32 - off uint64 - vaddr uint64 - paddr uint64 - filesz uint64 - memsz uint64 - align uint64 -} - -/* For accessing the fields of r_info. */ - -/* For constructing r_info from field values. */ - -/* - * Symbol table entries. - */ - -/* For accessing the fields of st_info. */ - -/* For constructing st_info from field values. */ - -/* For accessing the fields of st_other. */ - -/* - * Go linker interface - */ -const ( - ELF64HDRSIZE = 64 - ELF64PHDRSIZE = 56 - ELF64SHDRSIZE = 64 - ELF64RELSIZE = 16 - ELF64RELASIZE = 24 - ELF64SYMSIZE = 24 - ELF32HDRSIZE = 52 - ELF32PHDRSIZE = 32 - ELF32SHDRSIZE = 40 - ELF32SYMSIZE = 16 - ELF32RELSIZE = 8 -) - -/* - * The interface uses the 64-bit structures always, - * to avoid code duplication. The writers know how to - * marshal a 32-bit representation from the 64-bit structure. - */ - -var Elfstrdat []byte - -/* - * Total amount of space to reserve at the start of the file - * for Header, PHeaders, SHeaders, and interp. - * May waste some. - * On FreeBSD, cannot be larger than a page. - */ -const ( - ELFRESERVE = 3072 -) - -/* - * We use the 64-bit data structures on both 32- and 64-bit machines - * in order to write the code just once. The 64-bit data structure is - * written in the 32-bit format on the 32-bit machines. - */ -const ( - NSECT = 48 -) - -var Iself bool - -var Nelfsym int = 1 - -var elf64 bool - -var ehdr ElfEhdr - -var phdr [NSECT]*ElfPhdr - -var shdr [NSECT]*ElfShdr - -var interp string - -type Elfstring struct { - s string - off int -} - -var elfstr [100]Elfstring - -var nelfstr int - -var buildinfo []byte - -/* - Initialize the global variable that describes the ELF header. It will be updated as - we write section and prog headers. -*/ -func Elfinit() { - Iself = true - - switch Thearch.Thechar { - // 64-bit architectures - case '9': - if Ctxt.Arch.ByteOrder == binary.BigEndian { - ehdr.flags = 1 /* Version 1 ABI */ - } else { - ehdr.flags = 2 /* Version 2 ABI */ - } - fallthrough - - case '6', '7': - elf64 = true - - ehdr.phoff = ELF64HDRSIZE /* Must be be ELF64HDRSIZE: first PHdr must follow ELF header */ - ehdr.shoff = ELF64HDRSIZE /* Will move as we add PHeaders */ - ehdr.ehsize = ELF64HDRSIZE /* Must be ELF64HDRSIZE */ - ehdr.phentsize = ELF64PHDRSIZE /* Must be ELF64PHDRSIZE */ - ehdr.shentsize = ELF64SHDRSIZE /* Must be ELF64SHDRSIZE */ - - // we use EABI on both linux/arm and freebsd/arm. - // 32-bit architectures - case '5': - // we use EABI on both linux/arm and freebsd/arm. - if HEADTYPE == obj.Hlinux || HEADTYPE == obj.Hfreebsd { - ehdr.flags = 0x5000002 // has entry point, Version5 EABI - } - fallthrough - - default: - ehdr.phoff = ELF32HDRSIZE - /* Must be be ELF32HDRSIZE: first PHdr must follow ELF header */ - ehdr.shoff = ELF32HDRSIZE /* Will move as we add PHeaders */ - ehdr.ehsize = ELF32HDRSIZE /* Must be ELF32HDRSIZE */ - ehdr.phentsize = ELF32PHDRSIZE /* Must be ELF32PHDRSIZE */ - ehdr.shentsize = ELF32SHDRSIZE /* Must be ELF32SHDRSIZE */ - } -} - -func elf64phdr(e *ElfPhdr) { - Thearch.Lput(e.type_) - Thearch.Lput(e.flags) - Thearch.Vput(e.off) - Thearch.Vput(e.vaddr) - Thearch.Vput(e.paddr) - Thearch.Vput(e.filesz) - Thearch.Vput(e.memsz) - Thearch.Vput(e.align) -} - -func elf32phdr(e *ElfPhdr) { - if e.type_ == PT_LOAD { - // Correct ELF loaders will do this implicitly, - // but buggy ELF loaders like the one in some - // versions of QEMU won't. - frag := int(e.vaddr & (e.align - 1)) - - e.off -= uint64(frag) - e.vaddr -= uint64(frag) - e.paddr -= uint64(frag) - e.filesz += uint64(frag) - e.memsz += uint64(frag) - } - - Thearch.Lput(e.type_) - Thearch.Lput(uint32(e.off)) - Thearch.Lput(uint32(e.vaddr)) - Thearch.Lput(uint32(e.paddr)) - Thearch.Lput(uint32(e.filesz)) - Thearch.Lput(uint32(e.memsz)) - Thearch.Lput(e.flags) - Thearch.Lput(uint32(e.align)) -} - -func elf64shdr(e *ElfShdr) { - Thearch.Lput(e.name) - Thearch.Lput(e.type_) - Thearch.Vput(e.flags) - Thearch.Vput(e.addr) - Thearch.Vput(e.off) - Thearch.Vput(e.size) - Thearch.Lput(e.link) - Thearch.Lput(e.info) - Thearch.Vput(e.addralign) - Thearch.Vput(e.entsize) -} - -func elf32shdr(e *ElfShdr) { - Thearch.Lput(e.name) - Thearch.Lput(e.type_) - Thearch.Lput(uint32(e.flags)) - Thearch.Lput(uint32(e.addr)) - Thearch.Lput(uint32(e.off)) - Thearch.Lput(uint32(e.size)) - Thearch.Lput(e.link) - Thearch.Lput(e.info) - Thearch.Lput(uint32(e.addralign)) - Thearch.Lput(uint32(e.entsize)) -} - -func elfwriteshdrs() uint32 { - if elf64 { - for i := 0; i < int(ehdr.shnum); i++ { - elf64shdr(shdr[i]) - } - return uint32(ehdr.shnum) * ELF64SHDRSIZE - } - - for i := 0; i < int(ehdr.shnum); i++ { - elf32shdr(shdr[i]) - } - return uint32(ehdr.shnum) * ELF32SHDRSIZE -} - -func elfsetstring(s string, off int) { - if nelfstr >= len(elfstr) { - Diag("too many elf strings") - errorexit() - } - - elfstr[nelfstr].s = s - elfstr[nelfstr].off = off - nelfstr++ -} - -func elfwritephdrs() uint32 { - if elf64 { - for i := 0; i < int(ehdr.phnum); i++ { - elf64phdr(phdr[i]) - } - return uint32(ehdr.phnum) * ELF64PHDRSIZE - } - - for i := 0; i < int(ehdr.phnum); i++ { - elf32phdr(phdr[i]) - } - return uint32(ehdr.phnum) * ELF32PHDRSIZE -} - -func newElfPhdr() *ElfPhdr { - e := new(ElfPhdr) - if ehdr.phnum >= NSECT { - Diag("too many phdrs") - } else { - phdr[ehdr.phnum] = e - ehdr.phnum++ - } - if elf64 { - ehdr.shoff += ELF64PHDRSIZE - } else { - ehdr.shoff += ELF32PHDRSIZE - } - return e -} - -func newElfShdr(name int64) *ElfShdr { - e := new(ElfShdr) - e.name = uint32(name) - e.shnum = int(ehdr.shnum) - if ehdr.shnum >= NSECT { - Diag("too many shdrs") - } else { - shdr[ehdr.shnum] = e - ehdr.shnum++ - } - - return e -} - -func getElfEhdr() *ElfEhdr { - return &ehdr -} - -func elf64writehdr() uint32 { - for i := 0; i < EI_NIDENT; i++ { - Cput(ehdr.ident[i]) - } - Thearch.Wput(ehdr.type_) - Thearch.Wput(ehdr.machine) - Thearch.Lput(ehdr.version) - Thearch.Vput(ehdr.entry) - Thearch.Vput(ehdr.phoff) - Thearch.Vput(ehdr.shoff) - Thearch.Lput(ehdr.flags) - Thearch.Wput(ehdr.ehsize) - Thearch.Wput(ehdr.phentsize) - Thearch.Wput(ehdr.phnum) - Thearch.Wput(ehdr.shentsize) - Thearch.Wput(ehdr.shnum) - Thearch.Wput(ehdr.shstrndx) - return ELF64HDRSIZE -} - -func elf32writehdr() uint32 { - for i := 0; i < EI_NIDENT; i++ { - Cput(ehdr.ident[i]) - } - Thearch.Wput(ehdr.type_) - Thearch.Wput(ehdr.machine) - Thearch.Lput(ehdr.version) - Thearch.Lput(uint32(ehdr.entry)) - Thearch.Lput(uint32(ehdr.phoff)) - Thearch.Lput(uint32(ehdr.shoff)) - Thearch.Lput(ehdr.flags) - Thearch.Wput(ehdr.ehsize) - Thearch.Wput(ehdr.phentsize) - Thearch.Wput(ehdr.phnum) - Thearch.Wput(ehdr.shentsize) - Thearch.Wput(ehdr.shnum) - Thearch.Wput(ehdr.shstrndx) - return ELF32HDRSIZE -} - -func elfwritehdr() uint32 { - if elf64 { - return elf64writehdr() - } - return elf32writehdr() -} - -/* Taken directly from the definition document for ELF64 */ -func elfhash(name []byte) uint32 { - var h uint32 = 0 - var g uint32 - for len(name) != 0 { - h = (h << 4) + uint32(name[0]) - name = name[1:] - g = h & 0xf0000000 - if g != 0 { - h ^= g >> 24 - } - h &= 0x0fffffff - } - - return h -} - -func Elfwritedynent(s *LSym, tag int, val uint64) { - if elf64 { - Adduint64(Ctxt, s, uint64(tag)) - Adduint64(Ctxt, s, val) - } else { - Adduint32(Ctxt, s, uint32(tag)) - Adduint32(Ctxt, s, uint32(val)) - } -} - -func elfwritedynentsym(s *LSym, tag int, t *LSym) { - Elfwritedynentsymplus(s, tag, t, 0) -} - -func Elfwritedynentsymplus(s *LSym, tag int, t *LSym, add int64) { - if elf64 { - Adduint64(Ctxt, s, uint64(tag)) - } else { - Adduint32(Ctxt, s, uint32(tag)) - } - Addaddrplus(Ctxt, s, t, add) -} - -func elfwritedynentsymsize(s *LSym, tag int, t *LSym) { - if elf64 { - Adduint64(Ctxt, s, uint64(tag)) - } else { - Adduint32(Ctxt, s, uint32(tag)) - } - addsize(Ctxt, s, t) -} - -func elfinterp(sh *ElfShdr, startva uint64, resoff uint64, p string) int { - interp = p - n := len(interp) + 1 - sh.addr = startva + resoff - uint64(n) - sh.off = resoff - uint64(n) - sh.size = uint64(n) - - return n -} - -func elfwriteinterp() int { - sh := elfshname(".interp") - Cseek(int64(sh.off)) - coutbuf.WriteString(interp) - Cput(0) - return int(sh.size) -} - -func elfnote(sh *ElfShdr, startva uint64, resoff uint64, sz int, alloc bool) int { - n := 3*4 + uint64(sz) + resoff%4 - - sh.type_ = SHT_NOTE - if alloc { - sh.flags = SHF_ALLOC - } - sh.addralign = 4 - sh.addr = startva + resoff - n - sh.off = resoff - n - sh.size = n - resoff%4 - - return int(n) -} - -func elfwritenotehdr(str string, namesz uint32, descsz uint32, tag uint32) *ElfShdr { - sh := elfshname(str) - - // Write Elf_Note header. - Cseek(int64(sh.off)) - - Thearch.Lput(namesz) - Thearch.Lput(descsz) - Thearch.Lput(tag) - - return sh -} - -// NetBSD Signature (as per sys/exec_elf.h) -const ( - ELF_NOTE_NETBSD_NAMESZ = 7 - ELF_NOTE_NETBSD_DESCSZ = 4 - ELF_NOTE_NETBSD_TAG = 1 - ELF_NOTE_NETBSD_VERSION = 599000000 /* NetBSD 5.99 */ -) - -var ELF_NOTE_NETBSD_NAME = []byte("NetBSD\x00") - -func elfnetbsdsig(sh *ElfShdr, startva uint64, resoff uint64) int { - n := int(Rnd(ELF_NOTE_NETBSD_NAMESZ, 4) + Rnd(ELF_NOTE_NETBSD_DESCSZ, 4)) - return elfnote(sh, startva, resoff, n, true) -} - -func elfwritenetbsdsig() int { - // Write Elf_Note header. - sh := elfwritenotehdr(".note.netbsd.ident", ELF_NOTE_NETBSD_NAMESZ, ELF_NOTE_NETBSD_DESCSZ, ELF_NOTE_NETBSD_TAG) - - if sh == nil { - return 0 - } - - // Followed by NetBSD string and version. - Cwrite(ELF_NOTE_NETBSD_NAME) - Cput(0) - - Thearch.Lput(ELF_NOTE_NETBSD_VERSION) - - return int(sh.size) -} - -// OpenBSD Signature -const ( - ELF_NOTE_OPENBSD_NAMESZ = 8 - ELF_NOTE_OPENBSD_DESCSZ = 4 - ELF_NOTE_OPENBSD_TAG = 1 - ELF_NOTE_OPENBSD_VERSION = 0 -) - -var ELF_NOTE_OPENBSD_NAME = []byte("OpenBSD\x00") - -func elfopenbsdsig(sh *ElfShdr, startva uint64, resoff uint64) int { - n := ELF_NOTE_OPENBSD_NAMESZ + ELF_NOTE_OPENBSD_DESCSZ - return elfnote(sh, startva, resoff, n, true) -} - -func elfwriteopenbsdsig() int { - // Write Elf_Note header. - sh := elfwritenotehdr(".note.openbsd.ident", ELF_NOTE_OPENBSD_NAMESZ, ELF_NOTE_OPENBSD_DESCSZ, ELF_NOTE_OPENBSD_TAG) - - if sh == nil { - return 0 - } - - // Followed by OpenBSD string and version. - Cwrite(ELF_NOTE_OPENBSD_NAME) - - Thearch.Lput(ELF_NOTE_OPENBSD_VERSION) - - return int(sh.size) -} - -func addbuildinfo(val string) { - var j int - - if val[0] != '0' || val[1] != 'x' { - Exitf("-B argument must start with 0x: %s", val) - } - - ov := val - val = val[2:] - i := 0 - var b int - for val != "" { - if len(val) == 1 { - Exitf("-B argument must have even number of digits: %s", ov) - } - - b = 0 - for j = 0; j < 2; j, val = j+1, val[1:] { - b *= 16 - if val[0] >= '0' && val[0] <= '9' { - b += int(val[0]) - '0' - } else if val[0] >= 'a' && val[0] <= 'f' { - b += int(val[0]) - 'a' + 10 - } else if val[0] >= 'A' && val[0] <= 'F' { - b += int(val[0]) - 'A' + 10 - } else { - Exitf("-B argument contains invalid hex digit %c: %s", val[0], ov) - } - } - - const maxLen = 32 - if i >= maxLen { - Exitf("-B option too long (max %d digits): %s", maxLen, ov) - } - - buildinfo = append(buildinfo, uint8(b)) - i++ - } - - buildinfo = buildinfo[:i] -} - -// Build info note -const ( - ELF_NOTE_BUILDINFO_NAMESZ = 4 - ELF_NOTE_BUILDINFO_TAG = 3 -) - -var ELF_NOTE_BUILDINFO_NAME = []byte("GNU\x00") - -func elfbuildinfo(sh *ElfShdr, startva uint64, resoff uint64) int { - n := int(ELF_NOTE_BUILDINFO_NAMESZ + Rnd(int64(len(buildinfo)), 4)) - return elfnote(sh, startva, resoff, n, true) -} - -func elfwritebuildinfo() int { - sh := elfwritenotehdr(".note.gnu.build-id", ELF_NOTE_BUILDINFO_NAMESZ, uint32(len(buildinfo)), ELF_NOTE_BUILDINFO_TAG) - if sh == nil { - return 0 - } - - Cwrite(ELF_NOTE_BUILDINFO_NAME) - Cwrite(buildinfo) - var zero = make([]byte, 4) - Cwrite(zero[:int(Rnd(int64(len(buildinfo)), 4)-int64(len(buildinfo)))]) - - return int(sh.size) -} - -// Go package list note -const ( - ELF_NOTE_GOPKGLIST_TAG = 1 -) - -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 { - next *Elfaux - num int - vers string -} - -type Elflib struct { - next *Elflib - aux *Elfaux - file string -} - -func addelflib(list **Elflib, file string, vers string) *Elfaux { - var lib *Elflib - - for lib = *list; lib != nil; lib = lib.next { - if lib.file == file { - goto havelib - } - } - lib = new(Elflib) - lib.next = *list - lib.file = file - *list = lib - -havelib: - for aux := lib.aux; aux != nil; aux = aux.next { - if aux.vers == vers { - return aux - } - } - aux := new(Elfaux) - aux.next = lib.aux - aux.vers = vers - lib.aux = aux - - return aux -} - -func elfdynhash() { - if !Iself { - return - } - - nsym := Nelfsym - s := Linklookup(Ctxt, ".hash", 0) - s.Type = obj.SELFROSECT - s.Reachable = true - - i := nsym - nbucket := 1 - for i > 0 { - nbucket++ - i >>= 1 - } - - var needlib *Elflib - need := make([]*Elfaux, nsym) - chain := make([]uint32, nsym) - buckets := make([]uint32, nbucket) - - var b int - var hc uint32 - var name string - for sy := Ctxt.Allsym; sy != nil; sy = sy.Allsym { - if sy.Dynid <= 0 { - continue - } - - if sy.Dynimpvers != "" { - need[sy.Dynid] = addelflib(&needlib, sy.Dynimplib, sy.Dynimpvers) - } - - name = sy.Extname - hc = elfhash([]byte(name)) - - b = int(hc % uint32(nbucket)) - chain[sy.Dynid] = buckets[b] - buckets[b] = uint32(sy.Dynid) - } - - Adduint32(Ctxt, s, uint32(nbucket)) - Adduint32(Ctxt, s, uint32(nsym)) - for i := 0; i < nbucket; i++ { - Adduint32(Ctxt, s, buckets[i]) - } - for i := 0; i < nsym; i++ { - Adduint32(Ctxt, s, chain[i]) - } - - // version symbols - dynstr := Linklookup(Ctxt, ".dynstr", 0) - - s = Linklookup(Ctxt, ".gnu.version_r", 0) - i = 2 - nfile := 0 - var j int - var x *Elfaux - for l := needlib; l != nil; l = l.next { - nfile++ - - // header - Adduint16(Ctxt, s, 1) // table version - j = 0 - for x = l.aux; x != nil; x = x.next { - j++ - } - Adduint16(Ctxt, s, uint16(j)) // aux count - Adduint32(Ctxt, s, uint32(Addstring(dynstr, l.file))) // file string offset - Adduint32(Ctxt, s, 16) // offset from header to first aux - if l.next != nil { - Adduint32(Ctxt, s, 16+uint32(j)*16) // offset from this header to next - } else { - Adduint32(Ctxt, s, 0) - } - - for x = l.aux; x != nil; x = x.next { - x.num = i - i++ - - // aux struct - Adduint32(Ctxt, s, elfhash([]byte(x.vers))) // hash - Adduint16(Ctxt, s, 0) // flags - Adduint16(Ctxt, s, uint16(x.num)) // other - index we refer to this by - Adduint32(Ctxt, s, uint32(Addstring(dynstr, x.vers))) // version string offset - if x.next != nil { - Adduint32(Ctxt, s, 16) // offset from this aux to next - } else { - Adduint32(Ctxt, s, 0) - } - } - } - - // version references - s = Linklookup(Ctxt, ".gnu.version", 0) - - for i := 0; i < nsym; i++ { - if i == 0 { - Adduint16(Ctxt, s, 0) // first entry - no symbol - } else if need[i] == nil { - Adduint16(Ctxt, s, 1) // global - } else { - Adduint16(Ctxt, s, uint16(need[i].num)) - } - } - - s = Linklookup(Ctxt, ".dynamic", 0) - elfverneed = nfile - if elfverneed != 0 { - elfwritedynentsym(s, DT_VERNEED, Linklookup(Ctxt, ".gnu.version_r", 0)) - Elfwritedynent(s, DT_VERNEEDNUM, uint64(nfile)) - elfwritedynentsym(s, DT_VERSYM, Linklookup(Ctxt, ".gnu.version", 0)) - } - - switch Thearch.Thechar { - case '6', '7', '9': - sy := Linklookup(Ctxt, ".rela.plt", 0) - if sy.Size > 0 { - Elfwritedynent(s, DT_PLTREL, DT_RELA) - elfwritedynentsymsize(s, DT_PLTRELSZ, sy) - elfwritedynentsym(s, DT_JMPREL, sy) - } - default: - sy := Linklookup(Ctxt, ".rel.plt", 0) - if sy.Size > 0 { - Elfwritedynent(s, DT_PLTREL, DT_REL) - elfwritedynentsymsize(s, DT_PLTRELSZ, sy) - elfwritedynentsym(s, DT_JMPREL, sy) - } - } - - Elfwritedynent(s, DT_NULL, 0) -} - -func elfphload(seg *Segment) *ElfPhdr { - ph := newElfPhdr() - ph.type_ = PT_LOAD - if seg.Rwx&4 != 0 { - ph.flags |= PF_R - } - if seg.Rwx&2 != 0 { - ph.flags |= PF_W - } - if seg.Rwx&1 != 0 { - ph.flags |= PF_X - } - ph.vaddr = seg.Vaddr - ph.paddr = seg.Vaddr - ph.memsz = seg.Length - ph.off = seg.Fileoff - ph.filesz = seg.Filelen - ph.align = uint64(INITRND) - - return ph -} - -func elfshname(name string) *ElfShdr { - var off int - var sh *ElfShdr - - for i := 0; i < nelfstr; i++ { - if name == elfstr[i].s { - off = elfstr[i].off - for i = 0; i < int(ehdr.shnum); i++ { - sh = shdr[i] - if sh.name == uint32(off) { - return sh - } - } - - sh = newElfShdr(int64(off)) - return sh - } - } - - Diag("cannot find elf name %s", name) - errorexit() - return nil -} - -func elfshalloc(sect *Section) *ElfShdr { - sh := elfshname(sect.Name) - sect.Elfsect = sh - return sh -} - -func elfshbits(sect *Section) *ElfShdr { - sh := elfshalloc(sect) - if sh.type_ > 0 { - return sh - } - - if sect.Vaddr < sect.Seg.Vaddr+sect.Seg.Filelen { - sh.type_ = SHT_PROGBITS - } else { - sh.type_ = SHT_NOBITS - } - sh.flags = SHF_ALLOC - if sect.Rwx&1 != 0 { - sh.flags |= SHF_EXECINSTR - } - if sect.Rwx&2 != 0 { - sh.flags |= SHF_WRITE - } - if sect.Name == ".tbss" { - if goos != "android" { - sh.flags |= SHF_TLS // no TLS on android - } - sh.type_ = SHT_NOBITS - } - - if Linkmode != LinkExternal { - sh.addr = sect.Vaddr - } - sh.addralign = uint64(sect.Align) - sh.size = sect.Length - sh.off = sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr - - return sh -} - -func elfshreloc(sect *Section) *ElfShdr { - // If main section is SHT_NOBITS, nothing to relocate. - // Also nothing to relocate in .shstrtab. - if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { - return nil - } - if sect.Name == ".shstrtab" || sect.Name == ".tbss" { - return nil - } - - var prefix string - var typ int - switch Thearch.Thechar { - case '6', '7', '9': - prefix = ".rela" - typ = SHT_RELA - default: - prefix = ".rel" - typ = SHT_REL - } - - buf := fmt.Sprintf("%s%s", prefix, sect.Name) - sh := elfshname(buf) - sh.type_ = uint32(typ) - sh.entsize = uint64(Thearch.Regsize) * 2 - if typ == SHT_RELA { - sh.entsize += uint64(Thearch.Regsize) - } - sh.link = uint32(elfshname(".symtab").shnum) - sh.info = uint32((sect.Elfsect.(*ElfShdr)).shnum) - sh.off = sect.Reloff - sh.size = sect.Rellen - sh.addralign = uint64(Thearch.Regsize) - return sh -} - -func elfrelocsect(sect *Section, first *LSym) { - // If main section is SHT_NOBITS, nothing to relocate. - // Also nothing to relocate in .shstrtab. - if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { - return - } - if sect.Name == ".shstrtab" { - return - } - - sect.Reloff = uint64(Cpos()) - var sym *LSym - for sym = first; sym != nil; sym = sym.Next { - if !sym.Reachable { - continue - } - if uint64(sym.Value) >= sect.Vaddr { - break - } - } - - eaddr := int32(sect.Vaddr + sect.Length) - var r *Reloc - var ri int - for ; sym != nil; sym = sym.Next { - if !sym.Reachable { - continue - } - if sym.Value >= int64(eaddr) { - break - } - Ctxt.Cursym = sym - - for ri = 0; ri < len(sym.R); ri++ { - r = &sym.R[ri] - if r.Done != 0 { - continue - } - if r.Xsym == nil { - Diag("missing xsym in relocation") - continue - } - - if r.Xsym.Elfsym == 0 { - Diag("reloc %d to non-elf symbol %s (outer=%s) %d", r.Type, r.Sym.Name, r.Xsym.Name, r.Sym.Type) - } - if Thearch.Elfreloc1(r, int64(uint64(sym.Value+int64(r.Off))-sect.Vaddr)) < 0 { - Diag("unsupported obj reloc %d/%d to %s", r.Type, r.Siz, r.Sym.Name) - } - } - } - - sect.Rellen = uint64(Cpos()) - sect.Reloff -} - -func Elfemitreloc() { - for Cpos()&7 != 0 { - Cput(0) - } - - elfrelocsect(Segtext.Sect, Ctxt.Textp) - for sect := Segtext.Sect.Next; sect != nil; sect = sect.Next { - elfrelocsect(sect, datap) - } - for sect := Segrodata.Sect; sect != nil; sect = sect.Next { - elfrelocsect(sect, datap) - } - for sect := Segdata.Sect; sect != nil; sect = sect.Next { - elfrelocsect(sect, datap) - } -} - -func doelf() { - if !Iself { - return - } - - /* predefine strings we need for section headers */ - shstrtab := Linklookup(Ctxt, ".shstrtab", 0) - - shstrtab.Type = obj.SELFROSECT - shstrtab.Reachable = true - - Addstring(shstrtab, "") - Addstring(shstrtab, ".text") - Addstring(shstrtab, ".noptrdata") - Addstring(shstrtab, ".data") - Addstring(shstrtab, ".bss") - Addstring(shstrtab, ".noptrbss") - - // generate .tbss section (except for OpenBSD where it's not supported) - // for dynamic internal linker or external linking, so that various - // binutils could correctly calculate PT_TLS size. - // see http://golang.org/issue/5200. - if HEADTYPE != obj.Hopenbsd { - if Debug['d'] == 0 || Linkmode == LinkExternal { - Addstring(shstrtab, ".tbss") - } - } - if HEADTYPE == obj.Hnetbsd { - Addstring(shstrtab, ".note.netbsd.ident") - } - if HEADTYPE == obj.Hopenbsd { - Addstring(shstrtab, ".note.openbsd.ident") - } - 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") - Addstring(shstrtab, ".gosymtab") - Addstring(shstrtab, ".gopclntab") - - if Linkmode == LinkExternal { - debug_s = Debug['s'] - Debug['s'] = 0 - Debug['d'] = 1 - - switch Thearch.Thechar { - case '6', '7', '9': - Addstring(shstrtab, ".rela.text") - Addstring(shstrtab, ".rela.rodata") - Addstring(shstrtab, ".rela.typelink") - Addstring(shstrtab, ".rela.gosymtab") - Addstring(shstrtab, ".rela.gopclntab") - Addstring(shstrtab, ".rela.noptrdata") - Addstring(shstrtab, ".rela.data") - - default: - Addstring(shstrtab, ".rel.text") - Addstring(shstrtab, ".rel.rodata") - Addstring(shstrtab, ".rel.typelink") - Addstring(shstrtab, ".rel.gosymtab") - Addstring(shstrtab, ".rel.gopclntab") - Addstring(shstrtab, ".rel.noptrdata") - Addstring(shstrtab, ".rel.data") - } - - // add a .note.GNU-stack section to mark the stack as non-executable - Addstring(shstrtab, ".note.GNU-stack") - } - - hasinitarr := Linkshared - - /* shared library initializer */ - switch Buildmode { - case BuildmodeCArchive, BuildmodeCShared, BuildmodeShared: - hasinitarr = true - } - - if hasinitarr { - Addstring(shstrtab, ".init_array") - switch Thearch.Thechar { - case '6', '7', '9': - Addstring(shstrtab, ".rela.init_array") - default: - Addstring(shstrtab, ".rel.init_array") - } - } - - if Debug['s'] == 0 { - Addstring(shstrtab, ".symtab") - Addstring(shstrtab, ".strtab") - dwarfaddshstrings(shstrtab) - } - - Addstring(shstrtab, ".shstrtab") - - if Debug['d'] == 0 { /* -d suppresses dynamic loader format */ - Addstring(shstrtab, ".interp") - Addstring(shstrtab, ".hash") - Addstring(shstrtab, ".got") - if Thearch.Thechar == '9' { - Addstring(shstrtab, ".glink") - } - Addstring(shstrtab, ".got.plt") - Addstring(shstrtab, ".dynamic") - Addstring(shstrtab, ".dynsym") - Addstring(shstrtab, ".dynstr") - switch Thearch.Thechar { - case '6', '7', '9': - Addstring(shstrtab, ".rela") - Addstring(shstrtab, ".rela.plt") - default: - Addstring(shstrtab, ".rel") - Addstring(shstrtab, ".rel.plt") - } - - Addstring(shstrtab, ".plt") - Addstring(shstrtab, ".gnu.version") - Addstring(shstrtab, ".gnu.version_r") - - /* dynamic symbol table - first entry all zeros */ - s := Linklookup(Ctxt, ".dynsym", 0) - - s.Type = obj.SELFROSECT - s.Reachable = true - switch Thearch.Thechar { - case '6', '7', '9': - s.Size += ELF64SYMSIZE - default: - s.Size += ELF32SYMSIZE - } - - /* dynamic string table */ - s = Linklookup(Ctxt, ".dynstr", 0) - - s.Type = obj.SELFROSECT - s.Reachable = true - if s.Size == 0 { - Addstring(s, "") - } - dynstr := s - - /* relocation table */ - switch Thearch.Thechar { - case '6', '7', '9': - s = Linklookup(Ctxt, ".rela", 0) - default: - s = Linklookup(Ctxt, ".rel", 0) - } - s.Reachable = true - s.Type = obj.SELFROSECT - - /* global offset table */ - s = Linklookup(Ctxt, ".got", 0) - - s.Reachable = true - s.Type = obj.SELFGOT // writable - - /* ppc64 glink resolver */ - if Thearch.Thechar == '9' { - s := Linklookup(Ctxt, ".glink", 0) - s.Reachable = true - s.Type = obj.SELFRXSECT - } - - /* hash */ - s = Linklookup(Ctxt, ".hash", 0) - - s.Reachable = true - s.Type = obj.SELFROSECT - - s = Linklookup(Ctxt, ".got.plt", 0) - s.Reachable = true - s.Type = obj.SELFSECT // writable - - s = Linklookup(Ctxt, ".plt", 0) - - s.Reachable = true - if Thearch.Thechar == '9' { - // In the ppc64 ABI, .plt is a data section - // written by the dynamic linker. - s.Type = obj.SELFSECT - } else { - s.Type = obj.SELFRXSECT - } - - Thearch.Elfsetupplt() - - switch Thearch.Thechar { - case '6', '7', '9': - s = Linklookup(Ctxt, ".rela.plt", 0) - default: - s = Linklookup(Ctxt, ".rel.plt", 0) - } - s.Reachable = true - s.Type = obj.SELFROSECT - - s = Linklookup(Ctxt, ".gnu.version", 0) - s.Reachable = true - s.Type = obj.SELFROSECT - - s = Linklookup(Ctxt, ".gnu.version_r", 0) - s.Reachable = true - s.Type = obj.SELFROSECT - - /* define dynamic elf table */ - s = Linklookup(Ctxt, ".dynamic", 0) - - s.Reachable = true - s.Type = obj.SELFSECT // writable - - /* - * .dynamic table - */ - elfwritedynentsym(s, DT_HASH, Linklookup(Ctxt, ".hash", 0)) - - elfwritedynentsym(s, DT_SYMTAB, Linklookup(Ctxt, ".dynsym", 0)) - switch Thearch.Thechar { - case '6', '7', '9': - Elfwritedynent(s, DT_SYMENT, ELF64SYMSIZE) - default: - Elfwritedynent(s, DT_SYMENT, ELF32SYMSIZE) - } - elfwritedynentsym(s, DT_STRTAB, Linklookup(Ctxt, ".dynstr", 0)) - elfwritedynentsymsize(s, DT_STRSZ, Linklookup(Ctxt, ".dynstr", 0)) - switch Thearch.Thechar { - case '6', '7', '9': - elfwritedynentsym(s, DT_RELA, Linklookup(Ctxt, ".rela", 0)) - elfwritedynentsymsize(s, DT_RELASZ, Linklookup(Ctxt, ".rela", 0)) - Elfwritedynent(s, DT_RELAENT, ELF64RELASIZE) - default: - elfwritedynentsym(s, DT_REL, Linklookup(Ctxt, ".rel", 0)) - elfwritedynentsymsize(s, DT_RELSZ, Linklookup(Ctxt, ".rel", 0)) - Elfwritedynent(s, DT_RELENT, ELF32RELSIZE) - } - - if rpath.val != "" { - Elfwritedynent(s, DT_RUNPATH, uint64(Addstring(dynstr, rpath.val))) - } - - if Thearch.Thechar == '9' { - elfwritedynentsym(s, DT_PLTGOT, Linklookup(Ctxt, ".plt", 0)) - } else { - elfwritedynentsym(s, DT_PLTGOT, Linklookup(Ctxt, ".got.plt", 0)) - } - - if Thearch.Thechar == '9' { - Elfwritedynent(s, DT_PPC64_OPT, 0) - } - - // Solaris dynamic linker can't handle an empty .rela.plt if - // DT_JMPREL is emitted so we have to defer generation of DT_PLTREL, - // DT_PLTRELSZ, and DT_JMPREL dynamic entries until after we know the - // size of .rel(a).plt section. - Elfwritedynent(s, DT_DEBUG, 0) - } -} - -// Do not write DT_NULL. elfdynhash will finish it. -func shsym(sh *ElfShdr, s *LSym) { - addr := Symaddr(s) - if sh.flags&SHF_ALLOC != 0 { - sh.addr = uint64(addr) - } - sh.off = uint64(datoff(addr)) - sh.size = uint64(s.Size) -} - -func phsh(ph *ElfPhdr, sh *ElfShdr) { - ph.vaddr = sh.addr - ph.paddr = ph.vaddr - ph.off = sh.off - ph.filesz = sh.size - ph.memsz = sh.size - ph.align = sh.addralign -} - -func Asmbelfsetup() { - /* This null SHdr must appear before all others */ - elfshname("") - - for sect := Segtext.Sect; sect != nil; sect = sect.Next { - elfshalloc(sect) - } - for sect := Segrodata.Sect; sect != nil; sect = sect.Next { - elfshalloc(sect) - } - for sect := Segdata.Sect; sect != nil; sect = sect.Next { - elfshalloc(sect) - } -} - -func Asmbelf(symo int64) { - eh := getElfEhdr() - switch Thearch.Thechar { - default: - Exitf("unknown architecture in asmbelf: %v", Thearch.Thechar) - case '5': - eh.machine = EM_ARM - case '6': - eh.machine = EM_X86_64 - case '7': - eh.machine = EM_AARCH64 - case '8': - eh.machine = EM_386 - case '9': - eh.machine = EM_PPC64 - } - - elfreserve := int64(ELFRESERVE) - startva := INITTEXT - int64(HEADR) - resoff := elfreserve - - var pph *ElfPhdr - var pnote *ElfPhdr - if Linkmode == LinkExternal { - /* skip program headers */ - eh.phoff = 0 - - 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))) - } - goto elfobj - } - - /* program header info */ - pph = newElfPhdr() - - pph.type_ = PT_PHDR - pph.flags = PF_R - pph.off = uint64(eh.ehsize) - pph.vaddr = uint64(INITTEXT) - uint64(HEADR) + pph.off - pph.paddr = uint64(INITTEXT) - uint64(HEADR) + pph.off - pph.align = uint64(INITRND) - - /* - * PHDR must be in a loaded segment. Adjust the text - * segment boundaries downwards to include it. - * Except on NaCl where it must not be loaded. - */ - if HEADTYPE != obj.Hnacl { - o := int64(Segtext.Vaddr - pph.vaddr) - Segtext.Vaddr -= uint64(o) - Segtext.Length += uint64(o) - o = int64(Segtext.Fileoff - pph.off) - Segtext.Fileoff -= uint64(o) - Segtext.Filelen += uint64(o) - } - - if Debug['d'] == 0 { /* -d suppresses dynamic loader format */ - /* interpreter */ - sh := elfshname(".interp") - - sh.type_ = SHT_PROGBITS - sh.flags = SHF_ALLOC - sh.addralign = 1 - if interpreter == "" { - switch HEADTYPE { - case obj.Hlinux: - interpreter = Thearch.Linuxdynld - - case obj.Hfreebsd: - interpreter = Thearch.Freebsddynld - - case obj.Hnetbsd: - interpreter = Thearch.Netbsddynld - - case obj.Hopenbsd: - interpreter = Thearch.Openbsddynld - - case obj.Hdragonfly: - interpreter = Thearch.Dragonflydynld - - case obj.Hsolaris: - interpreter = Thearch.Solarisdynld - } - } - - resoff -= int64(elfinterp(sh, uint64(startva), uint64(resoff), interpreter)) - - ph := newElfPhdr() - ph.type_ = PT_INTERP - ph.flags = PF_R - phsh(ph, sh) - } - - pnote = nil - if HEADTYPE == obj.Hnetbsd || HEADTYPE == obj.Hopenbsd { - var sh *ElfShdr - switch HEADTYPE { - case obj.Hnetbsd: - sh = elfshname(".note.netbsd.ident") - resoff -= int64(elfnetbsdsig(sh, uint64(startva), uint64(resoff))) - - case obj.Hopenbsd: - sh = elfshname(".note.openbsd.ident") - resoff -= int64(elfopenbsdsig(sh, uint64(startva), uint64(resoff))) - } - - pnote = newElfPhdr() - pnote.type_ = PT_NOTE - pnote.flags = PF_R - phsh(pnote, sh) - } - - if len(buildinfo) > 0 { - sh := elfshname(".note.gnu.build-id") - resoff -= int64(elfbuildinfo(sh, uint64(startva), uint64(resoff))) - - if pnote == nil { - pnote = newElfPhdr() - pnote.type_ = PT_NOTE - pnote.flags = PF_R - } - - phsh(pnote, sh) - } - - // Additions to the reserved area must be above this line. - - elfphload(&Segtext) - if Segrodata.Sect != nil { - elfphload(&Segrodata) - } - elfphload(&Segdata) - - /* Dynamic linking sections */ - if Debug['d'] == 0 { - sh := elfshname(".dynsym") - sh.type_ = SHT_DYNSYM - sh.flags = SHF_ALLOC - if elf64 { - sh.entsize = ELF64SYMSIZE - } else { - sh.entsize = ELF32SYMSIZE - } - sh.addralign = uint64(Thearch.Regsize) - sh.link = uint32(elfshname(".dynstr").shnum) - - // sh->info = index of first non-local symbol (number of local symbols) - shsym(sh, Linklookup(Ctxt, ".dynsym", 0)) - - sh = elfshname(".dynstr") - sh.type_ = SHT_STRTAB - sh.flags = SHF_ALLOC - sh.addralign = 1 - shsym(sh, Linklookup(Ctxt, ".dynstr", 0)) - - if elfverneed != 0 { - sh := elfshname(".gnu.version") - sh.type_ = SHT_GNU_VERSYM - sh.flags = SHF_ALLOC - sh.addralign = 2 - sh.link = uint32(elfshname(".dynsym").shnum) - sh.entsize = 2 - shsym(sh, Linklookup(Ctxt, ".gnu.version", 0)) - - sh = elfshname(".gnu.version_r") - sh.type_ = SHT_GNU_VERNEED - sh.flags = SHF_ALLOC - sh.addralign = uint64(Thearch.Regsize) - sh.info = uint32(elfverneed) - sh.link = uint32(elfshname(".dynstr").shnum) - shsym(sh, Linklookup(Ctxt, ".gnu.version_r", 0)) - } - - switch eh.machine { - case EM_X86_64, EM_PPC64, EM_AARCH64: - sh := elfshname(".rela.plt") - sh.type_ = SHT_RELA - sh.flags = SHF_ALLOC - sh.entsize = ELF64RELASIZE - sh.addralign = uint64(Thearch.Regsize) - sh.link = uint32(elfshname(".dynsym").shnum) - sh.info = uint32(elfshname(".plt").shnum) - shsym(sh, Linklookup(Ctxt, ".rela.plt", 0)) - - sh = elfshname(".rela") - sh.type_ = SHT_RELA - sh.flags = SHF_ALLOC - sh.entsize = ELF64RELASIZE - sh.addralign = 8 - sh.link = uint32(elfshname(".dynsym").shnum) - shsym(sh, Linklookup(Ctxt, ".rela", 0)) - - default: - sh := elfshname(".rel.plt") - sh.type_ = SHT_REL - sh.flags = SHF_ALLOC - sh.entsize = ELF32RELSIZE - sh.addralign = 4 - sh.link = uint32(elfshname(".dynsym").shnum) - shsym(sh, Linklookup(Ctxt, ".rel.plt", 0)) - - sh = elfshname(".rel") - sh.type_ = SHT_REL - sh.flags = SHF_ALLOC - sh.entsize = ELF32RELSIZE - sh.addralign = 4 - sh.link = uint32(elfshname(".dynsym").shnum) - shsym(sh, Linklookup(Ctxt, ".rel", 0)) - } - - if eh.machine == EM_PPC64 { - sh := elfshname(".glink") - sh.type_ = SHT_PROGBITS - sh.flags = SHF_ALLOC + SHF_EXECINSTR - sh.addralign = 4 - shsym(sh, Linklookup(Ctxt, ".glink", 0)) - } - - sh = elfshname(".plt") - sh.type_ = SHT_PROGBITS - sh.flags = SHF_ALLOC + SHF_EXECINSTR - if eh.machine == EM_X86_64 { - sh.entsize = 16 - } else if eh.machine == EM_PPC64 { - // On ppc64, this is just a table of addresses - // filled by the dynamic linker - sh.type_ = SHT_NOBITS - - sh.flags = SHF_ALLOC + SHF_WRITE - sh.entsize = 8 - } else { - sh.entsize = 4 - } - sh.addralign = sh.entsize - shsym(sh, Linklookup(Ctxt, ".plt", 0)) - - // On ppc64, .got comes from the input files, so don't - // create it here, and .got.plt is not used. - if eh.machine != EM_PPC64 { - sh := elfshname(".got") - sh.type_ = SHT_PROGBITS - sh.flags = SHF_ALLOC + SHF_WRITE - sh.entsize = uint64(Thearch.Regsize) - sh.addralign = uint64(Thearch.Regsize) - shsym(sh, Linklookup(Ctxt, ".got", 0)) - - sh = elfshname(".got.plt") - sh.type_ = SHT_PROGBITS - sh.flags = SHF_ALLOC + SHF_WRITE - sh.entsize = uint64(Thearch.Regsize) - sh.addralign = uint64(Thearch.Regsize) - shsym(sh, Linklookup(Ctxt, ".got.plt", 0)) - } - - sh = elfshname(".hash") - sh.type_ = SHT_HASH - sh.flags = SHF_ALLOC - sh.entsize = 4 - sh.addralign = uint64(Thearch.Regsize) - sh.link = uint32(elfshname(".dynsym").shnum) - shsym(sh, Linklookup(Ctxt, ".hash", 0)) - - /* sh and PT_DYNAMIC for .dynamic section */ - sh = elfshname(".dynamic") - - sh.type_ = SHT_DYNAMIC - sh.flags = SHF_ALLOC + SHF_WRITE - sh.entsize = 2 * uint64(Thearch.Regsize) - sh.addralign = uint64(Thearch.Regsize) - sh.link = uint32(elfshname(".dynstr").shnum) - shsym(sh, Linklookup(Ctxt, ".dynamic", 0)) - ph := newElfPhdr() - ph.type_ = PT_DYNAMIC - ph.flags = PF_R + PF_W - phsh(ph, sh) - - /* - * Thread-local storage segment (really just size). - */ - // Do not emit PT_TLS for OpenBSD since ld.so(1) does - // not currently support it. This is handled - // appropriately in runtime/cgo. - if Ctxt.Tlsoffset != 0 && HEADTYPE != obj.Hopenbsd { - ph := newElfPhdr() - ph.type_ = PT_TLS - ph.flags = PF_R - ph.memsz = uint64(-Ctxt.Tlsoffset) - ph.align = uint64(Thearch.Regsize) - } - } - - if HEADTYPE == obj.Hlinux { - ph := newElfPhdr() - ph.type_ = PT_GNU_STACK - ph.flags = PF_W + PF_R - ph.align = uint64(Thearch.Regsize) - - ph = newElfPhdr() - ph.type_ = PT_PAX_FLAGS - ph.flags = 0x2a00 // mprotect, randexec, emutramp disabled - ph.align = uint64(Thearch.Regsize) - } - -elfobj: - sh := elfshname(".shstrtab") - sh.type_ = SHT_STRTAB - sh.addralign = 1 - shsym(sh, Linklookup(Ctxt, ".shstrtab", 0)) - eh.shstrndx = uint16(sh.shnum) - - // put these sections early in the list - if Debug['s'] == 0 { - elfshname(".symtab") - elfshname(".strtab") - } - - for sect := Segtext.Sect; sect != nil; sect = sect.Next { - elfshbits(sect) - } - for sect := Segrodata.Sect; sect != nil; sect = sect.Next { - elfshbits(sect) - } - for sect := Segdata.Sect; sect != nil; sect = sect.Next { - elfshbits(sect) - } - - if Linkmode == LinkExternal { - for sect := Segtext.Sect; sect != nil; sect = sect.Next { - elfshreloc(sect) - } - for sect := Segrodata.Sect; sect != nil; sect = sect.Next { - elfshreloc(sect) - } - for sect := Segdata.Sect; sect != nil; sect = sect.Next { - elfshreloc(sect) - } - - // add a .note.GNU-stack section to mark the stack as non-executable - sh := elfshname(".note.GNU-stack") - - sh.type_ = SHT_PROGBITS - sh.addralign = 1 - sh.flags = 0 - } - - // generate .tbss section for dynamic internal linking (except for OpenBSD) - // external linking generates .tbss in data.c - if Linkmode == LinkInternal && Debug['d'] == 0 && HEADTYPE != obj.Hopenbsd { - sh := elfshname(".tbss") - sh.type_ = SHT_NOBITS - sh.addralign = uint64(Thearch.Regsize) - sh.size = uint64(-Ctxt.Tlsoffset) - sh.flags = SHF_ALLOC | SHF_TLS | SHF_WRITE - } - - if Debug['s'] == 0 { - sh := elfshname(".symtab") - sh.type_ = SHT_SYMTAB - sh.off = uint64(symo) - sh.size = uint64(Symsize) - sh.addralign = uint64(Thearch.Regsize) - sh.entsize = 8 + 2*uint64(Thearch.Regsize) - sh.link = uint32(elfshname(".strtab").shnum) - sh.info = uint32(elfglobalsymndx) - - sh = elfshname(".strtab") - sh.type_ = SHT_STRTAB - sh.off = uint64(symo) + uint64(Symsize) - sh.size = uint64(len(Elfstrdat)) - sh.addralign = 1 - - dwarfaddelfheaders() - } - - /* Main header */ - eh.ident[EI_MAG0] = '\177' - - eh.ident[EI_MAG1] = 'E' - eh.ident[EI_MAG2] = 'L' - eh.ident[EI_MAG3] = 'F' - if HEADTYPE == obj.Hfreebsd { - eh.ident[EI_OSABI] = ELFOSABI_FREEBSD - } else if HEADTYPE == obj.Hnetbsd { - eh.ident[EI_OSABI] = ELFOSABI_NETBSD - } else if HEADTYPE == obj.Hopenbsd { - eh.ident[EI_OSABI] = ELFOSABI_OPENBSD - } else if HEADTYPE == obj.Hdragonfly { - eh.ident[EI_OSABI] = ELFOSABI_NONE - } - if elf64 { - eh.ident[EI_CLASS] = ELFCLASS64 - } else { - eh.ident[EI_CLASS] = ELFCLASS32 - } - if Ctxt.Arch.ByteOrder == binary.BigEndian { - eh.ident[EI_DATA] = ELFDATA2MSB - } else { - eh.ident[EI_DATA] = ELFDATA2LSB - } - eh.ident[EI_VERSION] = EV_CURRENT - - if Linkmode == LinkExternal { - eh.type_ = ET_REL - } else { - eh.type_ = ET_EXEC - } - - if Linkmode != LinkExternal { - eh.entry = uint64(Entryvalue()) - } - - eh.version = EV_CURRENT - - if pph != nil { - pph.filesz = uint64(eh.phnum) * uint64(eh.phentsize) - pph.memsz = pph.filesz - } - - Cseek(0) - a := int64(0) - a += int64(elfwritehdr()) - a += int64(elfwritephdrs()) - a += int64(elfwriteshdrs()) - if Debug['d'] == 0 { - a += int64(elfwriteinterp()) - } - if Linkmode != LinkExternal { - if HEADTYPE == obj.Hnetbsd { - a += int64(elfwritenetbsdsig()) - } - if HEADTYPE == obj.Hopenbsd { - a += int64(elfwriteopenbsdsig()) - } - if len(buildinfo) > 0 { - a += int64(elfwritebuildinfo()) - } - } - if Buildmode == BuildmodeShared { - a += int64(elfwritegopkgnote()) - } - - if a > elfreserve { - Diag("ELFRESERVE too small: %d > %d", a, elfreserve) - } -} - -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 -} - -func ELF32_R_TYPE(info uint32) uint32 { - return uint32(uint8(info)) -} - -func ELF32_R_INFO(sym uint32, type_ uint32) uint32 { - return sym<<8 | type_ -} - -func ELF32_ST_BIND(info uint8) uint8 { - return info >> 4 -} - -func ELF32_ST_TYPE(info uint8) uint8 { - return info & 0xf -} - -func ELF32_ST_INFO(bind uint8, type_ uint8) uint8 { - return bind<<4 | type_&0xf -} - -func ELF32_ST_VISIBILITY(oth uint8) uint8 { - return oth & 3 -} - -func ELF64_R_SYM(info uint64) uint32 { - return uint32(info >> 32) -} - -func ELF64_R_TYPE(info uint64) uint32 { - return uint32(info) -} - -func ELF64_R_INFO(sym uint32, type_ uint32) uint64 { - return uint64(sym)<<32 | uint64(type_) -} - -func ELF64_ST_BIND(info uint8) uint8 { - return info >> 4 -} - -func ELF64_ST_TYPE(info uint8) uint8 { - return info & 0xf -} - -func ELF64_ST_INFO(bind uint8, type_ uint8) uint8 { - return bind<<4 | type_&0xf -} - -func ELF64_ST_VISIBILITY(oth uint8) uint8 { - return oth & 3 -} diff --git a/src/cmd/internal/ld/go.go b/src/cmd/internal/ld/go.go deleted file mode 100644 index 875b8d2e17..0000000000 --- a/src/cmd/internal/ld/go.go +++ /dev/null @@ -1,870 +0,0 @@ -// Copyright 2009 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. - -// go-specific code shared across loaders (5l, 6l, 8l). - -package ld - -import ( - "bytes" - "cmd/internal/obj" - "fmt" - "os" - "strconv" - "strings" -) - -// go-specific code shared across loaders (5l, 6l, 8l). - -// replace all "". with pkg. -func expandpkg(t0 string, pkg string) string { - return strings.Replace(t0, `"".`, pkg+".", -1) -} - -// accumulate all type information from .6 files. -// check for inconsistencies. - -// TODO: -// generate debugging section in binary. -// once the dust settles, try to move some code to -// libmach, so that other linkers and ar can share. - -/* - * package import data - */ -type Import struct { - prefix string // "type", "var", "func", "const" - name string - def string - file string -} - -// importmap records type information about imported symbols to detect inconsistencies. -// Entries are keyed by qualified symbol name (e.g., "runtime.Callers" or "net/url.Error"). -var importmap = map[string]*Import{} - -func lookupImport(name string) *Import { - if x, ok := importmap[name]; ok { - return x - } - x := &Import{name: name} - importmap[name] = x - return x -} - -func ldpkg(f *obj.Biobuf, pkg string, length int64, filename string, whence int) { - var p0, p1 int - - if Debug['g'] != 0 { - return - } - - if int64(int(length)) != length { - fmt.Fprintf(os.Stderr, "%s: too much pkg data in %s\n", os.Args[0], filename) - if Debug['u'] != 0 { - errorexit() - } - return - } - - bdata := make([]byte, length) - if int64(obj.Bread(f, bdata)) != length { - fmt.Fprintf(os.Stderr, "%s: short pkg read %s\n", os.Args[0], filename) - if Debug['u'] != 0 { - errorexit() - } - return - } - data := string(bdata) - - // first \n$$ marks beginning of exports - skip rest of line - p0 = strings.Index(data, "\n$$") - if p0 < 0 { - if Debug['u'] != 0 && whence != ArchiveObj { - Exitf("cannot find export data in %s", filename) - } - return - } - - p0 += 3 - for p0 < len(data) && data[p0] != '\n' { - p0++ - } - - // second marks end of exports / beginning of local data - p1 = strings.Index(data[p0:], "\n$$") - if p1 < 0 { - fmt.Fprintf(os.Stderr, "%s: cannot find end of exports in %s\n", os.Args[0], filename) - if Debug['u'] != 0 { - errorexit() - } - return - } - p1 += p0 - - for p0 < p1 && (data[p0] == ' ' || data[p0] == '\t' || data[p0] == '\n') { - p0++ - } - if p0 < p1 { - if !strings.HasPrefix(data[p0:], "package ") { - fmt.Fprintf(os.Stderr, "%s: bad package section in %s - %.20s\n", os.Args[0], filename, data[p0:]) - if Debug['u'] != 0 { - errorexit() - } - return - } - - p0 += 8 - for p0 < p1 && (data[p0] == ' ' || data[p0] == '\t' || data[p0] == '\n') { - p0++ - } - pname := p0 - for p0 < p1 && data[p0] != ' ' && data[p0] != '\t' && data[p0] != '\n' { - p0++ - } - if Debug['u'] != 0 && whence != ArchiveObj && (p0+6 > p1 || !strings.HasPrefix(data[p0:], " safe\n")) { - Exitf("load of unsafe package %s", filename) - } - - name := data[pname:p0] - for p0 < p1 && data[p0] != '\n' { - p0++ - } - if p0 < p1 { - p0++ - } - - if pkg == "main" && name != "main" { - Exitf("%s: not package main (package %s)", filename, name) - } - - loadpkgdata(filename, pkg, data[p0:p1]) - } - - // __.PKGDEF has no cgo section - those are in the C compiler-generated object files. - if whence == Pkgdef { - return - } - - // look for cgo section - p0 = strings.Index(data[p1:], "\n$$ // cgo") - if p0 >= 0 { - p0 += p1 - i := strings.IndexByte(data[p0+1:], '\n') - if i < 0 { - fmt.Fprintf(os.Stderr, "%s: found $$ // cgo but no newline in %s\n", os.Args[0], filename) - if Debug['u'] != 0 { - errorexit() - } - return - } - p0 += 1 + i - - p1 = strings.Index(data[p0:], "\n$$") - if p1 < 0 { - p1 = strings.Index(data[p0:], "\n!\n") - } - if p1 < 0 { - fmt.Fprintf(os.Stderr, "%s: cannot find end of // cgo section in %s\n", os.Args[0], filename) - if Debug['u'] != 0 { - errorexit() - } - return - } - p1 += p0 - - loadcgo(filename, pkg, data[p0:p1]) - } -} - -func loadpkgdata(file string, pkg string, data string) { - var prefix string - var name string - var def string - - p := data - for parsepkgdata(file, pkg, &p, &prefix, &name, &def) > 0 { - x := lookupImport(name) - if x.prefix == "" { - x.prefix = prefix - x.def = def - x.file = file - } else if x.prefix != prefix { - fmt.Fprintf(os.Stderr, "%s: conflicting definitions for %s\n", os.Args[0], name) - fmt.Fprintf(os.Stderr, "%s:\t%s %s ...\n", x.file, x.prefix, name) - fmt.Fprintf(os.Stderr, "%s:\t%s %s ...\n", file, prefix, name) - nerrors++ - } else if x.def != def { - fmt.Fprintf(os.Stderr, "%s: conflicting definitions for %s\n", os.Args[0], name) - fmt.Fprintf(os.Stderr, "%s:\t%s %s %s\n", x.file, x.prefix, name, x.def) - fmt.Fprintf(os.Stderr, "%s:\t%s %s %s\n", file, prefix, name, def) - nerrors++ - } - } -} - -func parsepkgdata(file string, pkg string, pp *string, prefixp *string, namep *string, defp *string) int { - // skip white space - p := *pp - -loop: - for len(p) > 0 && (p[0] == ' ' || p[0] == '\t' || p[0] == '\n') { - p = p[1:] - } - if len(p) == 0 || strings.HasPrefix(p, "$$\n") { - return 0 - } - - // prefix: (var|type|func|const) - prefix := p - - if len(p) < 7 { - return -1 - } - if strings.HasPrefix(p, "var ") { - p = p[4:] - } else if strings.HasPrefix(p, "type ") { - p = p[5:] - } else if strings.HasPrefix(p, "func ") { - p = p[5:] - } else if strings.HasPrefix(p, "const ") { - p = p[6:] - } else if strings.HasPrefix(p, "import ") { - p = p[7:] - for len(p) > 0 && p[0] != ' ' { - p = p[1:] - } - p = p[1:] - line := p - for len(p) > 0 && p[0] != '\n' { - p = p[1:] - } - if len(p) == 0 { - fmt.Fprintf(os.Stderr, "%s: %s: confused in import line\n", os.Args[0], file) - nerrors++ - return -1 - } - line = line[:len(line)-len(p)] - line = strings.TrimSuffix(line, " // indirect") - path, err := strconv.Unquote(line) - if err != nil { - fmt.Fprintf(os.Stderr, "%s: %s: confused in import path: %q\n", os.Args[0], file, line) - nerrors++ - return -1 - } - p = p[1:] - imported(pkg, path) - goto loop - } else { - fmt.Fprintf(os.Stderr, "%s: %s: confused in pkg data near <<%.40s>>\n", os.Args[0], file, prefix) - nerrors++ - return -1 - } - - prefix = prefix[:len(prefix)-len(p)-1] - - // name: a.b followed by space - name := p - - inquote := false - for len(p) > 0 { - if p[0] == ' ' && !inquote { - break - } - - if p[0] == '\\' { - p = p[1:] - } else if p[0] == '"' { - inquote = !inquote - } - - p = p[1:] - } - - if len(p) == 0 { - return -1 - } - name = name[:len(name)-len(p)] - p = p[1:] - - // def: free form to new line - def := p - - for len(p) > 0 && p[0] != '\n' { - p = p[1:] - } - if len(p) == 0 { - return -1 - } - def = def[:len(def)-len(p)] - var defbuf *bytes.Buffer - p = p[1:] - - // include methods on successive lines in def of named type - var meth string - for parsemethod(&p, &meth) > 0 { - if defbuf == nil { - defbuf = new(bytes.Buffer) - defbuf.WriteString(def) - } - defbuf.WriteString("\n\t") - defbuf.WriteString(meth) - } - if defbuf != nil { - def = defbuf.String() - } - - name = expandpkg(name, pkg) - def = expandpkg(def, pkg) - - // done - *pp = p - - *prefixp = prefix - *namep = name - *defp = def - return 1 -} - -func parsemethod(pp *string, methp *string) int { - // skip white space - p := *pp - - for len(p) > 0 && (p[0] == ' ' || p[0] == '\t') { - p = p[1:] - } - if len(p) == 0 { - return 0 - } - - // might be a comment about the method - if strings.HasPrefix(p, "//") { - goto useline - } - - // if it says "func (", it's a method - if strings.HasPrefix(p, "func (") { - goto useline - } - return 0 - - // definition to end of line -useline: - *methp = p - - for len(p) > 0 && p[0] != '\n' { - p = p[1:] - } - if len(p) == 0 { - fmt.Fprintf(os.Stderr, "%s: lost end of line in method definition\n", os.Args[0]) - *pp = "" - return -1 - } - - *methp = (*methp)[:len(*methp)-len(p)] - *pp = p[1:] - return 1 -} - -func loadcgo(file string, pkg string, p string) { - var next string - var q string - var f []string - var local string - var remote string - var lib string - var s *LSym - - p0 := "" - for ; p != ""; p = next { - if i := strings.Index(p, "\n"); i >= 0 { - p, next = p[:i], p[i+1:] - } else { - next = "" - } - - p0 = p // save for error message - f = tokenize(p) - if len(f) == 0 { - continue - } - - if f[0] == "cgo_import_dynamic" { - if len(f) < 2 || len(f) > 4 { - goto err - } - - local = f[1] - remote = local - if len(f) > 2 { - remote = f[2] - } - lib = "" - if len(f) > 3 { - lib = f[3] - } - - if Debug['d'] != 0 { - fmt.Fprintf(os.Stderr, "%s: %s: cannot use dynamic imports with -d flag\n", os.Args[0], file) - nerrors++ - return - } - - if local == "_" && remote == "_" { - // allow #pragma dynimport _ _ "foo.so" - // to force a link of foo.so. - havedynamic = 1 - - if HEADTYPE == obj.Hdarwin { - Machoadddynlib(lib) - } else { - dynlib = append(dynlib, lib) - } - continue - } - - local = expandpkg(local, pkg) - q = "" - if i := strings.Index(remote, "#"); i >= 0 { - remote, q = remote[:i], remote[i+1:] - } - s = Linklookup(Ctxt, local, 0) - if local != f[1] { - } - if s.Type == 0 || s.Type == obj.SXREF || s.Type == obj.SHOSTOBJ { - s.Dynimplib = lib - s.Extname = remote - s.Dynimpvers = q - if s.Type != obj.SHOSTOBJ { - s.Type = obj.SDYNIMPORT - } - havedynamic = 1 - } - - continue - } - - if f[0] == "cgo_import_static" { - if len(f) != 2 { - goto err - } - local = f[1] - s = Linklookup(Ctxt, local, 0) - s.Type = obj.SHOSTOBJ - s.Size = 0 - continue - } - - if f[0] == "cgo_export_static" || f[0] == "cgo_export_dynamic" { - if len(f) < 2 || len(f) > 3 { - goto err - } - local = f[1] - if len(f) > 2 { - remote = f[2] - } else { - remote = local - } - local = expandpkg(local, pkg) - s = Linklookup(Ctxt, local, 0) - - switch Buildmode { - case BuildmodeCShared, BuildmodeCArchive: - if s == Linklookup(Ctxt, "main", 0) { - continue - } - } - - // export overrides import, for openbsd/cgo. - // see issue 4878. - if s.Dynimplib != "" { - s.Dynimplib = "" - s.Extname = "" - s.Dynimpvers = "" - s.Type = 0 - } - - if s.Cgoexport == 0 { - s.Extname = remote - dynexp = append(dynexp, s) - } else if s.Extname != remote { - fmt.Fprintf(os.Stderr, "%s: conflicting cgo_export directives: %s as %s and %s\n", os.Args[0], s.Name, s.Extname, remote) - nerrors++ - return - } - - if f[0] == "cgo_export_static" { - s.Cgoexport |= CgoExportStatic - } else { - s.Cgoexport |= CgoExportDynamic - } - if local != f[1] { - } - continue - } - - if f[0] == "cgo_dynamic_linker" { - if len(f) != 2 { - goto err - } - - if Debug['I'] == 0 { - if interpreter != "" && interpreter != f[1] { - fmt.Fprintf(os.Stderr, "%s: conflict dynlinker: %s and %s\n", os.Args[0], interpreter, f[1]) - nerrors++ - return - } - - interpreter = f[1] - } - - continue - } - - if f[0] == "cgo_ldflag" { - if len(f) != 2 { - goto err - } - ldflag = append(ldflag, f[1]) - continue - } - } - - return - -err: - fmt.Fprintf(os.Stderr, "%s: %s: invalid dynimport line: %s\n", os.Args[0], file, p0) - nerrors++ -} - -var seenlib = make(map[string]bool) - -func adddynlib(lib string) { - if seenlib[lib] || Linkmode == LinkExternal { - 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 { - Diag("adddynlib: unsupported binary format") - } -} - -func Adddynsym(ctxt *Link, s *LSym) { - if s.Dynid >= 0 || Linkmode == LinkExternal { - 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 - -func mark1(s *LSym, parent *LSym) { - if s == nil || s.Reachable { - return - } - if strings.HasPrefix(s.Name, "go.weak.") { - return - } - s.Reachable = true - s.Reachparent = parent - if markq == nil { - markq = s - } else { - emarkq.Queue = s - } - emarkq = s -} - -func mark(s *LSym) { - mark1(s, nil) -} - -func markflood() { - var a *Auto - var i int - - for s := markq; s != nil; s = s.Queue { - if s.Type == obj.STEXT { - if Debug['v'] > 1 { - fmt.Fprintf(&Bso, "marktext %s\n", s.Name) - } - for a = s.Autom; a != nil; a = a.Link { - mark1(a.Gotype, s) - } - } - - for i = 0; i < len(s.R); i++ { - mark1(s.R[i].Sym, s) - } - if s.Pcln != nil { - for i = 0; i < s.Pcln.Nfuncdata; i++ { - mark1(s.Pcln.Funcdata[i], s) - } - } - - mark1(s.Gotype, s) - mark1(s.Sub, s) - mark1(s.Outer, s) - } -} - -var markextra = []string{ - "runtime.morestack", - "runtime.morestackx", - "runtime.morestack00", - "runtime.morestack10", - "runtime.morestack01", - "runtime.morestack11", - "runtime.morestack8", - "runtime.morestack16", - "runtime.morestack24", - "runtime.morestack32", - "runtime.morestack40", - "runtime.morestack48", - // on arm, lock in the div/mod helpers too - "_div", - "_divu", - "_mod", - "_modu", -} - -func deadcode() { - if Debug['v'] != 0 { - fmt.Fprintf(&Bso, "%5.2f deadcode\n", obj.Cputime()) - } - - if Buildmode == BuildmodeShared { - // Mark all symbols as reachable when building a - // shared library. - for s := Ctxt.Allsym; s != nil; s = s.Allsym { - if s.Type != 0 { - mark(s) - } - } - mark(Linkrlookup(Ctxt, "main.main", 0)) - mark(Linkrlookup(Ctxt, "main.init", 0)) - } else { - mark(Linklookup(Ctxt, INITENTRY, 0)) - if Linkshared && Buildmode == BuildmodeExe { - mark(Linkrlookup(Ctxt, "main.main", 0)) - mark(Linkrlookup(Ctxt, "main.init", 0)) - } - for i := 0; i < len(markextra); i++ { - mark(Linklookup(Ctxt, markextra[i], 0)) - } - - for i := 0; i < len(dynexp); i++ { - mark(dynexp[i]) - } - markflood() - - // keep each beginning with 'typelink.' if the symbol it points at is being kept. - for s := Ctxt.Allsym; s != nil; s = s.Allsym { - if strings.HasPrefix(s.Name, "go.typelink.") { - s.Reachable = len(s.R) == 1 && s.R[0].Sym.Reachable - } - } - - // remove dead text but keep file information (z symbols). - var last *LSym - - for s := Ctxt.Textp; s != nil; s = s.Next { - if !s.Reachable { - continue - } - - // NOTE: Removing s from old textp and adding to new, shorter textp. - if last == nil { - Ctxt.Textp = s - } else { - last.Next = s - } - last = s - } - - if last == nil { - Ctxt.Textp = nil - Ctxt.Etextp = nil - } else { - last.Next = nil - Ctxt.Etextp = last - } - } - - for s := Ctxt.Allsym; s != nil; s = s.Allsym { - if strings.HasPrefix(s.Name, "go.weak.") { - s.Special = 1 // do not lay out in data segment - s.Reachable = true - s.Hide = 1 - } - } - - // record field tracking references - var buf bytes.Buffer - var p *LSym - for s := Ctxt.Allsym; s != nil; s = s.Allsym { - if strings.HasPrefix(s.Name, "go.track.") { - s.Special = 1 // do not lay out in data segment - s.Hide = 1 - if s.Reachable { - buf.WriteString(s.Name[9:]) - for p = s.Reachparent; p != nil; p = p.Reachparent { - buf.WriteString("\t") - buf.WriteString(p.Name) - } - buf.WriteString("\n") - } - - s.Type = obj.SCONST - s.Value = 0 - } - } - - if tracksym == "" { - return - } - s := Linklookup(Ctxt, tracksym, 0) - if !s.Reachable { - return - } - addstrdata(tracksym, buf.String()) -} - -func doweak() { - var t *LSym - - // resolve weak references only if - // target symbol will be in binary anyway. - for s := Ctxt.Allsym; s != nil; s = s.Allsym { - if strings.HasPrefix(s.Name, "go.weak.") { - t = Linkrlookup(Ctxt, s.Name[8:], int(s.Version)) - if t != nil && t.Type != 0 && t.Reachable { - s.Value = t.Value - s.Type = t.Type - s.Outer = t - } else { - s.Type = obj.SCONST - s.Value = 0 - } - - continue - } - } -} - -func addexport() { - if HEADTYPE == obj.Hdarwin { - return - } - - for _, exp := range dynexp { - Adddynsym(Ctxt, exp) - } - for _, lib := range dynlib { - adddynlib(lib) - } -} - -type Pkg struct { - mark bool - checked bool - path string - impby []*Pkg -} - -var ( - // pkgmap records the imported-by relationship between packages. - // Entries are keyed by package path (e.g., "runtime" or "net/url"). - pkgmap = map[string]*Pkg{} - - pkgall []*Pkg -) - -func lookupPkg(path string) *Pkg { - if p, ok := pkgmap[path]; ok { - return p - } - p := &Pkg{path: path} - pkgmap[path] = p - pkgall = append(pkgall, p) - return p -} - -// imported records that package pkg imports package imp. -func imported(pkg, imp string) { - // everyone imports runtime, even runtime. - if imp == "runtime" { - return - } - - p := lookupPkg(pkg) - i := lookupPkg(imp) - i.impby = append(i.impby, p) -} - -func (p *Pkg) cycle() *Pkg { - if p.checked { - return nil - } - - if p.mark { - nerrors++ - fmt.Printf("import cycle:\n") - fmt.Printf("\t%s\n", p.path) - return p - } - - p.mark = true - for _, q := range p.impby { - if bad := q.cycle(); bad != nil { - p.mark = false - p.checked = true - fmt.Printf("\timports %s\n", p.path) - if bad == p { - return nil - } - return bad - } - } - - p.checked = true - p.mark = false - return nil -} - -func importcycles() { - for _, p := range pkgall { - p.cycle() - } -} - -func setlinkmode(arg string) { - if arg == "internal" { - Linkmode = LinkInternal - } else if arg == "external" { - Linkmode = LinkExternal - } else if arg == "auto" { - Linkmode = LinkAuto - } else { - Exitf("unknown link mode -linkmode %s", arg) - } -} diff --git a/src/cmd/internal/ld/ld.go b/src/cmd/internal/ld/ld.go deleted file mode 100644 index 1068bdd767..0000000000 --- a/src/cmd/internal/ld/ld.go +++ /dev/null @@ -1,130 +0,0 @@ -// Derived from Inferno utils/6l/obj.c and utils/6l/span.c -// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c -// http://code.google.com/p/inferno-os/source/browse/utils/6l/span.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package ld - -import ( - "cmd/internal/obj" - "fmt" - "io/ioutil" - "os" - "path" - "strconv" - "strings" -) - -func addlib(ctxt *Link, src string, obj string, pathname string) { - name := path.Clean(pathname) - - // runtime.a -> runtime, runtime.6 -> runtime - pkg := name - if len(pkg) >= 2 && pkg[len(pkg)-2] == '.' { - pkg = pkg[:len(pkg)-2] - } - - // already loaded? - for i := 0; i < len(ctxt.Library); i++ { - if ctxt.Library[i].Pkg == pkg { - return - } - } - - var pname string - isshlib := false - if (ctxt.Windows == 0 && strings.HasPrefix(name, "/")) || (ctxt.Windows != 0 && len(name) >= 2 && name[1] == ':') { - pname = name - } else { - // try dot, -L "libdir", and then goroot. - for _, dir := range ctxt.Libdir { - if Linkshared { - pname = dir + "/" + pkg + ".shlibname" - if _, err := os.Stat(pname); err == nil { - isshlib = true - break - } - } - pname = dir + "/" + name - if _, err := os.Stat(pname); err == nil { - break - } - } - } - - pname = path.Clean(pname) - - if ctxt.Debugvlog > 1 && ctxt.Bso != nil { - fmt.Fprintf(ctxt.Bso, "%5.2f addlib: %s %s pulls in %s isshlib %v\n", elapsed(), obj, src, pname, isshlib) - } - - if isshlib { - addlibpath(ctxt, src, obj, "", pkg, pname) - } else { - addlibpath(ctxt, src, obj, pname, pkg, "") - } -} - -/* - * add library to library list. - * srcref: src file referring to package - * objref: object file referring to package - * file: object file, e.g., /home/rsc/go/pkg/container/vector.a - * pkg: package import path, e.g. container/vector - */ -func addlibpath(ctxt *Link, srcref string, objref string, file string, pkg string, shlibnamefile string) { - for i := 0; i < len(ctxt.Library); i++ { - if pkg == ctxt.Library[i].Pkg { - return - } - } - - if ctxt.Debugvlog > 1 && ctxt.Bso != nil { - 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] - l.Objref = objref - l.Srcref = srcref - l.File = file - l.Pkg = pkg - if shlibnamefile != "" { - shlibbytes, err := ioutil.ReadFile(shlibnamefile) - if err != nil { - Diag("cannot read %s: %v", shlibnamefile, err) - } - l.Shlib = strings.TrimSpace(string(shlibbytes)) - } -} - -func atolwhex(s string) int64 { - n, _ := strconv.ParseInt(s, 0, 64) - return n -} diff --git a/src/cmd/internal/ld/ldelf.go b/src/cmd/internal/ld/ldelf.go deleted file mode 100644 index 3efdb75b89..0000000000 --- a/src/cmd/internal/ld/ldelf.go +++ /dev/null @@ -1,1019 +0,0 @@ -package ld - -import ( - "bytes" - "cmd/internal/obj" - "encoding/binary" - "fmt" - "log" - "sort" - "strings" -) - -/* -Derived from Plan 9 from User Space's src/libmach/elf.h, elf.c -http://code.swtch.com/plan9port/src/tip/src/libmach/ - - Copyright © 2004 Russ Cox. - Portions Copyright © 2008-2010 Google Inc. - Portions Copyright © 2010 The Go Authors. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ -const ( - ElfClassNone = 0 - ElfClass32 = 1 - ElfClass64 = 2 -) - -const ( - ElfDataNone = 0 - ElfDataLsb = 1 - ElfDataMsb = 2 -) - -const ( - ElfTypeNone = 0 - ElfTypeRelocatable = 1 - ElfTypeExecutable = 2 - ElfTypeSharedObject = 3 - ElfTypeCore = 4 -) - -const ( - ElfMachNone = 0 - ElfMach32100 = 1 - ElfMachSparc = 2 - ElfMach386 = 3 - ElfMach68000 = 4 - ElfMach88000 = 5 - ElfMach486 = 6 - ElfMach860 = 7 - ElfMachMips = 8 - ElfMachS370 = 9 - ElfMachMipsLe = 10 - ElfMachParisc = 15 - ElfMachVpp500 = 17 - ElfMachSparc32Plus = 18 - ElfMach960 = 19 - ElfMachPower = 20 - ElfMachPower64 = 21 - ElfMachS390 = 22 - ElfMachV800 = 36 - ElfMachFr20 = 37 - ElfMachRh32 = 38 - ElfMachRce = 39 - ElfMachArm = 40 - ElfMachAlpha = 41 - ElfMachSH = 42 - ElfMachSparc9 = 43 - ElfMachAmd64 = 62 - ElfMachArm64 = 183 -) - -const ( - ElfAbiNone = 0 - ElfAbiSystemV = 0 - ElfAbiHPUX = 1 - ElfAbiNetBSD = 2 - ElfAbiLinux = 3 - ElfAbiSolaris = 6 - ElfAbiAix = 7 - ElfAbiIrix = 8 - ElfAbiFreeBSD = 9 - ElfAbiTru64 = 10 - ElfAbiModesto = 11 - ElfAbiOpenBSD = 12 - ElfAbiARM = 97 - ElfAbiEmbedded = 255 -) - -const ( - ElfSectNone = 0 - ElfSectProgbits = 1 - ElfSectSymtab = 2 - ElfSectStrtab = 3 - ElfSectRela = 4 - ElfSectHash = 5 - ElfSectDynamic = 6 - ElfSectNote = 7 - ElfSectNobits = 8 - ElfSectRel = 9 - ElfSectShlib = 10 - ElfSectDynsym = 11 - ElfSectFlagWrite = 0x1 - ElfSectFlagAlloc = 0x2 - ElfSectFlagExec = 0x4 -) - -const ( - ElfSymBindLocal = 0 - ElfSymBindGlobal = 1 - ElfSymBindWeak = 2 -) - -const ( - ElfSymTypeNone = 0 - ElfSymTypeObject = 1 - ElfSymTypeFunc = 2 - ElfSymTypeSection = 3 - ElfSymTypeFile = 4 -) - -const ( - ElfSymShnNone = 0 - ElfSymShnAbs = 0xFFF1 - ElfSymShnCommon = 0xFFF2 -) - -const ( - ElfProgNone = 0 - ElfProgLoad = 1 - ElfProgDynamic = 2 - ElfProgInterp = 3 - ElfProgNote = 4 - ElfProgShlib = 5 - ElfProgPhdr = 6 - ElfProgFlagExec = 0x1 - ElfProgFlagWrite = 0x2 - ElfProgFlagRead = 0x4 -) - -const ( - ElfNotePrStatus = 1 - ElfNotePrFpreg = 2 - ElfNotePrPsinfo = 3 - ElfNotePrTaskstruct = 4 - ElfNotePrAuxv = 6 - ElfNotePrXfpreg = 0x46e62b7f -) - -type ElfHdrBytes struct { - Ident [16]uint8 - Type [2]uint8 - Machine [2]uint8 - Version [4]uint8 - Entry [4]uint8 - Phoff [4]uint8 - Shoff [4]uint8 - Flags [4]uint8 - Ehsize [2]uint8 - Phentsize [2]uint8 - Phnum [2]uint8 - Shentsize [2]uint8 - Shnum [2]uint8 - Shstrndx [2]uint8 -} - -type ElfSectBytes struct { - Name [4]uint8 - Type [4]uint8 - Flags [4]uint8 - Addr [4]uint8 - Off [4]uint8 - Size [4]uint8 - Link [4]uint8 - Info [4]uint8 - Align [4]uint8 - Entsize [4]uint8 -} - -type ElfProgBytes struct { -} - -type ElfSymBytes struct { - Name [4]uint8 - Value [4]uint8 - Size [4]uint8 - Info uint8 - Other uint8 - Shndx [2]uint8 -} - -type ElfHdrBytes64 struct { - Ident [16]uint8 - Type [2]uint8 - Machine [2]uint8 - Version [4]uint8 - Entry [8]uint8 - Phoff [8]uint8 - Shoff [8]uint8 - Flags [4]uint8 - Ehsize [2]uint8 - Phentsize [2]uint8 - Phnum [2]uint8 - Shentsize [2]uint8 - Shnum [2]uint8 - Shstrndx [2]uint8 -} - -type ElfSectBytes64 struct { - Name [4]uint8 - Type [4]uint8 - Flags [8]uint8 - Addr [8]uint8 - Off [8]uint8 - Size [8]uint8 - Link [4]uint8 - Info [4]uint8 - Align [8]uint8 - Entsize [8]uint8 -} - -type ElfProgBytes64 struct { -} - -type ElfSymBytes64 struct { - Name [4]uint8 - Info uint8 - Other uint8 - Shndx [2]uint8 - Value [8]uint8 - Size [8]uint8 -} - -type ElfSect struct { - name string - nameoff uint32 - type_ uint32 - flags uint64 - addr uint64 - off uint64 - size uint64 - link uint32 - info uint32 - align uint64 - entsize uint64 - base []byte - sym *LSym -} - -type ElfObj struct { - f *obj.Biobuf - base int64 // offset in f where ELF begins - length int64 // length of ELF - is64 int - name string - e binary.ByteOrder - sect []ElfSect - nsect uint - shstrtab string - nsymtab int - symtab *ElfSect - symstr *ElfSect - type_ uint32 - machine uint32 - version uint32 - entry uint64 - phoff uint64 - shoff uint64 - flags uint32 - ehsize uint32 - phentsize uint32 - phnum uint32 - shentsize uint32 - shnum uint32 - shstrndx uint32 -} - -type ElfSym struct { - name string - value uint64 - size uint64 - bind uint8 - type_ uint8 - other uint8 - shndx uint16 - sym *LSym -} - -var ElfMagic = [4]uint8{0x7F, 'E', 'L', 'F'} - -func valuecmp(a *LSym, b *LSym) int { - if a.Value < b.Value { - return -1 - } - if a.Value > b.Value { - return +1 - } - return 0 -} - -func ldelf(f *obj.Biobuf, pkg string, length int64, pn string) { - if Debug['v'] != 0 { - fmt.Fprintf(&Bso, "%5.2f ldelf %s\n", obj.Cputime(), pn) - } - - Ctxt.Version++ - base := int32(obj.Boffset(f)) - - var add uint64 - var e binary.ByteOrder - var elfobj *ElfObj - var err error - var flag int - var hdr *ElfHdrBytes - var hdrbuf [64]uint8 - var info uint64 - var is64 int - var j int - var n int - var name string - var p []byte - var r []Reloc - var rela int - var rp *Reloc - var rsect *ElfSect - var s *LSym - var sect *ElfSect - var sym ElfSym - var symbols []*LSym - if obj.Bread(f, hdrbuf[:]) != len(hdrbuf) { - goto bad - } - hdr = new(ElfHdrBytes) - binary.Read(bytes.NewReader(hdrbuf[:]), binary.BigEndian, hdr) // only byte arrays; byte order doesn't matter - if string(hdr.Ident[:4]) != "\x7FELF" { - goto bad - } - switch hdr.Ident[5] { - case ElfDataLsb: - e = binary.LittleEndian - - case ElfDataMsb: - e = binary.BigEndian - - default: - goto bad - } - - // read header - elfobj = new(ElfObj) - - elfobj.e = e - elfobj.f = f - elfobj.base = int64(base) - elfobj.length = length - elfobj.name = pn - - is64 = 0 - if hdr.Ident[4] == ElfClass64 { - is64 = 1 - hdr := new(ElfHdrBytes64) - binary.Read(bytes.NewReader(hdrbuf[:]), binary.BigEndian, hdr) // only byte arrays; byte order doesn't matter - elfobj.type_ = uint32(e.Uint16(hdr.Type[:])) - elfobj.machine = uint32(e.Uint16(hdr.Machine[:])) - elfobj.version = e.Uint32(hdr.Version[:]) - elfobj.phoff = e.Uint64(hdr.Phoff[:]) - elfobj.shoff = e.Uint64(hdr.Shoff[:]) - elfobj.flags = e.Uint32(hdr.Flags[:]) - elfobj.ehsize = uint32(e.Uint16(hdr.Ehsize[:])) - elfobj.phentsize = uint32(e.Uint16(hdr.Phentsize[:])) - elfobj.phnum = uint32(e.Uint16(hdr.Phnum[:])) - elfobj.shentsize = uint32(e.Uint16(hdr.Shentsize[:])) - elfobj.shnum = uint32(e.Uint16(hdr.Shnum[:])) - elfobj.shstrndx = uint32(e.Uint16(hdr.Shstrndx[:])) - } else { - elfobj.type_ = uint32(e.Uint16(hdr.Type[:])) - elfobj.machine = uint32(e.Uint16(hdr.Machine[:])) - elfobj.version = e.Uint32(hdr.Version[:]) - elfobj.entry = uint64(e.Uint32(hdr.Entry[:])) - elfobj.phoff = uint64(e.Uint32(hdr.Phoff[:])) - elfobj.shoff = uint64(e.Uint32(hdr.Shoff[:])) - elfobj.flags = e.Uint32(hdr.Flags[:]) - elfobj.ehsize = uint32(e.Uint16(hdr.Ehsize[:])) - elfobj.phentsize = uint32(e.Uint16(hdr.Phentsize[:])) - elfobj.phnum = uint32(e.Uint16(hdr.Phnum[:])) - elfobj.shentsize = uint32(e.Uint16(hdr.Shentsize[:])) - elfobj.shnum = uint32(e.Uint16(hdr.Shnum[:])) - elfobj.shstrndx = uint32(e.Uint16(hdr.Shstrndx[:])) - } - - elfobj.is64 = is64 - - if uint32(hdr.Ident[6]) != elfobj.version { - goto bad - } - - if e.Uint16(hdr.Type[:]) != ElfTypeRelocatable { - Diag("%s: elf but not elf relocatable object", pn) - return - } - - switch Thearch.Thechar { - default: - Diag("%s: elf %s unimplemented", pn, Thestring) - return - - case '5': - if e != binary.LittleEndian || elfobj.machine != ElfMachArm || hdr.Ident[4] != ElfClass32 { - Diag("%s: elf object but not arm", pn) - return - } - - case '6': - if e != binary.LittleEndian || elfobj.machine != ElfMachAmd64 || hdr.Ident[4] != ElfClass64 { - Diag("%s: elf object but not amd64", pn) - return - } - - case '7': - if e != binary.LittleEndian || elfobj.machine != ElfMachArm64 || hdr.Ident[4] != ElfClass64 { - Diag("%s: elf object but not arm64", pn) - return - } - - case '8': - if e != binary.LittleEndian || elfobj.machine != ElfMach386 || hdr.Ident[4] != ElfClass32 { - Diag("%s: elf object but not 386", pn) - return - } - - case '9': - if elfobj.machine != ElfMachPower64 || hdr.Ident[4] != ElfClass64 { - Diag("%s: elf object but not ppc64", pn) - return - } - } - - // load section list into memory. - elfobj.sect = make([]ElfSect, elfobj.shnum) - - elfobj.nsect = uint(elfobj.shnum) - for i := 0; uint(i) < elfobj.nsect; i++ { - if obj.Bseek(f, int64(uint64(base)+elfobj.shoff+uint64(int64(i)*int64(elfobj.shentsize))), 0) < 0 { - goto bad - } - sect = &elfobj.sect[i] - if is64 != 0 { - var b ElfSectBytes64 - - if err = binary.Read(f, e, &b); err != nil { - goto bad - } - - sect.nameoff = uint32(e.Uint32(b.Name[:])) - sect.type_ = e.Uint32(b.Type[:]) - sect.flags = e.Uint64(b.Flags[:]) - sect.addr = e.Uint64(b.Addr[:]) - sect.off = e.Uint64(b.Off[:]) - sect.size = e.Uint64(b.Size[:]) - sect.link = e.Uint32(b.Link[:]) - sect.info = e.Uint32(b.Info[:]) - sect.align = e.Uint64(b.Align[:]) - sect.entsize = e.Uint64(b.Entsize[:]) - } else { - var b ElfSectBytes - - if err = binary.Read(f, e, &b); err != nil { - goto bad - } - - sect.nameoff = uint32(e.Uint32(b.Name[:])) - sect.type_ = e.Uint32(b.Type[:]) - sect.flags = uint64(e.Uint32(b.Flags[:])) - sect.addr = uint64(e.Uint32(b.Addr[:])) - sect.off = uint64(e.Uint32(b.Off[:])) - sect.size = uint64(e.Uint32(b.Size[:])) - sect.link = e.Uint32(b.Link[:]) - sect.info = e.Uint32(b.Info[:]) - sect.align = uint64(e.Uint32(b.Align[:])) - sect.entsize = uint64(e.Uint32(b.Entsize[:])) - } - } - - // read section string table and translate names - if elfobj.shstrndx >= uint32(elfobj.nsect) { - err = fmt.Errorf("shstrndx out of range %d >= %d", elfobj.shstrndx, elfobj.nsect) - goto bad - } - - sect = &elfobj.sect[elfobj.shstrndx] - if err = elfmap(elfobj, sect); err != nil { - goto bad - } - for i := 0; uint(i) < elfobj.nsect; i++ { - if elfobj.sect[i].nameoff != 0 { - elfobj.sect[i].name = cstring(sect.base[elfobj.sect[i].nameoff:]) - } - } - - // load string table for symbols into memory. - elfobj.symtab = section(elfobj, ".symtab") - - if elfobj.symtab == nil { - // our work is done here - no symbols means nothing can refer to this file - return - } - - if elfobj.symtab.link <= 0 || elfobj.symtab.link >= uint32(elfobj.nsect) { - Diag("%s: elf object has symbol table with invalid string table link", pn) - return - } - - elfobj.symstr = &elfobj.sect[elfobj.symtab.link] - if is64 != 0 { - elfobj.nsymtab = int(elfobj.symtab.size / ELF64SYMSIZE) - } else { - elfobj.nsymtab = int(elfobj.symtab.size / ELF32SYMSIZE) - } - - if err = elfmap(elfobj, elfobj.symtab); err != nil { - goto bad - } - if err = elfmap(elfobj, elfobj.symstr); err != nil { - goto bad - } - - // load text and data segments into memory. - // they are not as small as the section lists, but we'll need - // the memory anyway for the symbol images, so we might - // as well use one large chunk. - - // create symbols for elfmapped sections - for i := 0; uint(i) < elfobj.nsect; i++ { - sect = &elfobj.sect[i] - if (sect.type_ != ElfSectProgbits && sect.type_ != ElfSectNobits) || sect.flags&ElfSectFlagAlloc == 0 { - continue - } - if sect.type_ != ElfSectNobits { - if err = elfmap(elfobj, sect); err != nil { - goto bad - } - } - - name = fmt.Sprintf("%s(%s)", pkg, sect.name) - s = Linklookup(Ctxt, name, Ctxt.Version) - - switch int(sect.flags) & (ElfSectFlagAlloc | ElfSectFlagWrite | ElfSectFlagExec) { - default: - err = fmt.Errorf("unexpected flags for ELF section %s", sect.name) - goto bad - - case ElfSectFlagAlloc: - s.Type = obj.SRODATA - - case ElfSectFlagAlloc + ElfSectFlagWrite: - if sect.type_ == ElfSectNobits { - s.Type = obj.SNOPTRBSS - } else { - s.Type = obj.SNOPTRDATA - } - - case ElfSectFlagAlloc + ElfSectFlagExec: - s.Type = obj.STEXT - } - - if sect.name == ".got" || sect.name == ".toc" { - s.Type = obj.SELFGOT - } - if sect.type_ == ElfSectProgbits { - s.P = sect.base - s.P = s.P[:sect.size] - } - - s.Size = int64(sect.size) - s.Align = int32(sect.align) - sect.sym = s - } - - // enter sub-symbols into symbol table. - // symbol 0 is the null symbol. - symbols = make([]*LSym, elfobj.nsymtab) - - for i := 1; i < elfobj.nsymtab; i++ { - if err = readelfsym(elfobj, i, &sym, 1); err != nil { - goto bad - } - symbols[i] = sym.sym - if sym.type_ != ElfSymTypeFunc && sym.type_ != ElfSymTypeObject && sym.type_ != ElfSymTypeNone { - continue - } - if sym.shndx == ElfSymShnCommon { - s = sym.sym - if uint64(s.Size) < sym.size { - s.Size = int64(sym.size) - } - if s.Type == 0 || s.Type == obj.SXREF { - s.Type = obj.SNOPTRBSS - } - continue - } - - if uint(sym.shndx) >= elfobj.nsect || sym.shndx == 0 { - continue - } - - // even when we pass needSym == 1 to readelfsym, it might still return nil to skip some unwanted symbols - if sym.sym == nil { - continue - } - sect = &elfobj.sect[sym.shndx:][0] - if sect.sym == nil { - if strings.HasPrefix(sym.name, ".Linfo_string") { // clang does this - continue - } - Diag("%s: sym#%d: ignoring %s in section %d (type %d)", pn, i, sym.name, sym.shndx, sym.type_) - continue - } - - s = sym.sym - if s.Outer != nil { - if s.Dupok != 0 { - continue - } - Exitf("%s: duplicate symbol reference: %s in both %s and %s", pn, s.Name, s.Outer.Name, sect.sym.Name) - } - - s.Sub = sect.sym.Sub - sect.sym.Sub = s - s.Type = sect.sym.Type | s.Type&^obj.SMASK | obj.SSUB - if s.Cgoexport&CgoExportDynamic == 0 { - s.Dynimplib = "" // satisfy dynimport - } - s.Value = int64(sym.value) - s.Size = int64(sym.size) - s.Outer = sect.sym - if sect.sym.Type == obj.STEXT { - if s.External != 0 && s.Dupok == 0 { - Diag("%s: duplicate definition of %s", pn, s.Name) - } - s.External = 1 - } - - if elfobj.machine == ElfMachPower64 { - flag = int(sym.other) >> 5 - if 2 <= flag && flag <= 6 { - s.Localentry = 1 << uint(flag-2) - } else if flag == 7 { - Diag("%s: invalid sym.other 0x%x for %s", pn, sym.other, s.Name) - } - } - } - - // Sort outer lists by address, adding to textp. - // This keeps textp in increasing address order. - for i := 0; uint(i) < elfobj.nsect; i++ { - s = elfobj.sect[i].sym - if s == nil { - continue - } - if s.Sub != nil { - s.Sub = listsort(s.Sub, valuecmp, listsubp) - } - if s.Type == obj.STEXT { - if s.Onlist != 0 { - log.Fatalf("symbol %s listed multiple times", s.Name) - } - s.Onlist = 1 - if Ctxt.Etextp != nil { - Ctxt.Etextp.Next = s - } else { - Ctxt.Textp = s - } - Ctxt.Etextp = s - for s = s.Sub; s != nil; s = s.Sub { - if s.Onlist != 0 { - log.Fatalf("symbol %s listed multiple times", s.Name) - } - s.Onlist = 1 - Ctxt.Etextp.Next = s - Ctxt.Etextp = s - } - } - } - - // load relocations - for i := 0; uint(i) < elfobj.nsect; i++ { - rsect = &elfobj.sect[i] - if rsect.type_ != ElfSectRela && rsect.type_ != ElfSectRel { - continue - } - if rsect.info >= uint32(elfobj.nsect) || elfobj.sect[rsect.info].base == nil { - continue - } - sect = &elfobj.sect[rsect.info] - if err = elfmap(elfobj, rsect); err != nil { - goto bad - } - rela = 0 - if rsect.type_ == ElfSectRela { - rela = 1 - } - n = int(rsect.size / uint64(4+4*is64) / uint64(2+rela)) - r = make([]Reloc, n) - p = rsect.base - for j = 0; j < n; j++ { - add = 0 - rp = &r[j] - if is64 != 0 { - // 64-bit rel/rela - rp.Off = int32(e.Uint64(p)) - - p = p[8:] - info = e.Uint64(p) - p = p[8:] - if rela != 0 { - add = e.Uint64(p) - p = p[8:] - } - } else { - // 32-bit rel/rela - rp.Off = int32(e.Uint32(p)) - - p = p[4:] - info = uint64(e.Uint32(p)) - info = info>>8<<32 | info&0xff // convert to 64-bit info - p = p[4:] - if rela != 0 { - add = uint64(e.Uint32(p)) - p = p[4:] - } - } - - if info&0xffffffff == 0 { // skip R_*_NONE relocation - j-- - n-- - continue - } - - if info>>32 == 0 { // absolute relocation, don't bother reading the null symbol - rp.Sym = nil - } else { - if err = readelfsym(elfobj, int(info>>32), &sym, 0); err != nil { - goto bad - } - sym.sym = symbols[info>>32] - if sym.sym == nil { - err = fmt.Errorf("%s#%d: reloc of invalid sym #%d %s shndx=%d type=%d", sect.sym.Name, j, int(info>>32), sym.name, sym.shndx, sym.type_) - goto bad - } - - rp.Sym = sym.sym - } - - rp.Type = int32(reltype(pn, int(uint32(info)), &rp.Siz)) - if rela != 0 { - rp.Add = int64(add) - } else { - // load addend from image - if rp.Siz == 4 { - rp.Add = int64(e.Uint32(sect.base[rp.Off:])) - } else if rp.Siz == 8 { - rp.Add = int64(e.Uint64(sect.base[rp.Off:])) - } else { - Diag("invalid rela size %d", rp.Siz) - } - } - - if rp.Siz == 2 { - rp.Add = int64(int16(rp.Add)) - } - if rp.Siz == 4 { - rp.Add = int64(int32(rp.Add)) - } - } - - //print("rel %s %d %d %s %#llx\n", sect->sym->name, rp->type, rp->siz, rp->sym->name, rp->add); - sort.Sort(rbyoff(r[:n])) - // just in case - - s = sect.sym - s.R = r - s.R = s.R[:n] - } - - return - -bad: - Diag("%s: malformed elf file: %v", pn, err) -} - -func section(elfobj *ElfObj, name string) *ElfSect { - for i := 0; uint(i) < elfobj.nsect; i++ { - if elfobj.sect[i].name != "" && name != "" && elfobj.sect[i].name == name { - return &elfobj.sect[i] - } - } - return nil -} - -func elfmap(elfobj *ElfObj, sect *ElfSect) (err error) { - if sect.base != nil { - return nil - } - - if sect.off+sect.size > uint64(elfobj.length) { - err = fmt.Errorf("elf section past end of file") - return err - } - - sect.base = make([]byte, sect.size) - err = fmt.Errorf("short read") - if obj.Bseek(elfobj.f, int64(uint64(elfobj.base)+sect.off), 0) < 0 || obj.Bread(elfobj.f, sect.base) != len(sect.base) { - return err - } - - return nil -} - -func readelfsym(elfobj *ElfObj, i int, sym *ElfSym, needSym int) (err error) { - if i >= elfobj.nsymtab || i < 0 { - err = fmt.Errorf("invalid elf symbol index") - return err - } - - if i == 0 { - Diag("readym: read null symbol!") - } - - if elfobj.is64 != 0 { - b := new(ElfSymBytes64) - binary.Read(bytes.NewReader(elfobj.symtab.base[i*ELF64SYMSIZE:(i+1)*ELF64SYMSIZE]), elfobj.e, b) - sym.name = cstring(elfobj.symstr.base[elfobj.e.Uint32(b.Name[:]):]) - sym.value = elfobj.e.Uint64(b.Value[:]) - sym.size = elfobj.e.Uint64(b.Size[:]) - sym.shndx = elfobj.e.Uint16(b.Shndx[:]) - sym.bind = b.Info >> 4 - sym.type_ = b.Info & 0xf - sym.other = b.Other - } else { - b := new(ElfSymBytes) - binary.Read(bytes.NewReader(elfobj.symtab.base[i*ELF32SYMSIZE:(i+1)*ELF32SYMSIZE]), elfobj.e, b) - sym.name = cstring(elfobj.symstr.base[elfobj.e.Uint32(b.Name[:]):]) - sym.value = uint64(elfobj.e.Uint32(b.Value[:])) - sym.size = uint64(elfobj.e.Uint32(b.Size[:])) - sym.shndx = elfobj.e.Uint16(b.Shndx[:]) - sym.bind = b.Info >> 4 - sym.type_ = b.Info & 0xf - sym.other = b.Other - } - - var s *LSym - if sym.name == "_GLOBAL_OFFSET_TABLE_" { - sym.name = ".got" - } - if sym.name == ".TOC." { - // Magic symbol on ppc64. Will be set to this object - // file's .got+0x8000. - sym.bind = ElfSymBindLocal - } - - switch sym.type_ { - case ElfSymTypeSection: - s = elfobj.sect[sym.shndx].sym - - case ElfSymTypeObject, ElfSymTypeFunc, ElfSymTypeNone: - switch sym.bind { - case ElfSymBindGlobal: - if needSym != 0 { - s = Linklookup(Ctxt, sym.name, 0) - - // for global scoped hidden symbols we should insert it into - // symbol hash table, but mark them as hidden. - // __i686.get_pc_thunk.bx is allowed to be duplicated, to - // workaround that we set dupok. - // TODO(minux): correctly handle __i686.get_pc_thunk.bx without - // set dupok generally. See http://codereview.appspot.com/5823055/ - // comment #5 for details. - if s != nil && sym.other == 2 { - s.Type |= obj.SHIDDEN - s.Dupok = 1 - } - } - - case ElfSymBindLocal: - if Thearch.Thechar == '5' && (strings.HasPrefix(sym.name, "$a") || strings.HasPrefix(sym.name, "$d")) { - // binutils for arm generate these mapping - // symbols, ignore these - break - } - - if sym.name == ".TOC." { - // We need to be able to look this up, - // so put it in the hash table. - if needSym != 0 { - s = Linklookup(Ctxt, sym.name, Ctxt.Version) - s.Type |= obj.SHIDDEN - } - - break - } - - if needSym != 0 { - // local names and hidden visiblity global names are unique - // and should only reference by its index, not name, so we - // don't bother to add them into hash table - s = linknewsym(Ctxt, sym.name, Ctxt.Version) - - s.Type |= obj.SHIDDEN - } - - case ElfSymBindWeak: - if needSym != 0 { - s = linknewsym(Ctxt, sym.name, 0) - if sym.other == 2 { - s.Type |= obj.SHIDDEN - } - } - - default: - err = fmt.Errorf("%s: invalid symbol binding %d", sym.name, sym.bind) - return err - } - } - - if s != nil && s.Type == 0 && sym.type_ != ElfSymTypeSection { - s.Type = obj.SXREF - } - sym.sym = s - - return nil -} - -type rbyoff []Reloc - -func (x rbyoff) Len() int { - return len(x) -} - -func (x rbyoff) Swap(i, j int) { - x[i], x[j] = x[j], x[i] -} - -func (x rbyoff) Less(i, j int) bool { - a := &x[i] - b := &x[j] - if a.Off < b.Off { - return true - } - if a.Off > b.Off { - return false - } - return false -} - -func reltype(pn string, elftype int, siz *uint8) int { - switch uint32(Thearch.Thechar) | uint32(elftype)<<24 { - default: - Diag("%s: unknown relocation type %d; compiled without -fpic?", pn, elftype) - fallthrough - - case '9' | R_PPC64_TOC16<<24, - '9' | R_PPC64_TOC16_LO<<24, - '9' | R_PPC64_TOC16_HI<<24, - '9' | R_PPC64_TOC16_HA<<24, - '9' | R_PPC64_TOC16_DS<<24, - '9' | R_PPC64_TOC16_LO_DS<<24, - '9' | R_PPC64_REL16_LO<<24, - '9' | R_PPC64_REL16_HI<<24, - '9' | R_PPC64_REL16_HA<<24: - *siz = 2 - - case '5' | R_ARM_ABS32<<24, - '5' | R_ARM_GOT32<<24, - '5' | R_ARM_PLT32<<24, - '5' | R_ARM_GOTOFF<<24, - '5' | R_ARM_GOTPC<<24, - '5' | R_ARM_THM_PC22<<24, - '5' | R_ARM_REL32<<24, - '5' | R_ARM_CALL<<24, - '5' | R_ARM_V4BX<<24, - '5' | R_ARM_GOT_PREL<<24, - '5' | R_ARM_PC24<<24, - '5' | R_ARM_JUMP24<<24, - '6' | R_X86_64_PC32<<24, - '6' | R_X86_64_PLT32<<24, - '6' | R_X86_64_GOTPCREL<<24, - '8' | R_386_32<<24, - '8' | R_386_PC32<<24, - '8' | R_386_GOT32<<24, - '8' | R_386_PLT32<<24, - '8' | R_386_GOTOFF<<24, - '8' | R_386_GOTPC<<24, - '9' | R_PPC64_REL24<<24: - *siz = 4 - - case '6' | R_X86_64_64<<24, - '9' | R_PPC64_ADDR64<<24: - *siz = 8 - } - - return 256 + elftype -} diff --git a/src/cmd/internal/ld/ldmacho.go b/src/cmd/internal/ld/ldmacho.go deleted file mode 100644 index 2abfa3336f..0000000000 --- a/src/cmd/internal/ld/ldmacho.go +++ /dev/null @@ -1,893 +0,0 @@ -package ld - -import ( - "cmd/internal/obj" - "encoding/binary" - "fmt" - "log" - "sort" -) - -/* -Derived from Plan 9 from User Space's src/libmach/elf.h, elf.c -http://code.swtch.com/plan9port/src/tip/src/libmach/ - - Copyright © 2004 Russ Cox. - Portions Copyright © 2008-2010 Google Inc. - Portions Copyright © 2010 The Go Authors. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ -const ( - N_EXT = 0x01 - N_TYPE = 0x1e - N_STAB = 0xe0 -) - -type LdMachoObj struct { - f *obj.Biobuf - base int64 // off in f where Mach-O begins - length int64 // length of Mach-O - is64 bool - name string - e binary.ByteOrder - cputype uint - subcputype uint - filetype uint32 - flags uint32 - cmd []LdMachoCmd - ncmd uint -} - -type LdMachoCmd struct { - type_ int - off uint32 - size uint32 - seg LdMachoSeg - sym LdMachoSymtab - dsym LdMachoDysymtab -} - -type LdMachoSeg struct { - name string - vmaddr uint64 - vmsize uint64 - fileoff uint32 - filesz uint32 - maxprot uint32 - initprot uint32 - nsect uint32 - flags uint32 - sect []LdMachoSect -} - -type LdMachoSect struct { - name string - segname string - addr uint64 - size uint64 - off uint32 - align uint32 - reloff uint32 - nreloc uint32 - flags uint32 - res1 uint32 - res2 uint32 - sym *LSym - rel []LdMachoRel -} - -type LdMachoRel struct { - addr uint32 - symnum uint32 - pcrel uint8 - length uint8 - extrn uint8 - type_ uint8 - scattered uint8 - value uint32 -} - -type LdMachoSymtab struct { - symoff uint32 - nsym uint32 - stroff uint32 - strsize uint32 - str []byte - sym []LdMachoSym -} - -type LdMachoSym struct { - name string - type_ uint8 - sectnum uint8 - desc uint16 - kind int8 - value uint64 - sym *LSym -} - -type LdMachoDysymtab struct { - ilocalsym uint32 - nlocalsym uint32 - iextdefsym uint32 - nextdefsym uint32 - iundefsym uint32 - nundefsym uint32 - tocoff uint32 - ntoc uint32 - modtaboff uint32 - nmodtab uint32 - extrefsymoff uint32 - nextrefsyms uint32 - indirectsymoff uint32 - nindirectsyms uint32 - extreloff uint32 - nextrel uint32 - locreloff uint32 - nlocrel uint32 - indir []uint32 -} - -const ( - LdMachoCpuVax = 1 - LdMachoCpu68000 = 6 - LdMachoCpu386 = 7 - LdMachoCpuAmd64 = 0x1000007 - LdMachoCpuMips = 8 - LdMachoCpu98000 = 10 - LdMachoCpuHppa = 11 - LdMachoCpuArm = 12 - LdMachoCpu88000 = 13 - LdMachoCpuSparc = 14 - LdMachoCpu860 = 15 - LdMachoCpuAlpha = 16 - LdMachoCpuPower = 18 - LdMachoCmdSegment = 1 - LdMachoCmdSymtab = 2 - LdMachoCmdSymseg = 3 - LdMachoCmdThread = 4 - LdMachoCmdDysymtab = 11 - LdMachoCmdSegment64 = 25 - LdMachoFileObject = 1 - LdMachoFileExecutable = 2 - LdMachoFileFvmlib = 3 - LdMachoFileCore = 4 - LdMachoFilePreload = 5 -) - -func unpackcmd(p []byte, m *LdMachoObj, c *LdMachoCmd, type_ uint, sz uint) int { - e4 := m.e.Uint32 - e8 := m.e.Uint64 - - c.type_ = int(type_) - c.size = uint32(sz) - switch type_ { - default: - return -1 - - case LdMachoCmdSegment: - if sz < 56 { - return -1 - } - c.seg.name = cstring(p[8:24]) - c.seg.vmaddr = uint64(e4(p[24:])) - c.seg.vmsize = uint64(e4(p[28:])) - c.seg.fileoff = e4(p[32:]) - c.seg.filesz = e4(p[36:]) - c.seg.maxprot = e4(p[40:]) - c.seg.initprot = e4(p[44:]) - c.seg.nsect = e4(p[48:]) - c.seg.flags = e4(p[52:]) - c.seg.sect = make([]LdMachoSect, c.seg.nsect) - if uint32(sz) < 56+c.seg.nsect*68 { - return -1 - } - p = p[56:] - var s *LdMachoSect - for i := 0; uint32(i) < c.seg.nsect; i++ { - s = &c.seg.sect[i] - s.name = cstring(p[0:16]) - s.segname = cstring(p[16:32]) - s.addr = uint64(e4(p[32:])) - s.size = uint64(e4(p[36:])) - s.off = e4(p[40:]) - s.align = e4(p[44:]) - s.reloff = e4(p[48:]) - s.nreloc = e4(p[52:]) - s.flags = e4(p[56:]) - s.res1 = e4(p[60:]) - s.res2 = e4(p[64:]) - p = p[68:] - } - - case LdMachoCmdSegment64: - if sz < 72 { - return -1 - } - c.seg.name = cstring(p[8:24]) - c.seg.vmaddr = e8(p[24:]) - c.seg.vmsize = e8(p[32:]) - c.seg.fileoff = uint32(e8(p[40:])) - c.seg.filesz = uint32(e8(p[48:])) - c.seg.maxprot = e4(p[56:]) - c.seg.initprot = e4(p[60:]) - c.seg.nsect = e4(p[64:]) - c.seg.flags = e4(p[68:]) - c.seg.sect = make([]LdMachoSect, c.seg.nsect) - if uint32(sz) < 72+c.seg.nsect*80 { - return -1 - } - p = p[72:] - var s *LdMachoSect - for i := 0; uint32(i) < c.seg.nsect; i++ { - s = &c.seg.sect[i] - s.name = cstring(p[0:16]) - s.segname = cstring(p[16:32]) - s.addr = e8(p[32:]) - s.size = e8(p[40:]) - s.off = e4(p[48:]) - s.align = e4(p[52:]) - s.reloff = e4(p[56:]) - s.nreloc = e4(p[60:]) - s.flags = e4(p[64:]) - s.res1 = e4(p[68:]) - s.res2 = e4(p[72:]) - - // p+76 is reserved - p = p[80:] - } - - case LdMachoCmdSymtab: - if sz < 24 { - return -1 - } - c.sym.symoff = e4(p[8:]) - c.sym.nsym = e4(p[12:]) - c.sym.stroff = e4(p[16:]) - c.sym.strsize = e4(p[20:]) - - case LdMachoCmdDysymtab: - if sz < 80 { - return -1 - } - c.dsym.ilocalsym = e4(p[8:]) - c.dsym.nlocalsym = e4(p[12:]) - c.dsym.iextdefsym = e4(p[16:]) - c.dsym.nextdefsym = e4(p[20:]) - c.dsym.iundefsym = e4(p[24:]) - c.dsym.nundefsym = e4(p[28:]) - c.dsym.tocoff = e4(p[32:]) - c.dsym.ntoc = e4(p[36:]) - c.dsym.modtaboff = e4(p[40:]) - c.dsym.nmodtab = e4(p[44:]) - c.dsym.extrefsymoff = e4(p[48:]) - c.dsym.nextrefsyms = e4(p[52:]) - c.dsym.indirectsymoff = e4(p[56:]) - c.dsym.nindirectsyms = e4(p[60:]) - c.dsym.extreloff = e4(p[64:]) - c.dsym.nextrel = e4(p[68:]) - c.dsym.locreloff = e4(p[72:]) - c.dsym.nlocrel = e4(p[76:]) - } - - return 0 -} - -func macholoadrel(m *LdMachoObj, sect *LdMachoSect) int { - if sect.rel != nil || sect.nreloc == 0 { - return 0 - } - rel := make([]LdMachoRel, sect.nreloc) - n := int(sect.nreloc * 8) - buf := make([]byte, n) - if obj.Bseek(m.f, m.base+int64(sect.reloff), 0) < 0 || obj.Bread(m.f, buf) != n { - return -1 - } - var p []byte - var r *LdMachoRel - var v uint32 - for i := 0; uint32(i) < sect.nreloc; i++ { - r = &rel[i] - p = buf[i*8:] - r.addr = m.e.Uint32(p) - - // TODO(rsc): Wrong interpretation for big-endian bitfields? - if r.addr&0x80000000 != 0 { - // scatterbrained relocation - r.scattered = 1 - - v = r.addr >> 24 - r.addr &= 0xFFFFFF - r.type_ = uint8(v & 0xF) - v >>= 4 - r.length = 1 << (v & 3) - v >>= 2 - r.pcrel = uint8(v & 1) - r.value = m.e.Uint32(p[4:]) - } else { - v = m.e.Uint32(p[4:]) - r.symnum = v & 0xFFFFFF - v >>= 24 - r.pcrel = uint8(v & 1) - v >>= 1 - r.length = 1 << (v & 3) - v >>= 2 - r.extrn = uint8(v & 1) - v >>= 1 - r.type_ = uint8(v) - } - } - - sect.rel = rel - return 0 -} - -func macholoaddsym(m *LdMachoObj, d *LdMachoDysymtab) int { - n := int(d.nindirectsyms) - - p := make([]byte, n*4) - if obj.Bseek(m.f, m.base+int64(d.indirectsymoff), 0) < 0 || obj.Bread(m.f, p) != len(p) { - return -1 - } - - d.indir = make([]uint32, n) - for i := 0; i < n; i++ { - d.indir[i] = m.e.Uint32(p[4*i:]) - } - return 0 -} - -func macholoadsym(m *LdMachoObj, symtab *LdMachoSymtab) int { - if symtab.sym != nil { - return 0 - } - - strbuf := make([]byte, symtab.strsize) - if obj.Bseek(m.f, m.base+int64(symtab.stroff), 0) < 0 || obj.Bread(m.f, strbuf) != len(strbuf) { - return -1 - } - - symsize := 12 - if m.is64 { - symsize = 16 - } - n := int(symtab.nsym * uint32(symsize)) - symbuf := make([]byte, n) - if obj.Bseek(m.f, m.base+int64(symtab.symoff), 0) < 0 || obj.Bread(m.f, symbuf) != len(symbuf) { - return -1 - } - sym := make([]LdMachoSym, symtab.nsym) - p := symbuf - var s *LdMachoSym - var v uint32 - for i := 0; uint32(i) < symtab.nsym; i++ { - s = &sym[i] - v = m.e.Uint32(p) - if v >= symtab.strsize { - return -1 - } - s.name = cstring(strbuf[v:]) - s.type_ = uint8(p[4]) - s.sectnum = uint8(p[5]) - s.desc = m.e.Uint16(p[6:]) - if m.is64 { - s.value = m.e.Uint64(p[8:]) - } else { - s.value = uint64(m.e.Uint32(p[8:])) - } - p = p[symsize:] - } - - symtab.str = strbuf - symtab.sym = sym - return 0 -} - -func ldmacho(f *obj.Biobuf, pkg string, length int64, pn string) { - var err error - var j int - var is64 bool - var secaddr uint64 - var hdr [7 * 4]uint8 - var cmdp []byte - var dat []byte - var ncmd uint32 - var cmdsz uint32 - var ty uint32 - var sz uint32 - var off uint32 - var m *LdMachoObj - var e binary.ByteOrder - var sect *LdMachoSect - var rel *LdMachoRel - var rpi int - var s *LSym - var s1 *LSym - var outer *LSym - var c *LdMachoCmd - var symtab *LdMachoSymtab - var dsymtab *LdMachoDysymtab - var sym *LdMachoSym - var r []Reloc - var rp *Reloc - var name string - - Ctxt.Version++ - base := obj.Boffset(f) - if obj.Bread(f, hdr[:]) != len(hdr) { - goto bad - } - - if binary.BigEndian.Uint32(hdr[:])&^1 == 0xFEEDFACE { - e = binary.BigEndian - } else if binary.LittleEndian.Uint32(hdr[:])&^1 == 0xFEEDFACE { - e = binary.LittleEndian - } else { - err = fmt.Errorf("bad magic - not mach-o file") - goto bad - } - - is64 = e.Uint32(hdr[:]) == 0xFEEDFACF - ncmd = e.Uint32([]byte(hdr[4*4:])) - cmdsz = e.Uint32([]byte(hdr[5*4:])) - if ncmd > 0x10000 || cmdsz >= 0x01000000 { - err = fmt.Errorf("implausible mach-o header ncmd=%d cmdsz=%d", ncmd, cmdsz) - goto bad - } - - if is64 { - var tmp [4]uint8 - obj.Bread(f, tmp[:4]) // skip reserved word in header - } - - m = new(LdMachoObj) - - m.f = f - m.e = e - m.cputype = uint(e.Uint32([]byte(hdr[1*4:]))) - m.subcputype = uint(e.Uint32([]byte(hdr[2*4:]))) - m.filetype = e.Uint32([]byte(hdr[3*4:])) - m.ncmd = uint(ncmd) - m.flags = e.Uint32([]byte(hdr[6*4:])) - m.is64 = is64 - m.base = base - m.length = length - m.name = pn - - switch Thearch.Thechar { - default: - Diag("%s: mach-o %s unimplemented", pn, Thestring) - return - - case '6': - if e != binary.LittleEndian || m.cputype != LdMachoCpuAmd64 { - Diag("%s: mach-o object but not amd64", pn) - return - } - - case '8': - if e != binary.LittleEndian || m.cputype != LdMachoCpu386 { - Diag("%s: mach-o object but not 386", pn) - return - } - } - - m.cmd = make([]LdMachoCmd, ncmd) - off = uint32(len(hdr)) - cmdp = make([]byte, cmdsz) - if obj.Bread(f, cmdp) != len(cmdp) { - err = fmt.Errorf("reading cmds: %v", err) - goto bad - } - - // read and parse load commands - c = nil - - symtab = nil - dsymtab = nil - - for i := 0; uint32(i) < ncmd; i++ { - ty = e.Uint32(cmdp) - sz = e.Uint32(cmdp[4:]) - m.cmd[i].off = off - unpackcmd(cmdp, m, &m.cmd[i], uint(ty), uint(sz)) - cmdp = cmdp[sz:] - off += sz - if ty == LdMachoCmdSymtab { - if symtab != nil { - err = fmt.Errorf("multiple symbol tables") - goto bad - } - - symtab = &m.cmd[i].sym - macholoadsym(m, symtab) - } - - if ty == LdMachoCmdDysymtab { - dsymtab = &m.cmd[i].dsym - macholoaddsym(m, dsymtab) - } - - if (is64 && ty == LdMachoCmdSegment64) || (!is64 && ty == LdMachoCmdSegment) { - if c != nil { - err = fmt.Errorf("multiple load commands") - goto bad - } - - c = &m.cmd[i] - } - } - - // load text and data segments into memory. - // they are not as small as the load commands, but we'll need - // the memory anyway for the symbol images, so we might - // as well use one large chunk. - if c == nil { - err = fmt.Errorf("no load command") - goto bad - } - - if symtab == nil { - // our work is done here - no symbols means nothing can refer to this file - return - } - - if int64(c.seg.fileoff+c.seg.filesz) >= length { - err = fmt.Errorf("load segment out of range") - goto bad - } - - dat = make([]byte, c.seg.filesz) - if obj.Bseek(f, m.base+int64(c.seg.fileoff), 0) < 0 || obj.Bread(f, dat) != len(dat) { - err = fmt.Errorf("cannot load object data: %v", err) - goto bad - } - - for i := 0; uint32(i) < c.seg.nsect; i++ { - sect = &c.seg.sect[i] - if sect.segname != "__TEXT" && sect.segname != "__DATA" { - continue - } - if sect.name == "__eh_frame" { - continue - } - name = fmt.Sprintf("%s(%s/%s)", pkg, sect.segname, sect.name) - s = Linklookup(Ctxt, name, Ctxt.Version) - if s.Type != 0 { - err = fmt.Errorf("duplicate %s/%s", sect.segname, sect.name) - goto bad - } - - if sect.flags&0xff == 1 { // S_ZEROFILL - s.P = make([]byte, sect.size) - } else { - s.P = dat[sect.addr-c.seg.vmaddr:][:sect.size] - } - s.Size = int64(len(s.P)) - - if sect.segname == "__TEXT" { - if sect.name == "__text" { - s.Type = obj.STEXT - } else { - s.Type = obj.SRODATA - } - } else { - if sect.name == "__bss" { - s.Type = obj.SNOPTRBSS - s.P = s.P[:0] - } else { - s.Type = obj.SNOPTRDATA - } - } - - sect.sym = s - } - - // enter sub-symbols into symbol table. - // have to guess sizes from next symbol. - for i := 0; uint32(i) < symtab.nsym; i++ { - sym = &symtab.sym[i] - if sym.type_&N_STAB != 0 { - continue - } - - // TODO: check sym->type against outer->type. - name = sym.name - - if name[0] == '_' && name[1] != '\x00' { - name = name[1:] - } - v := 0 - if sym.type_&N_EXT == 0 { - v = Ctxt.Version - } - s = Linklookup(Ctxt, name, v) - if sym.type_&N_EXT == 0 { - s.Dupok = 1 - } - sym.sym = s - if sym.sectnum == 0 { // undefined - continue - } - if uint32(sym.sectnum) > c.seg.nsect { - err = fmt.Errorf("reference to invalid section %d", sym.sectnum) - goto bad - } - - sect = &c.seg.sect[sym.sectnum-1] - outer = sect.sym - if outer == nil { - err = fmt.Errorf("reference to invalid section %s/%s", sect.segname, sect.name) - continue - } - - if s.Outer != nil { - if s.Dupok != 0 { - continue - } - Exitf("%s: duplicate symbol reference: %s in both %s and %s", pn, s.Name, s.Outer.Name, sect.sym.Name) - } - - s.Type = outer.Type | obj.SSUB - s.Sub = outer.Sub - outer.Sub = s - s.Outer = outer - s.Value = int64(sym.value - sect.addr) - if s.Cgoexport&CgoExportDynamic == 0 { - s.Dynimplib = "" // satisfy dynimport - } - if outer.Type == obj.STEXT { - if s.External != 0 && s.Dupok == 0 { - Diag("%s: duplicate definition of %s", pn, s.Name) - } - s.External = 1 - } - - sym.sym = s - } - - // Sort outer lists by address, adding to textp. - // This keeps textp in increasing address order. - for i := 0; uint32(i) < c.seg.nsect; i++ { - sect = &c.seg.sect[i] - s = sect.sym - if s == nil { - continue - } - if s.Sub != nil { - s.Sub = listsort(s.Sub, valuecmp, listsubp) - - // assign sizes, now that we know symbols in sorted order. - for s1 = s.Sub; s1 != nil; s1 = s1.Sub { - if s1.Sub != nil { - s1.Size = s1.Sub.Value - s1.Value - } else { - s1.Size = s.Value + s.Size - s1.Value - } - } - } - - if s.Type == obj.STEXT { - if s.Onlist != 0 { - log.Fatalf("symbol %s listed multiple times", s.Name) - } - s.Onlist = 1 - if Ctxt.Etextp != nil { - Ctxt.Etextp.Next = s - } else { - Ctxt.Textp = s - } - Ctxt.Etextp = s - for s1 = s.Sub; s1 != nil; s1 = s1.Sub { - if s1.Onlist != 0 { - log.Fatalf("symbol %s listed multiple times", s1.Name) - } - s1.Onlist = 1 - Ctxt.Etextp.Next = s1 - Ctxt.Etextp = s1 - } - } - } - - // load relocations - for i := 0; uint32(i) < c.seg.nsect; i++ { - sect = &c.seg.sect[i] - s = sect.sym - if s == nil { - continue - } - macholoadrel(m, sect) - if sect.rel == nil { - continue - } - r = make([]Reloc, sect.nreloc) - rpi = 0 - Reloc: - for j = 0; uint32(j) < sect.nreloc; j++ { - rp = &r[rpi] - rel = §.rel[j] - if rel.scattered != 0 { - if Thearch.Thechar != '8' { - // mach-o only uses scattered relocation on 32-bit platforms - Diag("unexpected scattered relocation") - - continue - } - - // on 386, rewrite scattered 4/1 relocation and some - // scattered 2/1 relocation into the pseudo-pc-relative - // reference that it is. - // assume that the second in the pair is in this section - // and use that as the pc-relative base. - if uint32(j+1) >= sect.nreloc { - err = fmt.Errorf("unsupported scattered relocation %d", int(rel.type_)) - goto bad - } - - if sect.rel[j+1].scattered == 0 || sect.rel[j+1].type_ != 1 || (rel.type_ != 4 && rel.type_ != 2) || uint64(sect.rel[j+1].value) < sect.addr || uint64(sect.rel[j+1].value) >= sect.addr+sect.size { - err = fmt.Errorf("unsupported scattered relocation %d/%d", int(rel.type_), int(sect.rel[j+1].type_)) - goto bad - } - - rp.Siz = rel.length - rp.Off = int32(rel.addr) - - // NOTE(rsc): I haven't worked out why (really when) - // we should ignore the addend on a - // scattered relocation, but it seems that the - // common case is we ignore it. - // It's likely that this is not strictly correct - // and that the math should look something - // like the non-scattered case below. - rp.Add = 0 - - // want to make it pc-relative aka relative to rp->off+4 - // but the scatter asks for relative to off = sect->rel[j+1].value - sect->addr. - // adjust rp->add accordingly. - rp.Type = obj.R_PCREL - - rp.Add += int64(uint64(int64(rp.Off)+4) - (uint64(sect.rel[j+1].value) - sect.addr)) - - // now consider the desired symbol. - // find the section where it lives. - var ks *LdMachoSect - for k := 0; uint32(k) < c.seg.nsect; k++ { - ks = &c.seg.sect[k] - if ks.addr <= uint64(rel.value) && uint64(rel.value) < ks.addr+ks.size { - if ks.sym != nil { - rp.Sym = ks.sym - rp.Add += int64(uint64(rel.value) - ks.addr) - } else if ks.segname == "__IMPORT" && ks.name == "__pointers" { - // handle reference to __IMPORT/__pointers. - // how much worse can this get? - // why are we supporting 386 on the mac anyway? - rp.Type = 512 + MACHO_FAKE_GOTPCREL - - // figure out which pointer this is a reference to. - k = int(uint64(ks.res1) + (uint64(rel.value)-ks.addr)/4) - - // load indirect table for __pointers - // fetch symbol number - if dsymtab == nil || k < 0 || uint32(k) >= dsymtab.nindirectsyms || dsymtab.indir == nil { - err = fmt.Errorf("invalid scattered relocation: indirect symbol reference out of range") - goto bad - } - - k = int(dsymtab.indir[k]) - if k < 0 || uint32(k) >= symtab.nsym { - err = fmt.Errorf("invalid scattered relocation: symbol reference out of range") - goto bad - } - - rp.Sym = symtab.sym[k].sym - } else { - err = fmt.Errorf("unsupported scattered relocation: reference to %s/%s", ks.segname, ks.name) - goto bad - } - - rpi++ - - // skip #1 of 2 rel; continue skips #2 of 2. - j++ - - continue Reloc - } - } - - err = fmt.Errorf("unsupported scattered relocation: invalid address %#x", rel.addr) - goto bad - - } - - rp.Siz = rel.length - rp.Type = 512 + (int32(rel.type_) << 1) + int32(rel.pcrel) - rp.Off = int32(rel.addr) - - // Handle X86_64_RELOC_SIGNED referencing a section (rel->extrn == 0). - if Thearch.Thechar == '6' && rel.extrn == 0 && rel.type_ == 1 { - // Calculate the addend as the offset into the section. - // - // The rip-relative offset stored in the object file is encoded - // as follows: - // - // movsd 0x00000360(%rip),%xmm0 - // - // To get the absolute address of the value this rip-relative address is pointing - // to, we must add the address of the next instruction to it. This is done by - // taking the address of the relocation and adding 4 to it (since the rip-relative - // offset can at most be 32 bits long). To calculate the offset into the section the - // relocation is referencing, we subtract the vaddr of the start of the referenced - // section found in the original object file. - // - // [For future reference, see Darwin's /usr/include/mach-o/x86_64/reloc.h] - secaddr = c.seg.sect[rel.symnum-1].addr - - rp.Add = int64(uint64(int64(int32(e.Uint32(s.P[rp.Off:])))+int64(rp.Off)+4) - secaddr) - } else { - rp.Add = int64(int32(e.Uint32(s.P[rp.Off:]))) - } - - // For i386 Mach-O PC-relative, the addend is written such that - // it *is* the PC being subtracted. Use that to make - // it match our version of PC-relative. - if rel.pcrel != 0 && Thearch.Thechar == '8' { - rp.Add += int64(rp.Off) + int64(rp.Siz) - } - if rel.extrn == 0 { - if rel.symnum < 1 || rel.symnum > c.seg.nsect { - err = fmt.Errorf("invalid relocation: section reference out of range %d vs %d", rel.symnum, c.seg.nsect) - goto bad - } - - rp.Sym = c.seg.sect[rel.symnum-1].sym - if rp.Sym == nil { - err = fmt.Errorf("invalid relocation: %s", c.seg.sect[rel.symnum-1].name) - goto bad - } - - // References to symbols in other sections - // include that information in the addend. - // We only care about the delta from the - // section base. - if Thearch.Thechar == '8' { - rp.Add -= int64(c.seg.sect[rel.symnum-1].addr) - } - } else { - if rel.symnum >= symtab.nsym { - err = fmt.Errorf("invalid relocation: symbol reference out of range") - goto bad - } - - rp.Sym = symtab.sym[rel.symnum].sym - } - - rpi++ - } - - sort.Sort(rbyoff(r[:rpi])) - s.R = r - s.R = s.R[:rpi] - } - - return - -bad: - Diag("%s: malformed mach-o file: %v", pn, err) -} diff --git a/src/cmd/internal/ld/ldpe.go b/src/cmd/internal/ld/ldpe.go deleted file mode 100644 index b98cf0206b..0000000000 --- a/src/cmd/internal/ld/ldpe.go +++ /dev/null @@ -1,534 +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. - -package ld - -import ( - "cmd/internal/obj" - "encoding/binary" - "fmt" - "log" - "sort" - "strings" -) - -const ( - IMAGE_SYM_UNDEFINED = 0 - IMAGE_SYM_ABSOLUTE = -1 - IMAGE_SYM_DEBUG = -2 - IMAGE_SYM_TYPE_NULL = 0 - IMAGE_SYM_TYPE_VOID = 1 - IMAGE_SYM_TYPE_CHAR = 2 - IMAGE_SYM_TYPE_SHORT = 3 - IMAGE_SYM_TYPE_INT = 4 - IMAGE_SYM_TYPE_LONG = 5 - IMAGE_SYM_TYPE_FLOAT = 6 - IMAGE_SYM_TYPE_DOUBLE = 7 - IMAGE_SYM_TYPE_STRUCT = 8 - IMAGE_SYM_TYPE_UNION = 9 - IMAGE_SYM_TYPE_ENUM = 10 - IMAGE_SYM_TYPE_MOE = 11 - IMAGE_SYM_TYPE_BYTE = 12 - IMAGE_SYM_TYPE_WORD = 13 - IMAGE_SYM_TYPE_UINT = 14 - IMAGE_SYM_TYPE_DWORD = 15 - IMAGE_SYM_TYPE_PCODE = 32768 - IMAGE_SYM_DTYPE_NULL = 0 - IMAGE_SYM_DTYPE_POINTER = 0x10 - IMAGE_SYM_DTYPE_FUNCTION = 0x20 - IMAGE_SYM_DTYPE_ARRAY = 0x30 - IMAGE_SYM_CLASS_END_OF_FUNCTION = -1 - IMAGE_SYM_CLASS_NULL = 0 - IMAGE_SYM_CLASS_AUTOMATIC = 1 - IMAGE_SYM_CLASS_EXTERNAL = 2 - IMAGE_SYM_CLASS_STATIC = 3 - IMAGE_SYM_CLASS_REGISTER = 4 - IMAGE_SYM_CLASS_EXTERNAL_DEF = 5 - IMAGE_SYM_CLASS_LABEL = 6 - IMAGE_SYM_CLASS_UNDEFINED_LABEL = 7 - IMAGE_SYM_CLASS_MEMBER_OF_STRUCT = 8 - IMAGE_SYM_CLASS_ARGUMENT = 9 - IMAGE_SYM_CLASS_STRUCT_TAG = 10 - IMAGE_SYM_CLASS_MEMBER_OF_UNION = 11 - IMAGE_SYM_CLASS_UNION_TAG = 12 - IMAGE_SYM_CLASS_TYPE_DEFINITION = 13 - IMAGE_SYM_CLASS_UNDEFINED_STATIC = 14 - IMAGE_SYM_CLASS_ENUM_TAG = 15 - IMAGE_SYM_CLASS_MEMBER_OF_ENUM = 16 - IMAGE_SYM_CLASS_REGISTER_PARAM = 17 - IMAGE_SYM_CLASS_BIT_FIELD = 18 - IMAGE_SYM_CLASS_FAR_EXTERNAL = 68 /* Not in PECOFF v8 spec */ - IMAGE_SYM_CLASS_BLOCK = 100 - IMAGE_SYM_CLASS_FUNCTION = 101 - IMAGE_SYM_CLASS_END_OF_STRUCT = 102 - IMAGE_SYM_CLASS_FILE = 103 - IMAGE_SYM_CLASS_SECTION = 104 - IMAGE_SYM_CLASS_WEAK_EXTERNAL = 105 - IMAGE_SYM_CLASS_CLR_TOKEN = 107 - IMAGE_REL_I386_ABSOLUTE = 0x0000 - IMAGE_REL_I386_DIR16 = 0x0001 - IMAGE_REL_I386_REL16 = 0x0002 - IMAGE_REL_I386_DIR32 = 0x0006 - IMAGE_REL_I386_DIR32NB = 0x0007 - IMAGE_REL_I386_SEG12 = 0x0009 - IMAGE_REL_I386_SECTION = 0x000A - IMAGE_REL_I386_SECREL = 0x000B - IMAGE_REL_I386_TOKEN = 0x000C - IMAGE_REL_I386_SECREL7 = 0x000D - IMAGE_REL_I386_REL32 = 0x0014 - IMAGE_REL_AMD64_ABSOLUTE = 0x0000 - IMAGE_REL_AMD64_ADDR64 = 0x0001 - IMAGE_REL_AMD64_ADDR32 = 0x0002 - IMAGE_REL_AMD64_ADDR32NB = 0x0003 - IMAGE_REL_AMD64_REL32 = 0x0004 - IMAGE_REL_AMD64_REL32_1 = 0x0005 - IMAGE_REL_AMD64_REL32_2 = 0x0006 - IMAGE_REL_AMD64_REL32_3 = 0x0007 - IMAGE_REL_AMD64_REL32_4 = 0x0008 - IMAGE_REL_AMD64_REL32_5 = 0x0009 - IMAGE_REL_AMD64_SECTION = 0x000A - IMAGE_REL_AMD64_SECREL = 0x000B - IMAGE_REL_AMD64_SECREL7 = 0x000C - IMAGE_REL_AMD64_TOKEN = 0x000D - IMAGE_REL_AMD64_SREL32 = 0x000E - IMAGE_REL_AMD64_PAIR = 0x000F - IMAGE_REL_AMD64_SSPAN32 = 0x0010 -) - -type PeSym struct { - name string - value uint32 - sectnum uint16 - type_ uint16 - sclass uint8 - aux uint8 - sym *LSym -} - -type PeSect struct { - name string - base []byte - size uint64 - sym *LSym - sh IMAGE_SECTION_HEADER -} - -type PeObj struct { - f *obj.Biobuf - name string - base uint32 - sect []PeSect - nsect uint - pesym []PeSym - npesym uint - fh IMAGE_FILE_HEADER - snames []byte -} - -func ldpe(f *obj.Biobuf, pkg string, length int64, pn string) { - if Debug['v'] != 0 { - fmt.Fprintf(&Bso, "%5.2f ldpe %s\n", obj.Cputime(), pn) - } - - var sect *PeSect - Ctxt.Version++ - base := int32(obj.Boffset(f)) - - peobj := new(PeObj) - peobj.f = f - peobj.base = uint32(base) - peobj.name = pn - - // read header - var err error - var j int - var l uint32 - var name string - var numaux int - var r []Reloc - var rp *Reloc - var rsect *PeSect - var s *LSym - var sym *PeSym - var symbuf [18]uint8 - if err = binary.Read(f, binary.LittleEndian, &peobj.fh); err != nil { - goto bad - } - - // load section list - peobj.sect = make([]PeSect, peobj.fh.NumberOfSections) - - peobj.nsect = uint(peobj.fh.NumberOfSections) - for i := 0; i < int(peobj.fh.NumberOfSections); i++ { - if err = binary.Read(f, binary.LittleEndian, &peobj.sect[i].sh); err != nil { - goto bad - } - peobj.sect[i].size = uint64(peobj.sect[i].sh.SizeOfRawData) - peobj.sect[i].name = cstring(peobj.sect[i].sh.Name[:]) - } - - // TODO return error if found .cormeta - - // load string table - obj.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(peobj.fh.NumberOfSymbols), 0) - - if obj.Bread(f, symbuf[:4]) != 4 { - goto bad - } - l = Le32(symbuf[:]) - peobj.snames = make([]byte, l) - obj.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(peobj.fh.NumberOfSymbols), 0) - if obj.Bread(f, peobj.snames) != len(peobj.snames) { - goto bad - } - - // rewrite section names if they start with / - for i := 0; i < int(peobj.fh.NumberOfSections); i++ { - if peobj.sect[i].name == "" { - continue - } - if peobj.sect[i].name[0] != '/' { - continue - } - l = uint32(obj.Atoi(peobj.sect[i].name[1:])) - peobj.sect[i].name = cstring(peobj.snames[l:]) - } - - // read symbols - peobj.pesym = make([]PeSym, peobj.fh.NumberOfSymbols) - - peobj.npesym = uint(peobj.fh.NumberOfSymbols) - obj.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable), 0) - for i := 0; uint32(i) < peobj.fh.NumberOfSymbols; i += numaux + 1 { - obj.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(i), 0) - if obj.Bread(f, symbuf[:]) != len(symbuf) { - goto bad - } - - if (symbuf[0] == 0) && (symbuf[1] == 0) && (symbuf[2] == 0) && (symbuf[3] == 0) { - l = Le32(symbuf[4:]) - peobj.pesym[i].name = cstring(peobj.snames[l:]) // sym name length <= 8 - } else { - peobj.pesym[i].name = cstring(symbuf[:8]) - } - - peobj.pesym[i].value = Le32(symbuf[8:]) - peobj.pesym[i].sectnum = Le16(symbuf[12:]) - peobj.pesym[i].sclass = symbuf[16] - peobj.pesym[i].aux = symbuf[17] - peobj.pesym[i].type_ = Le16(symbuf[14:]) - numaux = int(peobj.pesym[i].aux) - if numaux < 0 { - numaux = 0 - } - } - - // create symbols for mapped sections - for i := 0; uint(i) < peobj.nsect; i++ { - sect = &peobj.sect[i] - if sect.sh.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 { - continue - } - - if sect.sh.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 { - // This has been seen for .idata sections, which we - // want to ignore. See issues 5106 and 5273. - continue - } - - if pemap(peobj, sect) < 0 { - goto bad - } - - name = fmt.Sprintf("%s(%s)", pkg, sect.name) - s = Linklookup(Ctxt, name, Ctxt.Version) - - switch sect.sh.Characteristics & (IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE) { - case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ: //.rdata - s.Type = obj.SRODATA - - case IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.bss - s.Type = obj.SNOPTRBSS - - case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.data - s.Type = obj.SNOPTRDATA - - case IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ: //.text - s.Type = obj.STEXT - - default: - err = fmt.Errorf("unexpected flags %#06x for PE section %s", sect.sh.Characteristics, sect.name) - goto bad - } - - s.P = sect.base - s.P = s.P[:sect.size] - s.Size = int64(sect.size) - sect.sym = s - if sect.name == ".rsrc" { - setpersrc(sect.sym) - } - } - - // load relocations - for i := 0; uint(i) < peobj.nsect; i++ { - rsect = &peobj.sect[i] - if rsect.sym == nil || rsect.sh.NumberOfRelocations == 0 { - continue - } - if rsect.sh.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 { - continue - } - if sect.sh.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 { - // This has been seen for .idata sections, which we - // want to ignore. See issues 5106 and 5273. - continue - } - - r = make([]Reloc, rsect.sh.NumberOfRelocations) - obj.Bseek(f, int64(peobj.base)+int64(rsect.sh.PointerToRelocations), 0) - for j = 0; j < int(rsect.sh.NumberOfRelocations); j++ { - rp = &r[j] - if obj.Bread(f, symbuf[:10]) != 10 { - goto bad - } - rva := Le32(symbuf[0:]) - symindex := Le32(symbuf[4:]) - type_ := Le16(symbuf[8:]) - if err = readpesym(peobj, int(symindex), &sym); err != nil { - goto bad - } - if sym.sym == nil { - err = fmt.Errorf("reloc of invalid sym %s idx=%d type=%d", sym.name, symindex, sym.type_) - goto bad - } - - rp.Sym = sym.sym - rp.Siz = 4 - rp.Off = int32(rva) - switch type_ { - default: - Diag("%s: unknown relocation type %d;", pn, type_) - fallthrough - - case IMAGE_REL_I386_REL32, IMAGE_REL_AMD64_REL32, - IMAGE_REL_AMD64_ADDR32, // R_X86_64_PC32 - IMAGE_REL_AMD64_ADDR32NB: - rp.Type = obj.R_PCREL - - rp.Add = int64(int32(Le32(rsect.base[rp.Off:]))) - - case IMAGE_REL_I386_DIR32NB, IMAGE_REL_I386_DIR32: - rp.Type = obj.R_ADDR - - // load addend from image - rp.Add = int64(int32(Le32(rsect.base[rp.Off:]))) - - case IMAGE_REL_AMD64_ADDR64: // R_X86_64_64 - rp.Siz = 8 - - rp.Type = obj.R_ADDR - - // load addend from image - rp.Add = int64(Le64(rsect.base[rp.Off:])) - } - - // ld -r could generate multiple section symbols for the - // same section but with different values, we have to take - // that into account - if issect(&peobj.pesym[symindex]) { - rp.Add += int64(peobj.pesym[symindex].value) - } - } - - sort.Sort(rbyoff(r[:rsect.sh.NumberOfRelocations])) - - s = rsect.sym - s.R = r - s.R = s.R[:rsect.sh.NumberOfRelocations] - } - - // enter sub-symbols into symbol table. - for i := 0; uint(i) < peobj.npesym; i++ { - if peobj.pesym[i].name == "" { - continue - } - if issect(&peobj.pesym[i]) { - continue - } - if uint(peobj.pesym[i].sectnum) > peobj.nsect { - continue - } - if peobj.pesym[i].sectnum > 0 { - sect = &peobj.sect[peobj.pesym[i].sectnum-1] - if sect.sym == nil { - continue - } - } - - if err = readpesym(peobj, i, &sym); err != nil { - goto bad - } - - s = sym.sym - if sym.sectnum == 0 { // extern - if s.Type == obj.SDYNIMPORT { - s.Plt = -2 // flag for dynimport in PE object files. - } - if s.Type == obj.SXREF && sym.value > 0 { // global data - s.Type = obj.SNOPTRDATA - s.Size = int64(sym.value) - } - - continue - } else if sym.sectnum > 0 && uint(sym.sectnum) <= peobj.nsect { - sect = &peobj.sect[sym.sectnum-1] - if sect.sym == nil { - Diag("%s: %s sym == 0!", pn, s.Name) - } - } else { - Diag("%s: %s sectnum < 0!", pn, s.Name) - } - - if sect == nil { - return - } - - if s.Outer != nil { - if s.Dupok != 0 { - continue - } - Exitf("%s: duplicate symbol reference: %s in both %s and %s", pn, s.Name, s.Outer.Name, sect.sym.Name) - } - - s.Sub = sect.sym.Sub - sect.sym.Sub = s - s.Type = sect.sym.Type | obj.SSUB - s.Value = int64(sym.value) - s.Size = 4 - s.Outer = sect.sym - if sect.sym.Type == obj.STEXT { - if s.External != 0 && s.Dupok == 0 { - Diag("%s: duplicate definition of %s", pn, s.Name) - } - s.External = 1 - } - } - - // Sort outer lists by address, adding to textp. - // This keeps textp in increasing address order. - for i := 0; uint(i) < peobj.nsect; i++ { - s = peobj.sect[i].sym - if s == nil { - continue - } - if s.Sub != nil { - s.Sub = listsort(s.Sub, valuecmp, listsubp) - } - if s.Type == obj.STEXT { - if s.Onlist != 0 { - log.Fatalf("symbol %s listed multiple times", s.Name) - } - s.Onlist = 1 - if Ctxt.Etextp != nil { - Ctxt.Etextp.Next = s - } else { - Ctxt.Textp = s - } - Ctxt.Etextp = s - for s = s.Sub; s != nil; s = s.Sub { - if s.Onlist != 0 { - log.Fatalf("symbol %s listed multiple times", s.Name) - } - s.Onlist = 1 - Ctxt.Etextp.Next = s - Ctxt.Etextp = s - } - } - } - - return - -bad: - Diag("%s: malformed pe file: %v", pn, err) -} - -func pemap(peobj *PeObj, sect *PeSect) int { - if sect.base != nil { - return 0 - } - - sect.base = make([]byte, sect.sh.SizeOfRawData) - if sect.sh.PointerToRawData == 0 { // .bss doesn't have data in object file - return 0 - } - if obj.Bseek(peobj.f, int64(peobj.base)+int64(sect.sh.PointerToRawData), 0) < 0 || obj.Bread(peobj.f, sect.base) != len(sect.base) { - return -1 - } - - return 0 -} - -func issect(s *PeSym) bool { - return s.sclass == IMAGE_SYM_CLASS_STATIC && s.type_ == 0 && s.name[0] == '.' -} - -func readpesym(peobj *PeObj, i int, y **PeSym) (err error) { - if uint(i) >= peobj.npesym || i < 0 { - err = fmt.Errorf("invalid pe symbol index") - return err - } - - sym := &peobj.pesym[i] - *y = sym - - var name string - if issect(sym) { - name = peobj.sect[sym.sectnum-1].sym.Name - } else { - name = sym.name - if strings.HasPrefix(name, "__imp_") { - name = name[6:] // __imp_Name => Name - } - if Thearch.Thechar == '8' && name[0] == '_' { - name = name[1:] // _Name => Name - } - } - - // remove last @XXX - if i := strings.LastIndex(name, "@"); i >= 0 { - name = name[:i] - } - - var s *LSym - switch sym.type_ { - default: - err = fmt.Errorf("%s: invalid symbol type %d", sym.name, sym.type_) - return err - - case IMAGE_SYM_DTYPE_FUNCTION, IMAGE_SYM_DTYPE_NULL: - switch sym.sclass { - case IMAGE_SYM_CLASS_EXTERNAL: //global - s = Linklookup(Ctxt, name, 0) - - case IMAGE_SYM_CLASS_NULL, IMAGE_SYM_CLASS_STATIC, IMAGE_SYM_CLASS_LABEL: - s = Linklookup(Ctxt, name, Ctxt.Version) - s.Dupok = 1 - - default: - err = fmt.Errorf("%s: invalid symbol binding %d", sym.name, sym.sclass) - return err - } - } - - if s != nil && s.Type == 0 && (sym.sclass != IMAGE_SYM_CLASS_STATIC || sym.value != 0) { - s.Type = obj.SXREF - } - if strings.HasPrefix(sym.name, "__imp_") { - s.Got = -2 // flag for __imp_ - } - sym.sym = s - - return nil -} diff --git a/src/cmd/internal/ld/lib.go b/src/cmd/internal/ld/lib.go deleted file mode 100644 index a36cd0f8f4..0000000000 --- a/src/cmd/internal/ld/lib.go +++ /dev/null @@ -1,1946 +0,0 @@ -// Inferno utils/8l/asm.c -// http://code.google.com/p/inferno-os/source/browse/utils/8l/asm.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package ld - -import ( - "bufio" - "bytes" - "cmd/internal/obj" - "crypto/sha1" - "debug/elf" - "fmt" - "io" - "io/ioutil" - "log" - "os" - "os/exec" - "path/filepath" - "strings" -) - -// Data layout and relocation. - -// Derived from Inferno utils/6l/l.h -// http://code.google.com/p/inferno-os/source/browse/utils/6l/l.h -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -type Arch struct { - Thechar int - Ptrsize int - Intsize int - Regsize int - Funcalign int - Maxalign int - Minlc int - Dwarfregsp int - Dwarfreglr int - Linuxdynld string - Freebsddynld string - Netbsddynld string - Openbsddynld string - Dragonflydynld string - Solarisdynld string - Adddynrel func(*LSym, *Reloc) - Archinit func() - Archreloc func(*Reloc, *LSym, *int64) int - Archrelocvariant func(*Reloc, *LSym, int64) int64 - Asmb func() - Elfreloc1 func(*Reloc, int64) int - Elfsetupplt func() - Gentext func() - Machoreloc1 func(*Reloc, int64) int - PEreloc1 func(*Reloc, int64) bool - Lput func(uint32) - Wput func(uint16) - Vput func(uint64) -} - -type Rpath struct { - set bool - val string -} - -func (r *Rpath) Set(val string) error { - r.set = true - r.val = val - return nil -} - -func (r *Rpath) String() string { - return r.val -} - -var ( - Thearch Arch - datap *LSym - Debug [128]int - Lcsize int32 - rpath Rpath - Spsize int32 - Symsize int32 -) - -// Terrible but standard terminology. -// A segment describes a block of file to load into memory. -// A section further describes the pieces of that block for -// use in debuggers and such. - -const ( - MAXIO = 8192 - MINFUNC = 16 // minimum size for a function -) - -type Segment struct { - Rwx uint8 // permission as usual unix bits (5 = r-x etc) - Vaddr uint64 // virtual address - Length uint64 // length in memory - Fileoff uint64 // file offset - Filelen uint64 // length on disk - Sect *Section -} - -type Section struct { - Rwx uint8 - Extnum int16 - Align int32 - Name string - Vaddr uint64 - Length uint64 - Next *Section - Seg *Segment - Elfsect interface{} - Reloff uint64 - Rellen uint64 -} - -// DynlinkingGo returns whether we are producing Go code that can live -// in separate shared libraries linked together at runtime. -func DynlinkingGo() bool { - return Buildmode == BuildmodeShared || Linkshared -} - -var ( - Thestring string - Thelinkarch *LinkArch - outfile string - dynexp []*LSym - dynlib []string - ldflag []string - havedynamic int - Funcalign int - iscgo bool - elfglobalsymndx int - flag_installsuffix string - flag_race int - Buildmode BuildMode - Linkshared bool - tracksym string - interpreter string - tmpdir string - extld string - extldflags string - debug_s int // backup old value of debug['s'] - Ctxt *Link - HEADR int32 - HEADTYPE int32 - INITRND int32 - INITTEXT int64 - INITDAT int64 - INITENTRY string /* entry point */ - nerrors int - Linkmode int - liveness int64 -) - -// for dynexport field of LSym -const ( - CgoExportDynamic = 1 << 0 - CgoExportStatic = 1 << 1 -) - -var ( - Segtext Segment - Segrodata Segment - Segdata Segment - Segdwarf Segment -) - -/* set by call to mywhatsys() */ - -/* whence for ldpkg */ -const ( - FileObj = 0 + iota - ArchiveObj - Pkgdef -) - -var ( - headstring string - // buffered output - Bso obj.Biobuf -) - -var coutbuf struct { - *bufio.Writer - f *os.File -} - -const ( - // Whether to assume that the external linker is "gold" - // (http://sourceware.org/ml/binutils/2008-03/msg00162.html). - AssumeGoldLinker = 0 -) - -const ( - symname = "__.GOSYMDEF" - pkgname = "__.PKGDEF" -) - -var ( - // Set if we see an object compiled by the host compiler that is not - // from a package that is known to support internal linking mode. - externalobj = false - goroot string - goarch string - goos string - theline string -) - -func Lflag(arg string) { - Ctxt.Libdir = append(Ctxt.Libdir, arg) -} - -// A BuildMode indicates the sort of object we are building: -// "exe": build a main package and everything it imports into an executable. -// "c-shared": build a main package, plus all packages that it imports, into a -// single C shared library. The only callable symbols will be those functions -// marked as exported. -// "shared": combine all packages passed on the command line, and their -// dependencies, into a single shared library that will be used when -// building with the -linkshared option. -type BuildMode uint8 - -const ( - BuildmodeExe BuildMode = iota - BuildmodeCArchive - BuildmodeCShared - BuildmodeShared -) - -func (mode *BuildMode) Set(s string) error { - goos := obj.Getgoos() - goarch := obj.Getgoarch() - badmode := func() error { - return fmt.Errorf("buildmode %s not supported on %s/%s", s, goos, goarch) - } - switch s { - default: - return fmt.Errorf("invalid buildmode: %q", s) - case "exe": - *mode = BuildmodeExe - case "c-archive": - switch goos { - case "darwin", "linux": - default: - return badmode() - } - *mode = BuildmodeCArchive - case "c-shared": - if goarch != "amd64" && goarch != "arm" { - return badmode() - } - *mode = BuildmodeCShared - case "shared": - if goos != "linux" || goarch != "amd64" { - return badmode() - } - *mode = BuildmodeShared - } - return nil -} - -func (mode *BuildMode) String() string { - switch *mode { - case BuildmodeExe: - return "exe" - case BuildmodeCArchive: - return "c-archive" - case BuildmodeCShared: - return "c-shared" - case BuildmodeShared: - return "shared" - } - return fmt.Sprintf("BuildMode(%d)", uint8(*mode)) -} - -/* - * Unix doesn't like it when we write to a running (or, sometimes, - * recently run) binary, so remove the output file before writing it. - * On Windows 7, remove() can force a subsequent create() to fail. - * S_ISREG() does not exist on Plan 9. - */ -func mayberemoveoutfile() { - if fi, err := os.Lstat(outfile); err == nil && !fi.Mode().IsRegular() { - return - } - os.Remove(outfile) -} - -func libinit() { - Funcalign = Thearch.Funcalign - mywhatsys() // get goroot, goarch, goos - - // add goroot to the end of the libdir list. - suffix := "" - - suffixsep := "" - if flag_installsuffix != "" { - suffixsep = "_" - suffix = flag_installsuffix - } else if flag_race != 0 { - suffixsep = "_" - suffix = "race" - } - - Lflag(fmt.Sprintf("%s/pkg/%s_%s%s%s", goroot, goos, goarch, suffixsep, suffix)) - - mayberemoveoutfile() - f, err := os.OpenFile(outfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0775) - if err != nil { - Exitf("cannot create %s: %v", outfile, err) - } - - coutbuf.Writer = bufio.NewWriter(f) - coutbuf.f = f - - if INITENTRY == "" { - switch Buildmode { - case BuildmodeCShared, BuildmodeCArchive: - INITENTRY = fmt.Sprintf("_rt0_%s_%s_lib", goarch, goos) - case BuildmodeExe: - INITENTRY = fmt.Sprintf("_rt0_%s_%s", goarch, goos) - case BuildmodeShared: - // No INITENTRY for -buildmode=shared - default: - Diag("unknown INITENTRY for buildmode %v", Buildmode) - } - } - - if !DynlinkingGo() { - Linklookup(Ctxt, INITENTRY, 0).Type = obj.SXREF - } -} - -func Exitf(format string, a ...interface{}) { - fmt.Fprintf(os.Stderr, os.Args[0]+": "+format+"\n", a...) - if coutbuf.f != nil { - coutbuf.f.Close() - mayberemoveoutfile() - } - Exit(2) -} - -func errorexit() { - if coutbuf.f != nil { - if nerrors != 0 { - Cflush() - } - // For rmtemp run at atexit time on Windows. - if err := coutbuf.f.Close(); err != nil { - Exitf("close: %v", err) - } - } - - if nerrors != 0 { - if coutbuf.f != nil { - mayberemoveoutfile() - } - Exit(2) - } - - Exit(0) -} - -func loadinternal(name string) { - found := 0 - for i := 0; i < len(Ctxt.Libdir); i++ { - if Linkshared { - shlibname := fmt.Sprintf("%s/%s.shlibname", Ctxt.Libdir[i], name) - if Debug['v'] != 0 { - fmt.Fprintf(&Bso, "searching for %s.a in %s\n", name, shlibname) - } - if obj.Access(shlibname, obj.AEXIST) >= 0 { - addlibpath(Ctxt, "internal", "internal", "", name, shlibname) - found = 1 - break - } - } - pname := fmt.Sprintf("%s/%s.a", Ctxt.Libdir[i], name) - if Debug['v'] != 0 { - fmt.Fprintf(&Bso, "searching for %s.a in %s\n", name, pname) - } - if obj.Access(pname, obj.AEXIST) >= 0 { - addlibpath(Ctxt, "internal", "internal", pname, name, "") - found = 1 - break - } - } - - if found == 0 { - fmt.Fprintf(&Bso, "warning: unable to find %s.a\n", name) - } -} - -func loadlib() { - switch Buildmode { - case BuildmodeCShared: - s := Linklookup(Ctxt, "runtime.islibrary", 0) - s.Dupok = 1 - Adduint8(Ctxt, s, 1) - case BuildmodeCArchive: - s := Linklookup(Ctxt, "runtime.isarchive", 0) - s.Dupok = 1 - Adduint8(Ctxt, s, 1) - } - - loadinternal("runtime") - if Thearch.Thechar == '5' { - loadinternal("math") - } - if flag_race != 0 { - loadinternal("runtime/race") - } - - var i int - for i = 0; i < len(Ctxt.Library); i++ { - if Debug['v'] > 1 { - fmt.Fprintf(&Bso, "%5.2f autolib: %s (from %s)\n", obj.Cputime(), Ctxt.Library[i].File, Ctxt.Library[i].Objref) - } - iscgo = iscgo || Ctxt.Library[i].Pkg == "runtime/cgo" - if Ctxt.Library[i].Shlib != "" { - ldshlibsyms(Ctxt.Library[i].Shlib) - } else { - objfile(Ctxt.Library[i]) - } - } - - if Linkmode == LinkAuto { - if iscgo && externalobj { - Linkmode = LinkExternal - } else { - Linkmode = LinkInternal - } - - // Force external linking for android. - if goos == "android" { - Linkmode = LinkExternal - } - - // cgo on Darwin must use external linking - // we can always use external linking, but then there will be circular - // dependency problems when compiling natively (external linking requires - // runtime/cgo, runtime/cgo requires cmd/cgo, but cmd/cgo needs to be - // compiled using external linking.) - if (Thearch.Thechar == '5' || Thearch.Thechar == '7') && HEADTYPE == obj.Hdarwin && iscgo { - Linkmode = LinkExternal - } - } - - // cmd/7l doesn't support cgo internal linking - // This is https://golang.org/issue/10373. - if iscgo && goarch == "arm64" { - Linkmode = LinkExternal - } - - if Linkmode == LinkExternal && !iscgo { - // This indicates a user requested -linkmode=external. - // The startup code uses an import of runtime/cgo to decide - // whether to initialize the TLS. So give it one. This could - // be handled differently but it's an unusual case. - loadinternal("runtime/cgo") - - if i < len(Ctxt.Library) { - if Ctxt.Library[i].Shlib != "" { - ldshlibsyms(Ctxt.Library[i].Shlib) - } else { - if DynlinkingGo() { - Exitf("cannot implicitly include runtime/cgo in a shared library") - } - objfile(Ctxt.Library[i]) - } - } - } - - if Linkmode == LinkInternal { - // Drop all the cgo_import_static declarations. - // Turns out we won't be needing them. - for s := Ctxt.Allsym; s != nil; s = s.Allsym { - if s.Type == obj.SHOSTOBJ { - // If a symbol was marked both - // cgo_import_static and cgo_import_dynamic, - // then we want to make it cgo_import_dynamic - // now. - if s.Extname != "" && s.Dynimplib != "" && s.Cgoexport == 0 { - s.Type = obj.SDYNIMPORT - } else { - s.Type = 0 - } - } - } - } - - tlsg := Linklookup(Ctxt, "runtime.tlsg", 0) - - // For most ports, runtime.tlsg is a placeholder symbol for TLS - // relocation. However, the Android and Darwin arm ports need it - // to be a real variable. - // - // TODO(crawshaw): android should require leaving the tlsg->type - // alone (as the runtime-provided SNOPTRBSS) just like darwin/arm. - // But some other part of the linker is expecting STLSBSS. - if tlsg.Type != obj.SDYNIMPORT && (goos != "darwin" || Thearch.Thechar != '5') { - tlsg.Type = obj.STLSBSS - } - tlsg.Size = int64(Thearch.Ptrsize) - tlsg.Reachable = true - Ctxt.Tlsg = tlsg - - // Now that we know the link mode, trim the dynexp list. - x := CgoExportDynamic - - if Linkmode == LinkExternal { - x = CgoExportStatic - } - w := 0 - for i := 0; i < len(dynexp); i++ { - if int(dynexp[i].Cgoexport)&x != 0 { - dynexp[w] = dynexp[i] - w++ - } - } - dynexp = dynexp[:w] - - // In internal link mode, read the host object files. - if Linkmode == LinkInternal { - hostobjs() - } else { - hostlinksetup() - } - - // We've loaded all the code now. - // If there are no dynamic libraries needed, gcc disables dynamic linking. - // Because of this, glibc's dynamic ELF loader occasionally (like in version 2.13) - // assumes that a dynamic binary always refers to at least one dynamic library. - // Rather than be a source of test cases for glibc, disable dynamic linking - // the same way that gcc would. - // - // Exception: on OS X, programs such as Shark only work with dynamic - // binaries, so leave it enabled on OS X (Mach-O) binaries. - // Also leave it enabled on Solaris which doesn't support - // statically linked binaries. - if Buildmode == BuildmodeExe && havedynamic == 0 && HEADTYPE != obj.Hdarwin && HEADTYPE != obj.Hsolaris { - Debug['d'] = 1 - } - - importcycles() -} - -/* - * look for the next file in an archive. - * adapted from libmach. - */ -func nextar(bp *obj.Biobuf, off int64, a *ArHdr) int64 { - if off&1 != 0 { - off++ - } - obj.Bseek(bp, off, 0) - buf := make([]byte, SAR_HDR) - if n := obj.Bread(bp, buf); n < len(buf) { - if n >= 0 { - return 0 - } - return -1 - } - - a.name = artrim(buf[0:16]) - a.date = artrim(buf[16:28]) - a.uid = artrim(buf[28:34]) - a.gid = artrim(buf[34:40]) - a.mode = artrim(buf[40:48]) - a.size = artrim(buf[48:58]) - a.fmag = artrim(buf[58:60]) - - arsize := atolwhex(a.size) - if arsize&1 != 0 { - arsize++ - } - return int64(arsize) + SAR_HDR -} - -func objfile(lib *Library) { - pkg := pathtoprefix(lib.Pkg) - - if Debug['v'] > 1 { - 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(lib.File) - if err != nil { - Exitf("cannot open file %s: %v", lib.File, err) - } - - magbuf := make([]byte, len(ARMAG)) - if obj.Bread(f, magbuf) != len(magbuf) || !strings.HasPrefix(string(magbuf), ARMAG) { - /* load it as a regular file */ - l := obj.Bseek(f, 0, 2) - - obj.Bseek(f, 0, 0) - ldobj(f, pkg, l, lib.File, lib.File, FileObj) - obj.Bterm(f) - - return - } - - /* skip over optional __.GOSYMDEF and process __.PKGDEF */ - off := obj.Boffset(f) - - var arhdr ArHdr - l := nextar(f, off, &arhdr) - var pname string - if l <= 0 { - Diag("%s: short read on archive file symbol header", lib.File) - goto out - } - - if strings.HasPrefix(arhdr.name, symname) { - off += l - l = nextar(f, off, &arhdr) - if l <= 0 { - 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", 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), lib.File, Pkgdef) - } - - /* - * load all the object files from the archive now. - * this gives us sequential file access and keeps us - * from needing to come back later to pick up more - * objects. it breaks the usual C archive model, but - * this is Go, not C. the common case in Go is that - * we need to load all the objects, and then we throw away - * the individual symbols that are unused. - * - * loading every object will also make it possible to - * load foreign objects not referenced by __.GOSYMDEF. - */ - for { - l = nextar(f, off, &arhdr) - if l == 0 { - break - } - if l < 0 { - Exitf("%s: malformed archive", lib.File) - } - - off += l - - pname = fmt.Sprintf("%s(%s)", lib.File, arhdr.name) - l = atolwhex(arhdr.size) - ldobj(f, pkg, l, pname, lib.File, ArchiveObj) - } - -out: - obj.Bterm(f) -} - -type Hostobj struct { - ld func(*obj.Biobuf, string, int64, string) - pkg string - pn string - file string - off int64 - length int64 -} - -var hostobj []Hostobj - -// These packages can use internal linking mode. -// Others trigger external mode. -var internalpkg = []string{ - "crypto/x509", - "net", - "os/user", - "runtime/cgo", - "runtime/race", -} - -func ldhostobj(ld func(*obj.Biobuf, string, int64, string), f *obj.Biobuf, pkg string, length int64, pn string, file string) { - isinternal := false - for i := 0; i < len(internalpkg); i++ { - if pkg == internalpkg[i] { - isinternal = true - break - } - } - - // DragonFly declares errno with __thread, which results in a symbol - // type of R_386_TLS_GD or R_X86_64_TLSGD. The Go linker does not - // currently know how to handle TLS relocations, hence we have to - // force external linking for any libraries that link in code that - // uses errno. This can be removed if the Go linker ever supports - // these relocation types. - if HEADTYPE == obj.Hdragonfly { - if pkg == "net" || pkg == "os/user" { - isinternal = false - } - } - - if !isinternal { - externalobj = true - } - - hostobj = append(hostobj, Hostobj{}) - h := &hostobj[len(hostobj)-1] - h.ld = ld - h.pkg = pkg - h.pn = pn - h.file = file - h.off = obj.Boffset(f) - h.length = length -} - -func hostobjs() { - var f *obj.Biobuf - var h *Hostobj - - for i := 0; i < len(hostobj); i++ { - h = &hostobj[i] - var err error - f, err = obj.Bopenr(h.file) - if f == nil { - Exitf("cannot reopen %s: %v", h.pn, err) - } - - obj.Bseek(f, h.off, 0) - h.ld(f, h.pkg, h.length, h.pn) - obj.Bterm(f) - } -} - -// provided by lib9 - -func rmtemp() { - os.RemoveAll(tmpdir) -} - -func hostlinksetup() { - if Linkmode != LinkExternal { - return - } - - // create temporary directory and arrange cleanup - if tmpdir == "" { - dir, err := ioutil.TempDir("", "go-link-") - if err != nil { - log.Fatal(err) - } - tmpdir = dir - AtExit(rmtemp) - } - - // change our output to temporary object file - coutbuf.f.Close() - - p := fmt.Sprintf("%s/go.o", tmpdir) - var err error - f, err := os.OpenFile(p, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0775) - if err != nil { - Exitf("cannot create %s: %v", p, err) - } - - coutbuf.Writer = bufio.NewWriter(f) - coutbuf.f = f -} - -// hostobjCopy creates a copy of the object files in hostobj in a -// temporary directory. -func hostobjCopy() (paths []string) { - for i, h := range hostobj { - f, err := os.Open(h.file) - if err != nil { - Exitf("cannot reopen %s: %v", h.pn, err) - } - if _, err := f.Seek(h.off, 0); err != nil { - Exitf("cannot seek %s: %v", h.pn, err) - } - - p := fmt.Sprintf("%s/%06d.o", tmpdir, i) - paths = append(paths, p) - w, err := os.Create(p) - if err != nil { - Exitf("cannot create %s: %v", p, err) - } - if _, err := io.CopyN(w, f, h.length); err != nil { - Exitf("cannot write %s: %v", p, err) - } - if err := w.Close(); err != nil { - Exitf("cannot close %s: %v", p, err) - } - } - return paths -} - -// archive builds a .a archive from the hostobj object files. -func archive() { - if Buildmode != BuildmodeCArchive { - return - } - - os.Remove(outfile) - argv := []string{"ar", "-q", "-c", outfile} - argv = append(argv, hostobjCopy()...) - argv = append(argv, fmt.Sprintf("%s/go.o", tmpdir)) - - if Debug['v'] != 0 { - fmt.Fprintf(&Bso, "archive: %s\n", strings.Join(argv, " ")) - Bso.Flush() - } - - if out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput(); err != nil { - Exitf("running %s failed: %v\n%s", argv[0], err, out) - } -} - -func hostlink() { - if Linkmode != LinkExternal || nerrors > 0 { - return - } - if Buildmode == BuildmodeCArchive { - return - } - - if extld == "" { - extld = "gcc" - } - - var argv []string - argv = append(argv, extld) - switch Thearch.Thechar { - case '8': - argv = append(argv, "-m32") - - case '6', '9': - argv = append(argv, "-m64") - - case '5': - argv = append(argv, "-marm") - - case '7': - // nothing needed - } - - if Debug['s'] == 0 && debug_s == 0 { - argv = append(argv, "-gdwarf-2") - } else { - argv = append(argv, "-s") - } - - if HEADTYPE == obj.Hdarwin { - argv = append(argv, "-Wl,-no_pie,-pagezero_size,4000000,-headerpad,1144") - } - if HEADTYPE == obj.Hopenbsd { - argv = append(argv, "-Wl,-nopie") - } - if HEADTYPE == obj.Hwindows { - if headstring == "windowsgui" { - argv = append(argv, "-mwindows") - } else { - argv = append(argv, "-mconsole") - } - } - - if Iself && AssumeGoldLinker != 0 /*TypeKind(100016)*/ { - argv = append(argv, "-Wl,--rosegment") - } - - switch Buildmode { - case BuildmodeCShared: - argv = append(argv, "-Wl,-Bsymbolic") - argv = append(argv, "-shared") - case BuildmodeShared: - // TODO(mwhudson): unless you do this, dynamic relocations fill - // out the findfunctab table and for some reason shared libraries - // and the executable both define a main function and putting the - // address of executable's main into the shared libraries - // findfunctab violates the assumptions of the runtime. TBH, I - // think we may well end up wanting to use -Bsymbolic here - // anyway. - argv = append(argv, "-Wl,-Bsymbolic-functions") - argv = append(argv, "-shared") - } - - if Linkshared && Iself { - // We force all symbol resolution to be done at program startup - // because lazy PLT resolution can use large amounts of stack at - // times we cannot allow it to do so. - argv = append(argv, "-Wl,-znow") - } - - argv = append(argv, "-o") - argv = append(argv, outfile) - - if rpath.val != "" { - argv = append(argv, fmt.Sprintf("-Wl,-rpath,%s", rpath.val)) - } - - // Force global symbols to be exported for dlopen, etc. - if Iself { - argv = append(argv, "-rdynamic") - } - - if strings.Contains(argv[0], "clang") { - argv = append(argv, "-Qunused-arguments") - } - - argv = append(argv, hostobjCopy()...) - 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) - } - base = strings.TrimSuffix(base, ".so") - base = strings.TrimPrefix(base, "lib") - argv = append(argv, "-l"+base) - } - } - - argv = append(argv, ldflag...) - - for _, p := range strings.Fields(extldflags) { - argv = append(argv, p) - - // clang, unlike GCC, passes -rdynamic to the linker - // even when linking with -static, causing a linker - // error when using GNU ld. So take out -rdynamic if - // we added it. We do it in this order, rather than - // only adding -rdynamic later, so that -extldflags - // can override -rdynamic without using -static. - if Iself && p == "-static" { - for i := range argv { - if argv[i] == "-rdynamic" { - argv[i] = "-static" - } - } - } - } - if HEADTYPE == obj.Hwindows { - argv = append(argv, peimporteddlls()...) - } - - if Debug['v'] != 0 { - fmt.Fprintf(&Bso, "host link:") - for _, v := range argv { - fmt.Fprintf(&Bso, " %q", v) - } - fmt.Fprintf(&Bso, "\n") - Bso.Flush() - } - - 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) { - eof := obj.Boffset(f) + length - - start := obj.Boffset(f) - c1 := obj.Bgetc(f) - c2 := obj.Bgetc(f) - c3 := obj.Bgetc(f) - c4 := obj.Bgetc(f) - obj.Bseek(f, start, 0) - - magic := uint32(c1)<<24 | uint32(c2)<<16 | uint32(c3)<<8 | uint32(c4) - if magic == 0x7f454c46 { // \x7F E L F - ldhostobj(ldelf, f, pkg, length, pn, file) - return - } - - if magic&^1 == 0xfeedface || magic&^0x01000000 == 0xcefaedfe { - ldhostobj(ldmacho, f, pkg, length, pn, file) - return - } - - if c1 == 0x4c && c2 == 0x01 || c1 == 0x64 && c2 == 0x86 { - ldhostobj(ldpe, f, pkg, length, pn, file) - return - } - - /* check the header */ - line := obj.Brdline(f, '\n') - if line == "" { - if obj.Blinelen(f) > 0 { - Diag("%s: not an object file", pn) - return - } - Diag("truncated object file: %s", pn) - return - } - - if !strings.HasPrefix(line, "go object ") { - if strings.HasSuffix(pn, ".go") { - Exitf("%cl: input %s is not .%c file (use %cg to compile .go files)", Thearch.Thechar, pn, Thearch.Thechar, Thearch.Thechar) - } - - if line == Thestring { - // old header format: just $GOOS - Diag("%s: stale object file", pn) - return - } - - Diag("%s: not an object file", pn) - return - } - - // First, check that the basic goos, goarch, and version match. - t := fmt.Sprintf("%s %s %s ", goos, obj.Getgoarch(), obj.Getgoversion()) - - line = strings.TrimRight(line, "\n") - if !strings.HasPrefix(line[10:]+" ", t) && Debug['f'] == 0 { - Diag("%s: object is [%s] expected [%s]", pn, line[10:], t) - return - } - - // Second, check that longer lines match each other exactly, - // so that the Go compiler and write additional information - // that must be the same from run to run. - if len(line) >= len(t)+10 { - if theline == "" { - theline = line[10:] - } else if theline != line[10:] { - Diag("%s: object is [%s] expected [%s]", pn, line[10:], theline) - return - } - } - - /* skip over exports and other info -- ends with \n!\n */ - import0 := obj.Boffset(f) - - c1 = '\n' // the last line ended in \n - c2 = obj.Bgetc(f) - c3 = obj.Bgetc(f) - for c1 != '\n' || c2 != '!' || c3 != '\n' { - c1 = c2 - c2 = c3 - c3 = obj.Bgetc(f) - if c3 == obj.Beof { - Diag("truncated object file: %s", pn) - return - } - } - - import1 := obj.Boffset(f) - - obj.Bseek(f, import0, 0) - ldpkg(f, pkg, import1-import0-2, pn, whence) // -2 for !\n - obj.Bseek(f, import1, 0) - - 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 := "" - for _, libdir := range Ctxt.Libdir { - libpath = filepath.Join(libdir, shlib) - if _, err := os.Stat(libpath); err == nil { - found = true - break - } - } - if !found { - Diag("cannot find shared library: %s", shlib) - return - } - for _, processedlib := range Ctxt.Shlibs { - if processedlib.Path == libpath { - return - } - } - if Ctxt.Debugvlog > 1 && Ctxt.Bso != nil { - fmt.Fprintf(Ctxt.Bso, "%5.2f ldshlibsyms: found library with name %s at %s\n", obj.Cputime(), shlib, libpath) - Ctxt.Bso.Flush() - } - - f, err := elf.Open(libpath) - if err != nil { - Diag("cannot open shared library: %s", libpath) - return - } - defer f.Close() - syms, err := f.Symbols() - 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{} - 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 - } - 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) - } - if s.Name == "go.link.abihashbytes" { - hash = readelfsymboldata(f, &s) - } - if elf.ST_BIND(s.Info) != elf.STB_GLOBAL { - continue - } - lsym := Linklookup(Ctxt, s.Name, 0) - if lsym.Type != 0 && lsym.Dupok == 0 { - Diag( - "Found duplicate symbol %s reading from %s, first found in %s", - s.Name, shlib, lsym.File) - } - 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 { - 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 - // list. - var last *LSym - - for s := Ctxt.Textp; s != nil; s = s.Next { - if s.Type == obj.SDYNIMPORT { - continue - } - - if last == nil { - Ctxt.Textp = s - } else { - last.Next = s - } - last = s - } - - if last == nil { - Ctxt.Textp = nil - Ctxt.Etextp = nil - } else { - last.Next = nil - Ctxt.Etextp = last - } - - Ctxt.Shlibs = append(Ctxt.Shlibs, Shlib{Path: libpath, Hash: hash}) -} - -func mywhatsys() { - goroot = obj.Getgoroot() - goos = obj.Getgoos() - goarch = obj.Getgoarch() - - if !strings.HasPrefix(goarch, Thestring) { - log.Fatalf("cannot use %cc with GOARCH=%s", Thearch.Thechar, goarch) - } -} - -// Copied from ../gc/subr.c:/^pathtoprefix; must stay in sync. -/* - * Convert raw string to the prefix that will be used in the symbol table. - * Invalid bytes turn into %xx. Right now the only bytes that need - * escaping are %, ., and ", but we escape all control characters too. - * - * If you edit this, edit ../gc/subr.c:/^pathtoprefix too. - * If you edit this, edit ../../debug/goobj/read.go:/importPathToPrefix too. - */ -func pathtoprefix(s string) string { - slash := strings.LastIndex(s, "/") - for i := 0; i < len(s); i++ { - c := s[i] - if c <= ' ' || i >= slash && c == '.' || c == '%' || c == '"' || c >= 0x7F { - var buf bytes.Buffer - for i := 0; i < len(s); i++ { - c := s[i] - if c <= ' ' || i >= slash && c == '.' || c == '%' || c == '"' || c >= 0x7F { - fmt.Fprintf(&buf, "%%%02x", c) - continue - } - buf.WriteByte(c) - } - return buf.String() - } - } - return s -} - -func addsection(seg *Segment, name string, rwx int) *Section { - var l **Section - - for l = &seg.Sect; *l != nil; l = &(*l).Next { - } - sect := new(Section) - sect.Rwx = uint8(rwx) - sect.Name = name - sect.Seg = seg - sect.Align = int32(Thearch.Ptrsize) // everything is at least pointer-aligned - *l = sect - return sect -} - -func Le16(b []byte) uint16 { - return uint16(b[0]) | uint16(b[1])<<8 -} - -func Le32(b []byte) uint32 { - return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 -} - -func Le64(b []byte) uint64 { - return uint64(Le32(b)) | uint64(Le32(b[4:]))<<32 -} - -func Be16(b []byte) uint16 { - return uint16(b[0])<<8 | uint16(b[1]) -} - -func Be32(b []byte) uint32 { - return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]) -} - -func Be64(b []byte) uint64 { - return uint64(Be32(b))<<32 | uint64(Be32(b[4:])) -} - -type Chain struct { - sym *LSym - up *Chain - limit int // limit on entry to sym -} - -var ( - morestack *LSym - newstack *LSym -) - -// TODO: Record enough information in new object files to -// allow stack checks here. - -func haslinkregister() bool { - return Thearch.Thechar == '5' || Thearch.Thechar == '9' || Thearch.Thechar == '7' -} - -func callsize() int { - if haslinkregister() { - return 0 - } - return Thearch.Regsize -} - -func dostkcheck() { - var ch Chain - - morestack = Linklookup(Ctxt, "runtime.morestack", 0) - newstack = Linklookup(Ctxt, "runtime.newstack", 0) - - // Every splitting function ensures that there are at least StackLimit - // bytes available below SP when the splitting prologue finishes. - // If the splitting function calls F, then F begins execution with - // at least StackLimit - callsize() bytes available. - // Check that every function behaves correctly with this amount - // of stack, following direct calls in order to piece together chains - // of non-splitting functions. - ch.up = nil - - ch.limit = obj.StackLimit - callsize() - - // Check every function, but do the nosplit functions in a first pass, - // to make the printed failure chains as short as possible. - for s := Ctxt.Textp; s != nil; s = s.Next { - // runtime.racesymbolizethunk is called from gcc-compiled C - // code running on the operating system thread stack. - // It uses more than the usual amount of stack but that's okay. - if s.Name == "runtime.racesymbolizethunk" { - continue - } - - if s.Nosplit != 0 { - Ctxt.Cursym = s - ch.sym = s - stkcheck(&ch, 0) - } - } - - for s := Ctxt.Textp; s != nil; s = s.Next { - if s.Nosplit == 0 { - Ctxt.Cursym = s - ch.sym = s - stkcheck(&ch, 0) - } - } -} - -func stkcheck(up *Chain, depth int) int { - limit := up.limit - s := up.sym - - // Don't duplicate work: only need to consider each - // function at top of safe zone once. - if limit == obj.StackLimit-callsize() { - if s.Stkcheck != 0 { - return 0 - } - s.Stkcheck = 1 - } - - if depth > 100 { - Diag("nosplit stack check too deep") - stkbroke(up, 0) - return -1 - } - - if s.External != 0 || s.Pcln == nil { - // external function. - // should never be called directly. - // only diagnose the direct caller. - // TODO(mwhudson): actually think about this. - if depth == 1 && s.Type != obj.SXREF && !DynlinkingGo() { - Diag("call to external function %s", s.Name) - } - return -1 - } - - if limit < 0 { - stkbroke(up, limit) - return -1 - } - - // morestack looks like it calls functions, - // but it switches the stack pointer first. - if s == morestack { - return 0 - } - - var ch Chain - ch.up = up - - // Walk through sp adjustments in function, consuming relocs. - ri := 0 - - endr := len(s.R) - var ch1 Chain - var pcsp Pciter - var r *Reloc - for pciterinit(Ctxt, &pcsp, &s.Pcln.Pcsp); pcsp.done == 0; pciternext(&pcsp) { - // pcsp.value is in effect for [pcsp.pc, pcsp.nextpc). - - // Check stack size in effect for this span. - if int32(limit)-pcsp.value < 0 { - stkbroke(up, int(int32(limit)-pcsp.value)) - return -1 - } - - // Process calls in this span. - for ; ri < endr && uint32(s.R[ri].Off) < pcsp.nextpc; ri++ { - r = &s.R[ri] - switch r.Type { - // Direct call. - case obj.R_CALL, obj.R_CALLARM, obj.R_CALLARM64, obj.R_CALLPOWER: - ch.limit = int(int32(limit) - pcsp.value - int32(callsize())) - - ch.sym = r.Sym - if stkcheck(&ch, depth+1) < 0 { - return -1 - } - - // If this is a call to morestack, we've just raised our limit back - // to StackLimit beyond the frame size. - if strings.HasPrefix(r.Sym.Name, "runtime.morestack") { - limit = int(obj.StackLimit + s.Locals) - if haslinkregister() { - limit += Thearch.Regsize - } - } - - // Indirect call. Assume it is a call to a splitting function, - // so we have to make sure it can call morestack. - // Arrange the data structures to report both calls, so that - // if there is an error, stkprint shows all the steps involved. - case obj.R_CALLIND: - ch.limit = int(int32(limit) - pcsp.value - int32(callsize())) - - ch.sym = nil - ch1.limit = ch.limit - callsize() // for morestack in called prologue - ch1.up = &ch - ch1.sym = morestack - if stkcheck(&ch1, depth+2) < 0 { - return -1 - } - } - } - } - - return 0 -} - -func stkbroke(ch *Chain, limit int) { - Diag("nosplit stack overflow") - stkprint(ch, limit) -} - -func stkprint(ch *Chain, limit int) { - var name string - - if ch.sym != nil { - name = ch.sym.Name - } else { - name = "function pointer" - } - - if ch.up == nil { - // top of chain. ch->sym != nil. - if ch.sym.Nosplit != 0 { - fmt.Printf("\t%d\tassumed on entry to %s\n", ch.limit, name) - } else { - fmt.Printf("\t%d\tguaranteed after split check in %s\n", ch.limit, name) - } - } else { - stkprint(ch.up, ch.limit+callsize()) - if !haslinkregister() { - fmt.Printf("\t%d\ton entry to %s\n", ch.limit, name) - } - } - - if ch.limit != limit { - fmt.Printf("\t%d\tafter %s uses %d\n", limit, name, ch.limit-limit) - } -} - -func Yconv(s *LSym) string { - var fp string - - if s == nil { - fp += fmt.Sprintf("") - } else { - fmt_ := "" - fmt_ += fmt.Sprintf("%s @0x%08x [%d]", s.Name, int64(s.Value), int64(s.Size)) - for i := 0; int64(i) < s.Size; i++ { - if i%8 == 0 { - fmt_ += fmt.Sprintf("\n\t0x%04x ", i) - } - fmt_ += fmt.Sprintf("%02x ", s.P[i]) - } - - fmt_ += fmt.Sprintf("\n") - for i := 0; i < len(s.R); i++ { - fmt_ += fmt.Sprintf("\t0x%04x[%x] %d %s[%x]\n", s.R[i].Off, s.R[i].Siz, s.R[i].Type, s.R[i].Sym.Name, int64(s.R[i].Add)) - } - - str := fmt_ - fp += str - } - - return fp -} - -func Cflush() { - if err := coutbuf.Writer.Flush(); err != nil { - Exitf("flushing %s: %v", coutbuf.f.Name(), err) - } -} - -func Cpos() int64 { - off, err := coutbuf.f.Seek(0, 1) - if err != nil { - Exitf("seeking in output [0, 1]: %v", err) - } - return off + int64(coutbuf.Buffered()) -} - -func Cseek(p int64) { - Cflush() - if _, err := coutbuf.f.Seek(p, 0); err != nil { - Exitf("seeking in output [0, 1]: %v", err) - } -} - -func Cwrite(p []byte) { - coutbuf.Write(p) -} - -func Cput(c uint8) { - coutbuf.WriteByte(c) -} - -func usage() { - fmt.Fprintf(os.Stderr, "usage: %cl [options] obj.%c\n", Thearch.Thechar, Thearch.Thechar) - obj.Flagprint(2) - Exit(2) -} - -func setheadtype(s string) { - h := headtype(s) - if h < 0 { - Exitf("unknown header type -H %s", s) - } - - headstring = s - HEADTYPE = int32(headtype(s)) -} - -func setinterp(s string) { - Debug['I'] = 1 // denote cmdline interpreter override - interpreter = s -} - -func doversion() { - Exitf("version %s", obj.Getgoversion()) -} - -func genasmsym(put func(*LSym, string, int, int64, int64, int, *LSym)) { - // These symbols won't show up in the first loop below because we - // skip STEXT symbols. Normal STEXT symbols are emitted by walking textp. - s := Linklookup(Ctxt, "runtime.text", 0) - - if s.Type == obj.STEXT { - put(s, s.Name, 'T', s.Value, s.Size, int(s.Version), nil) - } - s = Linklookup(Ctxt, "runtime.etext", 0) - if s.Type == obj.STEXT { - put(s, s.Name, 'T', s.Value, s.Size, int(s.Version), nil) - } - - for s := Ctxt.Allsym; s != nil; s = s.Allsym { - if s.Hide != 0 || (s.Name[0] == '.' && s.Version == 0 && s.Name != ".rathole") { - continue - } - switch s.Type & obj.SMASK { - case obj.SCONST, - obj.SRODATA, - obj.SSYMTAB, - obj.SPCLNTAB, - obj.SINITARR, - obj.SDATA, - obj.SNOPTRDATA, - obj.SELFROSECT, - obj.SMACHOGOT, - obj.STYPE, - obj.SSTRING, - obj.SGOSTRING, - obj.SGOFUNC, - obj.SWINDOWS: - if !s.Reachable { - continue - } - put(s, s.Name, 'D', Symaddr(s), s.Size, int(s.Version), s.Gotype) - - case obj.SBSS, obj.SNOPTRBSS: - if !s.Reachable { - continue - } - if len(s.P) > 0 { - Diag("%s should not be bss (size=%d type=%d special=%d)", s.Name, int(len(s.P)), s.Type, s.Special) - } - put(s, s.Name, 'B', Symaddr(s), s.Size, int(s.Version), s.Gotype) - - case obj.SFILE: - put(nil, s.Name, 'f', s.Value, 0, int(s.Version), nil) - - case obj.SHOSTOBJ: - if HEADTYPE == obj.Hwindows || Iself { - put(s, s.Name, 'U', s.Value, 0, int(s.Version), nil) - } - - case obj.SDYNIMPORT: - if !s.Reachable { - continue - } - put(s, s.Extname, 'U', 0, 0, int(s.Version), nil) - - case obj.STLSBSS: - if Linkmode == LinkExternal && HEADTYPE != obj.Hopenbsd { - var type_ int - if goos == "android" { - type_ = 'B' - } else { - type_ = 't' - } - put(s, s.Name, type_, Symaddr(s), s.Size, int(s.Version), s.Gotype) - } - } - } - - var a *Auto - var off int32 - for s := Ctxt.Textp; s != nil; s = s.Next { - put(s, s.Name, 'T', s.Value, s.Size, int(s.Version), s.Gotype) - - // NOTE(ality): acid can't produce a stack trace without .frame symbols - put(nil, ".frame", 'm', int64(s.Locals)+int64(Thearch.Ptrsize), 0, 0, nil) - - for a = s.Autom; a != nil; a = a.Link { - // Emit a or p according to actual offset, even if label is wrong. - // This avoids negative offsets, which cannot be encoded. - if a.Name != obj.A_AUTO && a.Name != obj.A_PARAM { - continue - } - - // compute offset relative to FP - if a.Name == obj.A_PARAM { - off = a.Aoffset - } else { - off = a.Aoffset - int32(Thearch.Ptrsize) - } - - // FP - if off >= 0 { - put(nil, a.Asym.Name, 'p', int64(off), 0, 0, a.Gotype) - continue - } - - // SP - if off <= int32(-Thearch.Ptrsize) { - put(nil, a.Asym.Name, 'a', -(int64(off) + int64(Thearch.Ptrsize)), 0, 0, a.Gotype) - continue - } - } - } - - // Otherwise, off is addressing the saved program counter. - // Something underhanded is going on. Say nothing. - if Debug['v'] != 0 || Debug['n'] != 0 { - fmt.Fprintf(&Bso, "%5.2f symsize = %d\n", obj.Cputime(), uint32(Symsize)) - } - Bso.Flush() -} - -func Symaddr(s *LSym) int64 { - if !s.Reachable { - Diag("unreachable symbol in symaddr - %s", s.Name) - } - return s.Value -} - -func xdefine(p string, t int, v int64) { - s := Linklookup(Ctxt, p, 0) - s.Type = int16(t) - s.Value = v - s.Reachable = true - s.Special = 1 - s.Local = true -} - -func datoff(addr int64) int64 { - if uint64(addr) >= Segdata.Vaddr { - return int64(uint64(addr) - Segdata.Vaddr + Segdata.Fileoff) - } - if uint64(addr) >= Segtext.Vaddr { - return int64(uint64(addr) - Segtext.Vaddr + Segtext.Fileoff) - } - Diag("datoff %#x", addr) - return 0 -} - -func Entryvalue() int64 { - a := INITENTRY - if a[0] >= '0' && a[0] <= '9' { - return atolwhex(a) - } - s := Linklookup(Ctxt, a, 0) - if s.Type == 0 { - return INITTEXT - } - if s.Type != obj.STEXT { - Diag("entry not text: %s", s.Name) - } - return s.Value -} - -func undefsym(s *LSym) { - var r *Reloc - - Ctxt.Cursym = s - for i := 0; i < len(s.R); i++ { - r = &s.R[i] - if r.Sym == nil { // happens for some external ARM relocs - continue - } - if r.Sym.Type == obj.Sxxx || r.Sym.Type == obj.SXREF { - Diag("undefined: %s", r.Sym.Name) - } - if !r.Sym.Reachable { - Diag("use of unreachable symbol: %s", r.Sym.Name) - } - } -} - -func undef() { - for s := Ctxt.Textp; s != nil; s = s.Next { - undefsym(s) - } - for s := datap; s != nil; s = s.Next { - undefsym(s) - } - if nerrors > 0 { - errorexit() - } -} - -func callgraph() { - if Debug['c'] == 0 { - return - } - - var i int - var r *Reloc - for s := Ctxt.Textp; s != nil; s = s.Next { - for i = 0; i < len(s.R); i++ { - r = &s.R[i] - if r.Sym == nil { - continue - } - if (r.Type == obj.R_CALL || r.Type == obj.R_CALLARM || r.Type == obj.R_CALLPOWER) && r.Sym.Type == obj.STEXT { - fmt.Fprintf(&Bso, "%s calls %s\n", s.Name, r.Sym.Name) - } - } - } -} - -func Diag(format string, args ...interface{}) { - tn := "" - sep := "" - if Ctxt.Cursym != nil { - tn = Ctxt.Cursym.Name - sep = ": " - } - fmt.Printf("%s%s%s\n", tn, sep, fmt.Sprintf(format, args...)) - - nerrors++ - if nerrors > 20 { - Exitf("too many errors") - } -} - -func checkgo() { - if Debug['C'] == 0 { - return - } - - // TODO(rsc,khr): Eventually we want to get to no Go-called C functions at all, - // which would simplify this logic quite a bit. - - // Mark every Go-called C function with cfunc=2, recursively. - var changed int - var i int - var r *Reloc - var s *LSym - for { - changed = 0 - for s = Ctxt.Textp; s != nil; s = s.Next { - if s.Cfunc == 0 || (s.Cfunc == 2 && s.Nosplit != 0) { - for i = 0; i < len(s.R); i++ { - r = &s.R[i] - if r.Sym == nil { - continue - } - if (r.Type == obj.R_CALL || r.Type == obj.R_CALLARM) && r.Sym.Type == obj.STEXT { - if r.Sym.Cfunc == 1 { - changed = 1 - r.Sym.Cfunc = 2 - } - } - } - } - } - if changed == 0 { - break - } - } - - // Complain about Go-called C functions that can split the stack - // (that can be preempted for garbage collection or trigger a stack copy). - for s := Ctxt.Textp; s != nil; s = s.Next { - if s.Cfunc == 0 || (s.Cfunc == 2 && s.Nosplit != 0) { - for i = 0; i < len(s.R); i++ { - r = &s.R[i] - if r.Sym == nil { - continue - } - if (r.Type == obj.R_CALL || r.Type == obj.R_CALLARM) && r.Sym.Type == obj.STEXT { - if s.Cfunc == 0 && r.Sym.Cfunc == 2 && r.Sym.Nosplit == 0 { - fmt.Printf("Go %s calls C %s\n", s.Name, r.Sym.Name) - } else if s.Cfunc == 2 && s.Nosplit != 0 && r.Sym.Nosplit == 0 { - fmt.Printf("Go calls C %s calls %s\n", s.Name, r.Sym.Name) - } - } - } - } - } -} - -func Rnd(v int64, r int64) int64 { - if r <= 0 { - return v - } - v += r - 1 - c := v % r - if c < 0 { - c += r - } - v -= c - return v -} diff --git a/src/cmd/internal/ld/link.go b/src/cmd/internal/ld/link.go deleted file mode 100644 index 3098147819..0000000000 --- a/src/cmd/internal/ld/link.go +++ /dev/null @@ -1,226 +0,0 @@ -// Derived from Inferno utils/6l/l.h and related files. -// http://code.google.com/p/inferno-os/source/browse/utils/6l/l.h -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package ld - -import ( - "cmd/internal/obj" - "debug/elf" - "encoding/binary" - "fmt" -) - -type LSym struct { - Name string - Extname string - Type int16 - Version int16 - Dupok uint8 - Cfunc uint8 - External uint8 - Nosplit uint8 - Reachable bool - Cgoexport uint8 - Special uint8 - Stkcheck uint8 - Hide uint8 - Leaf uint8 - Localentry uint8 - Onlist uint8 - // ElfType is set for symbols read from shared libraries by ldshlibsyms. It - // is not set for symbols defined by the packages being linked or by symbols - // read by ldelf (and so is left as elf.STT_NOTYPE). - ElfType elf.SymType - Dynid int32 - Plt int32 - Got int32 - Align int32 - Elfsym int32 - Args int32 - Locals int32 - Value int64 - Size int64 - Hash *LSym - Allsym *LSym - Next *LSym - Sub *LSym - Outer *LSym - Gotype *LSym - Reachparent *LSym - Queue *LSym - File string - Dynimplib string - Dynimpvers string - Sect interface{} - Autom *Auto - Pcln *Pcln - P []byte - R []Reloc - Local bool - 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 - Done uint8 - Type int32 - Variant int32 - Add int64 - Xadd int64 - Sym *LSym - Xsym *LSym -} - -type Auto struct { - Asym *LSym - Link *Auto - Aoffset int32 - Name int16 - Gotype *LSym -} - -type Shlib struct { - Path string - Hash []byte -} - -type Link struct { - Thechar int32 - Thestring string - Goarm int32 - Headtype int - Arch *LinkArch - Debugasm int32 - Debugvlog int32 - Bso *obj.Biobuf - Windows int32 - Goroot string - Hash map[symVer]*LSym - Allsym *LSym - Nsymbol int32 - Tlsg *LSym - Libdir []string - Library []*Library - Shlibs []Shlib - Tlsoffset int - Diag func(string, ...interface{}) - Cursym *LSym - Version int - Textp *LSym - Etextp *LSym - Nhistfile int32 - Filesyms *LSym -} - -type LinkArch struct { - ByteOrder binary.ByteOrder - Name string - Thechar int - Minlc int - Ptrsize int - Regsize int -} - -type Library struct { - Objref string - Srcref string - File string - Pkg string - Shlib string - hash []byte -} - -type Pcln struct { - Pcsp Pcdata - Pcfile Pcdata - Pcline Pcdata - Pcdata []Pcdata - Npcdata int - Funcdata []*LSym - Funcdataoff []int64 - Nfuncdata int - File []*LSym - Nfile int - Mfile int - Lastfile *LSym - Lastindex int -} - -type Pcdata struct { - P []byte -} - -type Pciter struct { - d Pcdata - p []byte - pc uint32 - nextpc uint32 - pcscale uint32 - value int32 - start int - done int -} - -// Reloc.variant -const ( - RV_NONE = iota - RV_POWER_LO - RV_POWER_HI - RV_POWER_HA - RV_POWER_DS - RV_CHECK_OVERFLOW = 1 << 8 - RV_TYPE_MASK = RV_CHECK_OVERFLOW - 1 -) - -const ( - LINKHASH = 100003 -) - -// Pcdata iterator. -// for(pciterinit(ctxt, &it, &pcd); !it.done; pciternext(&it)) { it.value holds in [it.pc, it.nextpc) } - -// Link holds the context for writing object code from a compiler -// to be linker input or for reading that input into the linker. - -// LinkArch is the definition of a single architecture. - -const ( - LinkAuto = 0 + iota - LinkInternal - LinkExternal -) diff --git a/src/cmd/internal/ld/macho.go b/src/cmd/internal/ld/macho.go deleted file mode 100644 index 0258aff104..0000000000 --- a/src/cmd/internal/ld/macho.go +++ /dev/null @@ -1,855 +0,0 @@ -// Copyright 2009 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 ( - "cmd/internal/obj" - "sort" - "strings" -) - -type MachoHdr struct { - cpu uint32 - subcpu uint32 -} - -type MachoSect struct { - name string - segname string - addr uint64 - size uint64 - off uint32 - align uint32 - reloc uint32 - nreloc uint32 - flag uint32 - res1 uint32 - res2 uint32 -} - -type MachoSeg struct { - name string - vsize uint64 - vaddr uint64 - fileoffset uint64 - filesize uint64 - prot1 uint32 - prot2 uint32 - nsect uint32 - msect uint32 - sect []MachoSect - flag uint32 -} - -type MachoLoad struct { - type_ uint32 - data []uint32 -} - -/* - * Total amount of space to reserve at the start of the file - * for Header, PHeaders, and SHeaders. - * May waste some. - */ -const ( - INITIAL_MACHO_HEADR = 4 * 1024 -) - -const ( - MACHO_CPU_AMD64 = 1<<24 | 7 - MACHO_CPU_386 = 7 - MACHO_SUBCPU_X86 = 3 - MACHO_CPU_ARM = 12 - MACHO_SUBCPU_ARM = 0 - MACHO_SUBCPU_ARMV7 = 9 - MACHO_CPU_ARM64 = 1<<24 | 12 - MACHO_SUBCPU_ARM64_ALL = 0 - MACHO32SYMSIZE = 12 - MACHO64SYMSIZE = 16 - MACHO_X86_64_RELOC_UNSIGNED = 0 - MACHO_X86_64_RELOC_SIGNED = 1 - MACHO_X86_64_RELOC_BRANCH = 2 - MACHO_X86_64_RELOC_GOT_LOAD = 3 - MACHO_X86_64_RELOC_GOT = 4 - MACHO_X86_64_RELOC_SUBTRACTOR = 5 - MACHO_X86_64_RELOC_SIGNED_1 = 6 - MACHO_X86_64_RELOC_SIGNED_2 = 7 - MACHO_X86_64_RELOC_SIGNED_4 = 8 - MACHO_ARM_RELOC_VANILLA = 0 - MACHO_ARM_RELOC_BR24 = 5 - MACHO_ARM64_RELOC_UNSIGNED = 0 - MACHO_ARM64_RELOC_BRANCH26 = 2 - MACHO_ARM64_RELOC_PAGE21 = 3 - MACHO_ARM64_RELOC_PAGEOFF12 = 4 - MACHO_ARM64_RELOC_ADDEND = 10 - MACHO_GENERIC_RELOC_VANILLA = 0 - MACHO_FAKE_GOTPCREL = 100 -) - -// Copyright 2009 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. - -// Mach-O file writing -// http://developer.apple.com/mac/library/DOCUMENTATION/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html - -var macho64 bool - -var machohdr MachoHdr - -var load []MachoLoad - -var seg [16]MachoSeg - -var nseg int - -var ndebug int - -var nsect int - -const ( - SymKindLocal = 0 + iota - SymKindExtdef - SymKindUndef - NumSymKind -) - -var nkind [NumSymKind]int - -var sortsym []*LSym - -var nsortsym int - -// Amount of space left for adding load commands -// that refer to dynamic libraries. Because these have -// to go in the Mach-O header, we can't just pick a -// "big enough" header size. The initial header is -// one page, the non-dynamic library stuff takes -// up about 1300 bytes; we overestimate that as 2k. -var load_budget int = INITIAL_MACHO_HEADR - 2*1024 - -func Machoinit() { - switch Thearch.Thechar { - // 64-bit architectures - case '6', '7', '9': - macho64 = true - - // 32-bit architectures - default: - break - } -} - -func getMachoHdr() *MachoHdr { - return &machohdr -} - -func newMachoLoad(type_ uint32, ndata uint32) *MachoLoad { - if macho64 && (ndata&1 != 0) { - ndata++ - } - - load = append(load, MachoLoad{}) - l := &load[len(load)-1] - l.type_ = type_ - l.data = make([]uint32, ndata) - return l -} - -func newMachoSeg(name string, msect int) *MachoSeg { - if nseg >= len(seg) { - Exitf("too many segs") - } - - s := &seg[nseg] - nseg++ - s.name = name - s.msect = uint32(msect) - s.sect = make([]MachoSect, msect) - return s -} - -func newMachoSect(seg *MachoSeg, name string, segname string) *MachoSect { - if seg.nsect >= seg.msect { - Exitf("too many sects in segment %s", seg.name) - } - - s := &seg.sect[seg.nsect] - seg.nsect++ - s.name = name - s.segname = segname - nsect++ - return s -} - -// Generic linking code. - -var dylib []string - -var linkoff int64 - -func machowrite() int { - o1 := Cpos() - - loadsize := 4 * 4 * ndebug - for i := 0; i < len(load); i++ { - loadsize += 4 * (len(load[i].data) + 2) - } - if macho64 { - loadsize += 18 * 4 * nseg - loadsize += 20 * 4 * nsect - } else { - loadsize += 14 * 4 * nseg - loadsize += 17 * 4 * nsect - } - - if macho64 { - Thearch.Lput(0xfeedfacf) - } else { - Thearch.Lput(0xfeedface) - } - Thearch.Lput(machohdr.cpu) - Thearch.Lput(machohdr.subcpu) - if Linkmode == LinkExternal { - Thearch.Lput(1) /* file type - mach object */ - } else { - Thearch.Lput(2) /* file type - mach executable */ - } - Thearch.Lput(uint32(len(load)) + uint32(nseg) + uint32(ndebug)) - Thearch.Lput(uint32(loadsize)) - Thearch.Lput(1) /* flags - no undefines */ - if macho64 { - Thearch.Lput(0) /* reserved */ - } - - var j int - var s *MachoSeg - var t *MachoSect - for i := 0; i < nseg; i++ { - s = &seg[i] - if macho64 { - Thearch.Lput(25) /* segment 64 */ - Thearch.Lput(72 + 80*s.nsect) - strnput(s.name, 16) - Thearch.Vput(s.vaddr) - Thearch.Vput(s.vsize) - Thearch.Vput(s.fileoffset) - Thearch.Vput(s.filesize) - Thearch.Lput(s.prot1) - Thearch.Lput(s.prot2) - Thearch.Lput(s.nsect) - Thearch.Lput(s.flag) - } else { - Thearch.Lput(1) /* segment 32 */ - Thearch.Lput(56 + 68*s.nsect) - strnput(s.name, 16) - Thearch.Lput(uint32(s.vaddr)) - Thearch.Lput(uint32(s.vsize)) - Thearch.Lput(uint32(s.fileoffset)) - Thearch.Lput(uint32(s.filesize)) - Thearch.Lput(s.prot1) - Thearch.Lput(s.prot2) - Thearch.Lput(s.nsect) - Thearch.Lput(s.flag) - } - - for j = 0; uint32(j) < s.nsect; j++ { - t = &s.sect[j] - if macho64 { - strnput(t.name, 16) - strnput(t.segname, 16) - Thearch.Vput(t.addr) - Thearch.Vput(t.size) - Thearch.Lput(t.off) - Thearch.Lput(t.align) - Thearch.Lput(t.reloc) - Thearch.Lput(t.nreloc) - Thearch.Lput(t.flag) - Thearch.Lput(t.res1) /* reserved */ - Thearch.Lput(t.res2) /* reserved */ - Thearch.Lput(0) /* reserved */ - } else { - strnput(t.name, 16) - strnput(t.segname, 16) - Thearch.Lput(uint32(t.addr)) - Thearch.Lput(uint32(t.size)) - Thearch.Lput(t.off) - Thearch.Lput(t.align) - Thearch.Lput(t.reloc) - Thearch.Lput(t.nreloc) - Thearch.Lput(t.flag) - Thearch.Lput(t.res1) /* reserved */ - Thearch.Lput(t.res2) /* reserved */ - } - } - } - - var l *MachoLoad - for i := 0; i < len(load); i++ { - l = &load[i] - Thearch.Lput(l.type_) - Thearch.Lput(4 * (uint32(len(l.data)) + 2)) - for j = 0; j < len(l.data); j++ { - Thearch.Lput(l.data[j]) - } - } - - return int(Cpos() - o1) -} - -func domacho() { - if Debug['d'] != 0 { - return - } - - // empirically, string table must begin with " \x00". - s := Linklookup(Ctxt, ".machosymstr", 0) - - s.Type = obj.SMACHOSYMSTR - s.Reachable = true - Adduint8(Ctxt, s, ' ') - Adduint8(Ctxt, s, '\x00') - - s = Linklookup(Ctxt, ".machosymtab", 0) - s.Type = obj.SMACHOSYMTAB - s.Reachable = true - - if Linkmode != LinkExternal { - s := Linklookup(Ctxt, ".plt", 0) // will be __symbol_stub - s.Type = obj.SMACHOPLT - s.Reachable = true - - s = Linklookup(Ctxt, ".got", 0) // will be __nl_symbol_ptr - s.Type = obj.SMACHOGOT - s.Reachable = true - s.Align = 4 - - s = Linklookup(Ctxt, ".linkedit.plt", 0) // indirect table for .plt - s.Type = obj.SMACHOINDIRECTPLT - s.Reachable = true - - s = Linklookup(Ctxt, ".linkedit.got", 0) // indirect table for .got - s.Type = obj.SMACHOINDIRECTGOT - s.Reachable = true - } -} - -func Machoadddynlib(lib string) { - // Will need to store the library name rounded up - // and 24 bytes of header metadata. If not enough - // space, grab another page of initial space at the - // beginning of the output file. - load_budget -= (len(lib)+7)/8*8 + 24 - - if load_budget < 0 { - HEADR += 4096 - INITTEXT += 4096 - load_budget += 4096 - } - - dylib = append(dylib, lib) -} - -func machoshbits(mseg *MachoSeg, sect *Section, segname string) { - buf := "__" + strings.Replace(sect.Name[1:], ".", "_", -1) - - var msect *MachoSect - if Thearch.Thechar == '7' && sect.Rwx&1 == 0 { - // darwin/arm64 forbids absolute relocs in __TEXT, so if - // the section is not executable, put it in __DATA segment. - msect = newMachoSect(mseg, buf, "__DATA") - } else { - msect = newMachoSect(mseg, buf, segname) - } - - if sect.Rellen > 0 { - msect.reloc = uint32(sect.Reloff) - msect.nreloc = uint32(sect.Rellen / 8) - } - - for 1< sect.Seg.Vaddr+sect.Seg.Filelen-sect.Vaddr { - Diag("macho cannot represent section %s crossing data and bss", sect.Name) - } - msect.off = uint32(sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr) - } else { - // zero fill - msect.off = 0 - - msect.flag |= 1 - } - - if sect.Rwx&1 != 0 { - msect.flag |= 0x400 /* has instructions */ - } - - if sect.Name == ".plt" { - msect.name = "__symbol_stub1" - msect.flag = 0x80000408 /* only instructions, code, symbol stubs */ - msect.res1 = 0 //nkind[SymKindLocal]; - msect.res2 = 6 - } - - if sect.Name == ".got" { - msect.name = "__nl_symbol_ptr" - msect.flag = 6 /* section with nonlazy symbol pointers */ - msect.res1 = uint32(Linklookup(Ctxt, ".linkedit.plt", 0).Size / 4) /* offset into indirect symbol table */ - } - - if sect.Name == ".init_array" { - msect.name = "__mod_init_func" - msect.flag = 9 // S_MOD_INIT_FUNC_POINTERS - } -} - -func Asmbmacho() { - /* apple MACH */ - va := INITTEXT - int64(HEADR) - - mh := getMachoHdr() - switch Thearch.Thechar { - default: - Exitf("unknown macho architecture: %v", Thearch.Thechar) - - case '5': - mh.cpu = MACHO_CPU_ARM - mh.subcpu = MACHO_SUBCPU_ARMV7 - - case '6': - mh.cpu = MACHO_CPU_AMD64 - mh.subcpu = MACHO_SUBCPU_X86 - - case '7': - mh.cpu = MACHO_CPU_ARM64 - mh.subcpu = MACHO_SUBCPU_ARM64_ALL - - case '8': - mh.cpu = MACHO_CPU_386 - mh.subcpu = MACHO_SUBCPU_X86 - } - - var ms *MachoSeg - if Linkmode == LinkExternal { - /* segment for entire file */ - ms = newMachoSeg("", 40) - - ms.fileoffset = Segtext.Fileoff - ms.filesize = Segdwarf.Fileoff + Segdwarf.Filelen - Segtext.Fileoff - ms.vsize = ms.filesize - } - - /* segment for zero page */ - if Linkmode != LinkExternal { - ms = newMachoSeg("__PAGEZERO", 0) - ms.vsize = uint64(va) - } - - /* text */ - v := Rnd(int64(uint64(HEADR)+Segtext.Length), int64(INITRND)) - - if Linkmode != LinkExternal { - ms = newMachoSeg("__TEXT", 20) - ms.vaddr = uint64(va) - ms.vsize = uint64(v) - ms.fileoffset = 0 - ms.filesize = uint64(v) - ms.prot1 = 7 - ms.prot2 = 5 - } - - for sect := Segtext.Sect; sect != nil; sect = sect.Next { - machoshbits(ms, sect, "__TEXT") - } - - /* data */ - if Linkmode != LinkExternal { - w := int64(Segdata.Length) - ms = newMachoSeg("__DATA", 20) - ms.vaddr = uint64(va) + uint64(v) - ms.vsize = uint64(w) - ms.fileoffset = uint64(v) - ms.filesize = Segdata.Filelen - ms.prot1 = 3 - ms.prot2 = 3 - } - - for sect := Segdata.Sect; sect != nil; sect = sect.Next { - machoshbits(ms, sect, "__DATA") - } - - if Linkmode != LinkExternal { - switch Thearch.Thechar { - default: - Exitf("unknown macho architecture: %v", Thearch.Thechar) - - case '5': - ml := newMachoLoad(5, 17+2) /* unix thread */ - ml.data[0] = 1 /* thread type */ - ml.data[1] = 17 /* word count */ - ml.data[2+15] = uint32(Entryvalue()) /* start pc */ - - case '6': - ml := newMachoLoad(5, 42+2) /* unix thread */ - ml.data[0] = 4 /* thread type */ - ml.data[1] = 42 /* word count */ - ml.data[2+32] = uint32(Entryvalue()) /* start pc */ - ml.data[2+32+1] = uint32(Entryvalue() >> 32) - - case '7': - ml := newMachoLoad(5, 68+2) /* unix thread */ - ml.data[0] = 6 /* thread type */ - ml.data[1] = 68 /* word count */ - ml.data[2+64] = uint32(Entryvalue()) /* start pc */ - ml.data[2+64+1] = uint32(Entryvalue() >> 32) - - case '8': - ml := newMachoLoad(5, 16+2) /* unix thread */ - ml.data[0] = 1 /* thread type */ - ml.data[1] = 16 /* word count */ - ml.data[2+10] = uint32(Entryvalue()) /* start pc */ - } - } - - if Debug['d'] == 0 { - // must match domacholink below - s1 := Linklookup(Ctxt, ".machosymtab", 0) - - s2 := Linklookup(Ctxt, ".linkedit.plt", 0) - s3 := Linklookup(Ctxt, ".linkedit.got", 0) - s4 := Linklookup(Ctxt, ".machosymstr", 0) - - if Linkmode != LinkExternal { - ms := newMachoSeg("__LINKEDIT", 0) - ms.vaddr = uint64(va) + uint64(v) + uint64(Rnd(int64(Segdata.Length), int64(INITRND))) - ms.vsize = uint64(s1.Size) + uint64(s2.Size) + uint64(s3.Size) + uint64(s4.Size) - ms.fileoffset = uint64(linkoff) - ms.filesize = ms.vsize - ms.prot1 = 7 - ms.prot2 = 3 - } - - ml := newMachoLoad(2, 4) /* LC_SYMTAB */ - ml.data[0] = uint32(linkoff) /* symoff */ - ml.data[1] = uint32(nsortsym) /* nsyms */ - ml.data[2] = uint32(linkoff + s1.Size + s2.Size + s3.Size) /* stroff */ - ml.data[3] = uint32(s4.Size) /* strsize */ - - machodysymtab() - - if Linkmode != LinkExternal { - ml := newMachoLoad(14, 6) /* LC_LOAD_DYLINKER */ - ml.data[0] = 12 /* offset to string */ - stringtouint32(ml.data[1:], "/usr/lib/dyld") - - for i := 0; i < len(dylib); i++ { - ml = newMachoLoad(12, 4+(uint32(len(dylib[i]))+1+7)/8*2) /* LC_LOAD_DYLIB */ - ml.data[0] = 24 /* offset of string from beginning of load */ - ml.data[1] = 0 /* time stamp */ - ml.data[2] = 0 /* version */ - ml.data[3] = 0 /* compatibility version */ - stringtouint32(ml.data[4:], dylib[i]) - } - } - } - - // TODO: dwarf headers go in ms too - if Debug['s'] == 0 { - dwarfaddmachoheaders(ms) - } - - a := machowrite() - if int32(a) > HEADR { - Exitf("HEADR too small: %d > %d", a, HEADR) - } -} - -func symkind(s *LSym) int { - if s.Type == obj.SDYNIMPORT { - return SymKindUndef - } - if s.Cgoexport != 0 { - return SymKindExtdef - } - return SymKindLocal -} - -func addsym(s *LSym, name string, type_ int, addr int64, size int64, ver int, gotype *LSym) { - if s == nil { - return - } - - switch type_ { - default: - return - - case 'D', 'B', 'T': - break - } - - if sortsym != nil { - sortsym[nsortsym] = s - nkind[symkind(s)]++ - } - - nsortsym++ -} - -type machoscmp []*LSym - -func (x machoscmp) Len() int { - return len(x) -} - -func (x machoscmp) Swap(i, j int) { - x[i], x[j] = x[j], x[i] -} - -func (x machoscmp) Less(i, j int) bool { - s1 := x[i] - s2 := x[j] - - k1 := symkind(s1) - k2 := symkind(s2) - if k1 != k2 { - return k1-k2 < 0 - } - - return stringsCompare(s1.Extname, s2.Extname) < 0 -} - -func machogenasmsym(put func(*LSym, string, int, int64, int64, int, *LSym)) { - genasmsym(put) - for s := Ctxt.Allsym; s != nil; s = s.Allsym { - if s.Type == obj.SDYNIMPORT || s.Type == obj.SHOSTOBJ { - if s.Reachable { - put(s, "", 'D', 0, 0, 0, nil) - } - } - } -} - -func machosymorder() { - // On Mac OS X Mountain Lion, we must sort exported symbols - // So we sort them here and pre-allocate dynid for them - // See http://golang.org/issue/4029 - for i := 0; i < len(dynexp); i++ { - dynexp[i].Reachable = true - } - machogenasmsym(addsym) - sortsym = make([]*LSym, nsortsym) - nsortsym = 0 - machogenasmsym(addsym) - sort.Sort(machoscmp(sortsym[:nsortsym])) - for i := 0; i < nsortsym; i++ { - sortsym[i].Dynid = int32(i) - } -} - -func machosymtab() { - var s *LSym - var o *LSym - var p string - - symtab := Linklookup(Ctxt, ".machosymtab", 0) - symstr := Linklookup(Ctxt, ".machosymstr", 0) - - for i := 0; i < nsortsym; i++ { - s = sortsym[i] - Adduint32(Ctxt, symtab, uint32(symstr.Size)) - - // Only add _ to C symbols. Go symbols have dot in the name. - if !strings.Contains(s.Extname, ".") { - Adduint8(Ctxt, symstr, '_') - } - - // replace "·" as ".", because DTrace cannot handle it. - if !strings.Contains(s.Extname, "·") { - Addstring(symstr, s.Extname) - } else { - for p = s.Extname; p != ""; p = p[1:] { - if uint8(p[0]) == 0xc2 && uint8((p[1:])[0]) == 0xb7 { - Adduint8(Ctxt, symstr, '.') - p = p[1:] - } else { - Adduint8(Ctxt, symstr, uint8(p[0])) - } - } - - Adduint8(Ctxt, symstr, '\x00') - } - - if s.Type == obj.SDYNIMPORT || s.Type == obj.SHOSTOBJ { - Adduint8(Ctxt, symtab, 0x01) // type N_EXT, external symbol - Adduint8(Ctxt, symtab, 0) // no section - Adduint16(Ctxt, symtab, 0) // desc - adduintxx(Ctxt, symtab, 0, Thearch.Ptrsize) // no value - } else { - if s.Cgoexport != 0 { - Adduint8(Ctxt, symtab, 0x0f) - } else { - Adduint8(Ctxt, symtab, 0x0e) - } - o = s - for o.Outer != nil { - o = o.Outer - } - if o.Sect == nil { - Diag("missing section for %s", s.Name) - Adduint8(Ctxt, symtab, 0) - } else { - Adduint8(Ctxt, symtab, uint8((o.Sect.(*Section)).Extnum)) - } - Adduint16(Ctxt, symtab, 0) // desc - adduintxx(Ctxt, symtab, uint64(Symaddr(s)), Thearch.Ptrsize) - } - } -} - -func machodysymtab() { - ml := newMachoLoad(11, 18) /* LC_DYSYMTAB */ - - n := 0 - ml.data[0] = uint32(n) /* ilocalsym */ - ml.data[1] = uint32(nkind[SymKindLocal]) /* nlocalsym */ - n += nkind[SymKindLocal] - - ml.data[2] = uint32(n) /* iextdefsym */ - ml.data[3] = uint32(nkind[SymKindExtdef]) /* nextdefsym */ - n += nkind[SymKindExtdef] - - ml.data[4] = uint32(n) /* iundefsym */ - ml.data[5] = uint32(nkind[SymKindUndef]) /* nundefsym */ - - ml.data[6] = 0 /* tocoffset */ - ml.data[7] = 0 /* ntoc */ - ml.data[8] = 0 /* modtaboff */ - ml.data[9] = 0 /* nmodtab */ - ml.data[10] = 0 /* extrefsymoff */ - ml.data[11] = 0 /* nextrefsyms */ - - // must match domacholink below - s1 := Linklookup(Ctxt, ".machosymtab", 0) - - s2 := Linklookup(Ctxt, ".linkedit.plt", 0) - s3 := Linklookup(Ctxt, ".linkedit.got", 0) - ml.data[12] = uint32(linkoff + s1.Size) /* indirectsymoff */ - ml.data[13] = uint32((s2.Size + s3.Size) / 4) /* nindirectsyms */ - - ml.data[14] = 0 /* extreloff */ - ml.data[15] = 0 /* nextrel */ - ml.data[16] = 0 /* locreloff */ - ml.data[17] = 0 /* nlocrel */ -} - -func Domacholink() int64 { - machosymtab() - - // write data that will be linkedit section - s1 := Linklookup(Ctxt, ".machosymtab", 0) - - s2 := Linklookup(Ctxt, ".linkedit.plt", 0) - s3 := Linklookup(Ctxt, ".linkedit.got", 0) - s4 := Linklookup(Ctxt, ".machosymstr", 0) - - // Force the linkedit section to end on a 16-byte - // boundary. This allows pure (non-cgo) Go binaries - // to be code signed correctly. - // - // Apple's codesign_allocate (a helper utility for - // the codesign utility) can do this fine itself if - // it is run on a dynamic Mach-O binary. However, - // when it is run on a pure (non-cgo) Go binary, where - // the linkedit section is mostly empty, it fails to - // account for the extra padding that it itself adds - // when adding the LC_CODE_SIGNATURE load command - // (which must be aligned on a 16-byte boundary). - // - // By forcing the linkedit section to end on a 16-byte - // boundary, codesign_allocate will not need to apply - // any alignment padding itself, working around the - // issue. - for s4.Size%16 != 0 { - Adduint8(Ctxt, s4, 0) - } - - size := int(s1.Size + s2.Size + s3.Size + s4.Size) - - if size > 0 { - linkoff = Rnd(int64(uint64(HEADR)+Segtext.Length), int64(INITRND)) + Rnd(int64(Segdata.Filelen), int64(INITRND)) + Rnd(int64(Segdwarf.Filelen), int64(INITRND)) - Cseek(linkoff) - - Cwrite(s1.P[:s1.Size]) - Cwrite(s2.P[:s2.Size]) - Cwrite(s3.P[:s3.Size]) - Cwrite(s4.P[:s4.Size]) - } - - return Rnd(int64(size), int64(INITRND)) -} - -func machorelocsect(sect *Section, first *LSym) { - // If main section has no bits, nothing to relocate. - if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { - return - } - - sect.Reloff = uint64(Cpos()) - var sym *LSym - for sym = first; sym != nil; sym = sym.Next { - if !sym.Reachable { - continue - } - if uint64(sym.Value) >= sect.Vaddr { - break - } - } - - eaddr := int32(sect.Vaddr + sect.Length) - var r *Reloc - var ri int - for ; sym != nil; sym = sym.Next { - if !sym.Reachable { - continue - } - if sym.Value >= int64(eaddr) { - break - } - Ctxt.Cursym = sym - - for ri = 0; ri < len(sym.R); ri++ { - r = &sym.R[ri] - if r.Done != 0 { - continue - } - if Thearch.Machoreloc1(r, int64(uint64(sym.Value+int64(r.Off))-sect.Vaddr)) < 0 { - Diag("unsupported obj reloc %d/%d to %s", r.Type, r.Siz, r.Sym.Name) - } - } - } - - sect.Rellen = uint64(Cpos()) - sect.Reloff -} - -func Machoemitreloc() { - for Cpos()&7 != 0 { - Cput(0) - } - - machorelocsect(Segtext.Sect, Ctxt.Textp) - for sect := Segtext.Sect.Next; sect != nil; sect = sect.Next { - machorelocsect(sect, datap) - } - 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) -} diff --git a/src/cmd/internal/ld/objfile.go b/src/cmd/internal/ld/objfile.go deleted file mode 100644 index 613fcb2a40..0000000000 --- a/src/cmd/internal/ld/objfile.go +++ /dev/null @@ -1,354 +0,0 @@ -// Copyright 2013 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" - "cmd/internal/obj" - "fmt" - "log" - "strconv" - "strings" -) - -const ( - startmagic = "\x00\x00go13ld" - endmagic = "\xff\xffgo13ld" -) - -func ldobjfile(ctxt *Link, f *obj.Biobuf, pkg string, length int64, pn string) { - start := obj.Boffset(f) - ctxt.Version++ - var buf [8]uint8 - obj.Bread(f, buf[:]) - if string(buf[:]) != startmagic { - log.Fatalf("%s: invalid file start %x %x %x %x %x %x %x %x", pn, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]) - } - c := obj.Bgetc(f) - if c != 1 { - log.Fatalf("%s: invalid file version number %d", pn, c) - } - - var lib string - for { - lib = rdstring(f) - if lib == "" { - break - } - addlib(ctxt, pkg, pn, lib) - } - - for { - c, err := f.Peek(1) - if err != nil { - log.Fatalf("%s: peeking: %v", pn, err) - } - if c[0] == 0xff { - break - } - readsym(ctxt, f, pkg, pn) - } - - buf = [8]uint8{} - obj.Bread(f, buf[:]) - if string(buf[:]) != endmagic { - log.Fatalf("%s: invalid file end", pn) - } - - if obj.Boffset(f) != start+length { - log.Fatalf("%s: unexpected end at %d, want %d", pn, int64(obj.Boffset(f)), int64(start+length)) - } -} - -var readsym_ndup int - -func readsym(ctxt *Link, f *obj.Biobuf, pkg string, pn string) { - if obj.Bgetc(f) != 0xfe { - log.Fatalf("readsym out of sync") - } - t := int(rdint(f)) - name := expandpkg(rdstring(f), pkg) - v := int(rdint(f)) - if v != 0 && v != 1 { - log.Fatalf("invalid symbol version %d", v) - } - flags := int(rdint(f)) - dupok := flags & 1 - local := false - if flags&2 != 0 { - local = true - } - size := int(rdint(f)) - typ := rdsym(ctxt, f, pkg) - data := rddata(f) - nreloc := int(rdint(f)) - - if v != 0 { - v = ctxt.Version - } - s := Linklookup(ctxt, name, v) - var dup *LSym - if s.Type != 0 && s.Type != obj.SXREF { - if (t == obj.SDATA || t == obj.SBSS || t == obj.SNOPTRBSS) && len(data) == 0 && nreloc == 0 { - if s.Size < int64(size) { - s.Size = int64(size) - } - if typ != nil && s.Gotype == nil { - s.Gotype = typ - } - return - } - - if (s.Type == obj.SDATA || s.Type == obj.SBSS || s.Type == obj.SNOPTRBSS) && len(s.P) == 0 && len(s.R) == 0 { - goto overwrite - } - if s.Type != obj.SBSS && s.Type != obj.SNOPTRBSS && dupok == 0 && s.Dupok == 0 { - log.Fatalf("duplicate symbol %s (types %d and %d) in %s and %s", s.Name, s.Type, t, s.File, pn) - } - if len(s.P) > 0 { - dup = s - s = linknewsym(ctxt, ".dup", readsym_ndup) - readsym_ndup++ // scratch - } - } - -overwrite: - s.File = pkg - s.Dupok = uint8(dupok) - if t == obj.SXREF { - log.Fatalf("bad sxref") - } - if t == 0 { - log.Fatalf("missing type for %s in %s", name, pn) - } - if t == obj.SBSS && (s.Type == obj.SRODATA || s.Type == obj.SNOPTRBSS) { - t = int(s.Type) - } - s.Type = int16(t) - if s.Size < int64(size) { - s.Size = int64(size) - } - s.Local = local - if typ != nil { // if bss sym defined multiple times, take type from any one def - s.Gotype = typ - } - if dup != nil && typ != nil { - dup.Gotype = typ - } - s.P = data - s.P = s.P[:len(data)] - if nreloc > 0 { - s.R = make([]Reloc, nreloc) - s.R = s.R[:nreloc] - var r *Reloc - for i := 0; i < nreloc; i++ { - r = &s.R[i] - r.Off = int32(rdint(f)) - r.Siz = uint8(rdint(f)) - r.Type = int32(rdint(f)) - r.Add = rdint(f) - rdint(f) // Xadd, ignored - r.Sym = rdsym(ctxt, f, pkg) - rdsym(ctxt, f, pkg) // Xsym, ignored - } - } - - if len(s.P) > 0 && dup != nil && len(dup.P) > 0 && strings.HasPrefix(s.Name, "gclocals·") { - // content-addressed garbage collection liveness bitmap symbol. - // double check for hash collisions. - if !bytes.Equal(s.P, dup.P) { - log.Fatalf("dupok hash collision for %s in %s and %s", s.Name, s.File, pn) - } - } - - if s.Type == obj.STEXT { - s.Args = int32(rdint(f)) - s.Locals = int32(rdint(f)) - s.Nosplit = uint8(rdint(f)) - v := int(rdint(f)) - s.Leaf = uint8(v & 1) - s.Cfunc = uint8(v & 2) - n := int(rdint(f)) - var a *Auto - for i := 0; i < n; i++ { - a = new(Auto) - a.Asym = rdsym(ctxt, f, pkg) - a.Aoffset = int32(rdint(f)) - a.Name = int16(rdint(f)) - a.Gotype = rdsym(ctxt, f, pkg) - a.Link = s.Autom - s.Autom = a - } - - s.Pcln = new(Pcln) - pc := s.Pcln - pc.Pcsp.P = rddata(f) - pc.Pcfile.P = rddata(f) - pc.Pcline.P = rddata(f) - n = int(rdint(f)) - pc.Pcdata = make([]Pcdata, n) - pc.Npcdata = n - for i := 0; i < n; i++ { - pc.Pcdata[i].P = rddata(f) - } - n = int(rdint(f)) - pc.Funcdata = make([]*LSym, n) - pc.Funcdataoff = make([]int64, n) - pc.Nfuncdata = n - for i := 0; i < n; i++ { - pc.Funcdata[i] = rdsym(ctxt, f, pkg) - } - for i := 0; i < n; i++ { - pc.Funcdataoff[i] = rdint(f) - } - n = int(rdint(f)) - pc.File = make([]*LSym, n) - pc.Nfile = n - for i := 0; i < n; i++ { - pc.File[i] = rdsym(ctxt, f, pkg) - } - - if dup == nil { - if s.Onlist != 0 { - log.Fatalf("symbol %s listed multiple times", s.Name) - } - s.Onlist = 1 - if ctxt.Etextp != nil { - ctxt.Etextp.Next = s - } else { - ctxt.Textp = s - } - ctxt.Etextp = s - } - } - - if ctxt.Debugasm != 0 { - fmt.Fprintf(ctxt.Bso, "%s ", s.Name) - if s.Version != 0 { - fmt.Fprintf(ctxt.Bso, "v=%d ", s.Version) - } - if s.Type != 0 { - fmt.Fprintf(ctxt.Bso, "t=%d ", s.Type) - } - if s.Dupok != 0 { - fmt.Fprintf(ctxt.Bso, "dupok ") - } - if s.Cfunc != 0 { - fmt.Fprintf(ctxt.Bso, "cfunc ") - } - if s.Nosplit != 0 { - fmt.Fprintf(ctxt.Bso, "nosplit ") - } - fmt.Fprintf(ctxt.Bso, "size=%d value=%d", int64(s.Size), int64(s.Value)) - if s.Type == obj.STEXT { - fmt.Fprintf(ctxt.Bso, " args=%#x locals=%#x", uint64(s.Args), uint64(s.Locals)) - } - fmt.Fprintf(ctxt.Bso, "\n") - var c int - var j int - for i := 0; i < len(s.P); { - fmt.Fprintf(ctxt.Bso, "\t%#04x", uint(i)) - for j = i; j < i+16 && j < len(s.P); j++ { - fmt.Fprintf(ctxt.Bso, " %02x", s.P[j]) - } - for ; j < i+16; j++ { - fmt.Fprintf(ctxt.Bso, " ") - } - fmt.Fprintf(ctxt.Bso, " ") - for j = i; j < i+16 && j < len(s.P); j++ { - c = int(s.P[j]) - if ' ' <= c && c <= 0x7e { - fmt.Fprintf(ctxt.Bso, "%c", c) - } else { - fmt.Fprintf(ctxt.Bso, ".") - } - } - - fmt.Fprintf(ctxt.Bso, "\n") - i += 16 - } - - var r *Reloc - for i := 0; i < len(s.R); i++ { - r = &s.R[i] - fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%d\n", int(r.Off), r.Siz, r.Type, r.Sym.Name, int64(r.Add)) - } - } -} - -func rdint(f *obj.Biobuf) int64 { - var c int - - uv := uint64(0) - for shift := 0; ; shift += 7 { - if shift >= 64 { - log.Fatalf("corrupt input") - } - c = obj.Bgetc(f) - uv |= uint64(c&0x7F) << uint(shift) - if c&0x80 == 0 { - break - } - } - - return int64(uv>>1) ^ (int64(uint64(uv)<<63) >> 63) -} - -func rdstring(f *obj.Biobuf) string { - n := rdint(f) - p := make([]byte, n) - obj.Bread(f, p) - return string(p) -} - -func rddata(f *obj.Biobuf) []byte { - n := rdint(f) - p := make([]byte, n) - obj.Bread(f, p) - return p -} - -var symbuf []byte - -func rdsym(ctxt *Link, f *obj.Biobuf, pkg string) *LSym { - n := int(rdint(f)) - if n == 0 { - rdint(f) - return nil - } - - if len(symbuf) < n { - symbuf = make([]byte, n) - } - obj.Bread(f, symbuf[:n]) - p := string(symbuf[:n]) - v := int(rdint(f)) - if v != 0 { - v = ctxt.Version - } - s := Linklookup(ctxt, expandpkg(p, pkg), v) - - if v == 0 && s.Name[0] == '$' && s.Type == 0 { - if strings.HasPrefix(s.Name, "$f32.") { - x, _ := strconv.ParseUint(s.Name[5:], 16, 32) - i32 := int32(x) - s.Type = obj.SRODATA - s.Local = true - Adduint32(ctxt, s, uint32(i32)) - s.Reachable = false - } else if strings.HasPrefix(s.Name, "$f64.") || strings.HasPrefix(s.Name, "$i64.") { - x, _ := strconv.ParseUint(s.Name[5:], 16, 64) - i64 := int64(x) - s.Type = obj.SRODATA - s.Local = true - Adduint64(ctxt, s, uint64(i64)) - s.Reachable = false - } - } - if v == 0 && strings.HasPrefix(s.Name, "runtime.gcbits.") { - s.Local = true - } - return s -} diff --git a/src/cmd/internal/ld/pcln.go b/src/cmd/internal/ld/pcln.go deleted file mode 100644 index be2568d3b9..0000000000 --- a/src/cmd/internal/ld/pcln.go +++ /dev/null @@ -1,462 +0,0 @@ -// Copyright 2013 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 ( - "cmd/internal/obj" - "fmt" - "log" -) - -// funcpctab writes to dst a pc-value table mapping the code in func to the values -// returned by valfunc parameterized by arg. The invocation of valfunc to update the -// current value is, for each p, -// -// val = valfunc(func, val, p, 0, arg); -// record val as value at p->pc; -// val = valfunc(func, val, p, 1, arg); -// -// where func is the function, val is the current value, p is the instruction being -// considered, and arg can be used to further parameterize valfunc. - -// pctofileline computes either the file number (arg == 0) -// or the line number (arg == 1) to use at p. -// Because p->lineno applies to p, phase == 0 (before p) -// takes care of the update. - -// pctospadj computes the sp adjustment in effect. -// It is oldval plus any adjustment made by p itself. -// The adjustment by p takes effect only after p, so we -// apply the change during phase == 1. - -// pctopcdata computes the pcdata value in effect at p. -// A PCDATA instruction sets the value in effect at future -// non-PCDATA instructions. -// Since PCDATA instructions have no width in the final code, -// it does not matter which phase we use for the update. - -// iteration over encoded pcdata tables. - -func getvarint(pp *[]byte) uint32 { - v := uint32(0) - p := *pp - for shift := 0; ; shift += 7 { - v |= uint32(p[0]&0x7F) << uint(shift) - tmp4 := p - p = p[1:] - if tmp4[0]&0x80 == 0 { - break - } - } - - *pp = p - return v -} - -func pciternext(it *Pciter) { - it.pc = it.nextpc - if it.done != 0 { - return - } - if -cap(it.p) >= -cap(it.d.P[len(it.d.P):]) { - it.done = 1 - return - } - - // value delta - v := getvarint(&it.p) - - if v == 0 && it.start == 0 { - it.done = 1 - return - } - - it.start = 0 - dv := int32(v>>1) ^ (int32(v<<31) >> 31) - it.value += dv - - // pc delta - v = getvarint(&it.p) - - it.nextpc = it.pc + v*it.pcscale -} - -func pciterinit(ctxt *Link, it *Pciter, d *Pcdata) { - it.d = *d - it.p = it.d.P - it.pc = 0 - it.nextpc = 0 - it.value = -1 - it.start = 1 - it.done = 0 - it.pcscale = uint32(ctxt.Arch.Minlc) - pciternext(it) -} - -// Copyright 2013 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. - -func addvarint(d *Pcdata, val uint32) { - n := int32(0) - for v := val; v >= 0x80; v >>= 7 { - n++ - } - n++ - - old := len(d.P) - for cap(d.P) < len(d.P)+int(n) { - d.P = append(d.P[:cap(d.P)], 0) - } - d.P = d.P[:old+int(n)] - - p := d.P[old:] - var v uint32 - for v = val; v >= 0x80; v >>= 7 { - p[0] = byte(v | 0x80) - p = p[1:] - } - p[0] = byte(v) -} - -func addpctab(ftab *LSym, off int32, d *Pcdata) int32 { - start := int32(len(ftab.P)) - Symgrow(Ctxt, ftab, int64(start)+int64(len(d.P))) - copy(ftab.P[start:], d.P) - - return int32(setuint32(Ctxt, ftab, int64(off), uint32(start))) -} - -func ftabaddstring(ftab *LSym, s string) int32 { - n := int32(len(s)) + 1 - start := int32(len(ftab.P)) - Symgrow(Ctxt, ftab, int64(start)+int64(n)+1) - copy(ftab.P[start:], s) - return start -} - -func renumberfiles(ctxt *Link, files []*LSym, d *Pcdata) { - var f *LSym - - // Give files numbers. - for i := 0; i < len(files); i++ { - f = files[i] - if f.Type != obj.SFILEPATH { - ctxt.Nhistfile++ - f.Value = int64(ctxt.Nhistfile) - f.Type = obj.SFILEPATH - f.Next = ctxt.Filesyms - ctxt.Filesyms = f - } - } - - newval := int32(-1) - var out Pcdata - - var dv int32 - var it Pciter - var oldval int32 - var v uint32 - var val int32 - for pciterinit(ctxt, &it, d); it.done == 0; pciternext(&it) { - // value delta - oldval = it.value - - if oldval == -1 { - val = -1 - } else { - if oldval < 0 || oldval >= int32(len(files)) { - log.Fatalf("bad pcdata %d", oldval) - } - val = int32(files[oldval].Value) - } - - dv = val - newval - newval = val - v = (uint32(dv) << 1) ^ uint32(int32(dv>>31)) - addvarint(&out, v) - - // pc delta - addvarint(&out, (it.nextpc-it.pc)/it.pcscale) - } - - // terminating value delta - addvarint(&out, 0) - - *d = out -} - -func container(s *LSym) int { - // We want to generate func table entries only for the "lowest level" symbols, - // not containers of subsymbols. - if s != nil && s.Sub != nil { - return 1 - } - return 0 -} - -// pclntab initializes the pclntab symbol with -// runtime function and file name information. - -var pclntab_zpcln Pcln - -// These variables are used to initialize runtime.firstmoduledata, see symtab.go:symtab. -var pclntabNfunc int32 -var pclntabFiletabOffset int32 -var pclntabPclntabOffset int32 -var pclntabFirstFunc *LSym -var pclntabLastFunc *LSym - -func pclntab() { - funcdata_bytes := int64(0) - ftab := Linklookup(Ctxt, "runtime.pclntab", 0) - ftab.Type = obj.SPCLNTAB - ftab.Reachable = true - - // See golang.org/s/go12symtab for the format. Briefly: - // 8-byte header - // nfunc [thearch.ptrsize bytes] - // function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes] - // end PC [thearch.ptrsize bytes] - // offset to file table [4 bytes] - nfunc := int32(0) - - for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { - if container(Ctxt.Cursym) == 0 { - nfunc++ - } - } - - pclntabNfunc = nfunc - Symgrow(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize)+4) - setuint32(Ctxt, ftab, 0, 0xfffffffb) - setuint8(Ctxt, ftab, 6, uint8(Thearch.Minlc)) - setuint8(Ctxt, ftab, 7, uint8(Thearch.Ptrsize)) - setuintxx(Ctxt, ftab, 8, uint64(nfunc), int64(Thearch.Ptrsize)) - pclntabPclntabOffset = int32(8 + Thearch.Ptrsize) - - nfunc = 0 - var last *LSym - var end int32 - var funcstart int32 - var i int32 - var it Pciter - var off int32 - var pcln *Pcln - for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { - last = Ctxt.Cursym - if container(Ctxt.Cursym) != 0 { - continue - } - pcln = Ctxt.Cursym.Pcln - if pcln == nil { - pcln = &pclntab_zpcln - } - - if pclntabFirstFunc == nil { - pclntabFirstFunc = Ctxt.Cursym - } - - funcstart = int32(len(ftab.P)) - funcstart += int32(-len(ftab.P)) & (int32(Thearch.Ptrsize) - 1) - - setaddr(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize), Ctxt.Cursym) - setuintxx(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize), uint64(funcstart), int64(Thearch.Ptrsize)) - - // fixed size of struct, checked below - off = funcstart - - end = funcstart + int32(Thearch.Ptrsize) + 3*4 + 5*4 + int32(pcln.Npcdata)*4 + int32(pcln.Nfuncdata)*int32(Thearch.Ptrsize) - if pcln.Nfuncdata > 0 && (end&int32(Thearch.Ptrsize-1) != 0) { - end += 4 - } - Symgrow(Ctxt, ftab, int64(end)) - - // entry uintptr - off = int32(setaddr(Ctxt, ftab, int64(off), Ctxt.Cursym)) - - // name int32 - off = int32(setuint32(Ctxt, ftab, int64(off), uint32(ftabaddstring(ftab, Ctxt.Cursym.Name)))) - - // args int32 - // TODO: Move into funcinfo. - off = int32(setuint32(Ctxt, ftab, int64(off), uint32(Ctxt.Cursym.Args))) - - // frame int32 - // This has been removed (it was never set quite correctly anyway). - // Nothing should use it. - // Leave an obviously incorrect value. - // TODO: Remove entirely. - off = int32(setuint32(Ctxt, ftab, int64(off), 0x1234567)) - - if pcln != &pclntab_zpcln { - renumberfiles(Ctxt, pcln.File, &pcln.Pcfile) - if false { - // Sanity check the new numbering - for pciterinit(Ctxt, &it, &pcln.Pcfile); it.done == 0; pciternext(&it) { - if it.value < 1 || it.value > Ctxt.Nhistfile { - Diag("bad file number in pcfile: %d not in range [1, %d]\n", it.value, Ctxt.Nhistfile) - errorexit() - } - } - } - } - - // pcdata - off = addpctab(ftab, off, &pcln.Pcsp) - - off = addpctab(ftab, off, &pcln.Pcfile) - off = addpctab(ftab, off, &pcln.Pcline) - off = int32(setuint32(Ctxt, ftab, int64(off), uint32(pcln.Npcdata))) - off = int32(setuint32(Ctxt, ftab, int64(off), uint32(pcln.Nfuncdata))) - for i = 0; i < int32(pcln.Npcdata); i++ { - off = addpctab(ftab, off, &pcln.Pcdata[i]) - } - - // funcdata, must be pointer-aligned and we're only int32-aligned. - // Missing funcdata will be 0 (nil pointer). - if pcln.Nfuncdata > 0 { - if off&int32(Thearch.Ptrsize-1) != 0 { - off += 4 - } - for i = 0; i < int32(pcln.Nfuncdata); i++ { - if pcln.Funcdata[i] == nil { - setuintxx(Ctxt, ftab, int64(off)+int64(Thearch.Ptrsize)*int64(i), uint64(pcln.Funcdataoff[i]), int64(Thearch.Ptrsize)) - } else { - // TODO: Dedup. - funcdata_bytes += pcln.Funcdata[i].Size - - setaddrplus(Ctxt, ftab, int64(off)+int64(Thearch.Ptrsize)*int64(i), pcln.Funcdata[i], pcln.Funcdataoff[i]) - } - } - - off += int32(pcln.Nfuncdata) * int32(Thearch.Ptrsize) - } - - if off != end { - Diag("bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, pcln.Npcdata, pcln.Nfuncdata, Thearch.Ptrsize) - errorexit() - } - - nfunc++ - } - - pclntabLastFunc = last - // Final entry of table is just end pc. - setaddrplus(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize), last, last.Size) - - // Start file table. - start := int32(len(ftab.P)) - - start += int32(-len(ftab.P)) & (int32(Thearch.Ptrsize) - 1) - pclntabFiletabOffset = start - setuint32(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize), uint32(start)) - - Symgrow(Ctxt, ftab, int64(start)+(int64(Ctxt.Nhistfile)+1)*4) - setuint32(Ctxt, ftab, int64(start), uint32(Ctxt.Nhistfile)) - for s := Ctxt.Filesyms; s != nil; s = s.Next { - setuint32(Ctxt, ftab, int64(start)+s.Value*4, uint32(ftabaddstring(ftab, s.Name))) - } - - ftab.Size = int64(len(ftab.P)) - - if Debug['v'] != 0 { - fmt.Fprintf(&Bso, "%5.2f pclntab=%d bytes, funcdata total %d bytes\n", obj.Cputime(), int64(ftab.Size), int64(funcdata_bytes)) - } -} - -const ( - BUCKETSIZE = 256 * MINFUNC - SUBBUCKETS = 16 - SUBBUCKETSIZE = BUCKETSIZE / SUBBUCKETS - NOIDX = 0x7fffffff -) - -// findfunctab generates a lookup table to quickly find the containing -// function for a pc. See src/runtime/symtab.go:findfunc for details. -func findfunctab() { - t := Linklookup(Ctxt, "runtime.findfunctab", 0) - t.Type = obj.SRODATA - t.Reachable = true - t.Local = true - - // find min and max address - min := Ctxt.Textp.Value - - max := int64(0) - for s := Ctxt.Textp; s != nil; s = s.Next { - max = s.Value + s.Size - } - - // for each subbucket, compute the minimum of all symbol indexes - // that map to that subbucket. - n := int32((max - min + SUBBUCKETSIZE - 1) / SUBBUCKETSIZE) - - indexes := make([]int32, n) - for i := int32(0); i < n; i++ { - indexes[i] = NOIDX - } - idx := int32(0) - var e *LSym - var i int32 - var p int64 - var q int64 - for s := Ctxt.Textp; s != nil; s = s.Next { - if container(s) != 0 { - continue - } - p = s.Value - e = s.Next - for container(e) != 0 { - e = e.Next - } - if e != nil { - q = e.Value - } else { - q = max - } - - //print("%d: [%lld %lld] %s\n", idx, p, q, s->name); - for ; p < q; p += SUBBUCKETSIZE { - i = int32((p - min) / SUBBUCKETSIZE) - if indexes[i] > idx { - indexes[i] = idx - } - } - - i = int32((q - 1 - min) / SUBBUCKETSIZE) - if indexes[i] > idx { - indexes[i] = idx - } - idx++ - } - - // allocate table - nbuckets := int32((max - min + BUCKETSIZE - 1) / BUCKETSIZE) - - Symgrow(Ctxt, t, 4*int64(nbuckets)+int64(n)) - - // fill in table - var base int32 - var j int32 - for i := int32(0); i < nbuckets; i++ { - base = indexes[i*SUBBUCKETS] - if base == NOIDX { - Diag("hole in findfunctab") - } - setuint32(Ctxt, t, int64(i)*(4+SUBBUCKETS), uint32(base)) - for j = 0; j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++ { - idx = indexes[i*SUBBUCKETS+j] - if idx == NOIDX { - Diag("hole in findfunctab") - } - if idx-base >= 256 { - Diag("too many functions in a findfunc bucket! %d/%d %d %d", i, nbuckets, j, idx-base) - } - - setuint8(Ctxt, t, int64(i)*(4+SUBBUCKETS)+4+int64(j), uint8(idx-base)) - } - } -} diff --git a/src/cmd/internal/ld/pe.go b/src/cmd/internal/ld/pe.go deleted file mode 100644 index cb82c053d8..0000000000 --- a/src/cmd/internal/ld/pe.go +++ /dev/null @@ -1,1253 +0,0 @@ -// Copyright 2009 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 ( - "cmd/internal/obj" - "encoding/binary" - "fmt" - "sort" - "strconv" - "strings" -) - -type IMAGE_FILE_HEADER struct { - Machine uint16 - NumberOfSections uint16 - TimeDateStamp uint32 - PointerToSymbolTable uint32 - NumberOfSymbols uint32 - SizeOfOptionalHeader uint16 - Characteristics uint16 -} - -type IMAGE_DATA_DIRECTORY struct { - VirtualAddress uint32 - Size uint32 -} - -type IMAGE_OPTIONAL_HEADER struct { - Magic uint16 - MajorLinkerVersion uint8 - MinorLinkerVersion uint8 - SizeOfCode uint32 - SizeOfInitializedData uint32 - SizeOfUninitializedData uint32 - AddressOfEntryPoint uint32 - BaseOfCode uint32 - BaseOfData uint32 - ImageBase uint32 - SectionAlignment uint32 - FileAlignment uint32 - MajorOperatingSystemVersion uint16 - MinorOperatingSystemVersion uint16 - MajorImageVersion uint16 - MinorImageVersion uint16 - MajorSubsystemVersion uint16 - MinorSubsystemVersion uint16 - Win32VersionValue uint32 - SizeOfImage uint32 - SizeOfHeaders uint32 - CheckSum uint32 - Subsystem uint16 - DllCharacteristics uint16 - SizeOfStackReserve uint32 - SizeOfStackCommit uint32 - SizeOfHeapReserve uint32 - SizeOfHeapCommit uint32 - LoaderFlags uint32 - NumberOfRvaAndSizes uint32 - DataDirectory [16]IMAGE_DATA_DIRECTORY -} - -type IMAGE_SECTION_HEADER struct { - Name [8]uint8 - VirtualSize uint32 - VirtualAddress uint32 - SizeOfRawData uint32 - PointerToRawData uint32 - PointerToRelocations uint32 - PointerToLineNumbers uint32 - NumberOfRelocations uint16 - NumberOfLineNumbers uint16 - Characteristics uint32 -} - -type IMAGE_IMPORT_DESCRIPTOR struct { - OriginalFirstThunk uint32 - TimeDateStamp uint32 - ForwarderChain uint32 - Name uint32 - FirstThunk uint32 -} - -type IMAGE_EXPORT_DIRECTORY struct { - Characteristics uint32 - TimeDateStamp uint32 - MajorVersion uint16 - MinorVersion uint16 - Name uint32 - Base uint32 - NumberOfFunctions uint32 - NumberOfNames uint32 - AddressOfFunctions uint32 - AddressOfNames uint32 - AddressOfNameOrdinals uint32 -} - -const ( - PEBASE = 0x00400000 - - // SectionAlignment must be greater than or equal to FileAlignment. - // The default is the page size for the architecture. - PESECTALIGN = 0x1000 - - // FileAlignment should be a power of 2 between 512 and 64 K, inclusive. - // The default is 512. If the SectionAlignment is less than - // the architecture's page size, then FileAlignment must match SectionAlignment. - PEFILEALIGN = 2 << 8 -) - -const ( - IMAGE_FILE_MACHINE_I386 = 0x14c - IMAGE_FILE_MACHINE_AMD64 = 0x8664 - IMAGE_FILE_RELOCS_STRIPPED = 0x0001 - IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002 - IMAGE_FILE_LINE_NUMS_STRIPPED = 0x0004 - IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020 - IMAGE_FILE_32BIT_MACHINE = 0x0100 - IMAGE_FILE_DEBUG_STRIPPED = 0x0200 - IMAGE_SCN_CNT_CODE = 0x00000020 - IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040 - IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080 - IMAGE_SCN_MEM_EXECUTE = 0x20000000 - IMAGE_SCN_MEM_READ = 0x40000000 - IMAGE_SCN_MEM_WRITE = 0x80000000 - IMAGE_SCN_MEM_DISCARDABLE = 0x2000000 - IMAGE_SCN_LNK_NRELOC_OVFL = 0x1000000 - IMAGE_SCN_ALIGN_32BYTES = 0x600000 - IMAGE_DIRECTORY_ENTRY_EXPORT = 0 - IMAGE_DIRECTORY_ENTRY_IMPORT = 1 - IMAGE_DIRECTORY_ENTRY_RESOURCE = 2 - IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3 - IMAGE_DIRECTORY_ENTRY_SECURITY = 4 - IMAGE_DIRECTORY_ENTRY_BASERELOC = 5 - IMAGE_DIRECTORY_ENTRY_DEBUG = 6 - IMAGE_DIRECTORY_ENTRY_COPYRIGHT = 7 - IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7 - IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8 - IMAGE_DIRECTORY_ENTRY_TLS = 9 - IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10 - IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11 - IMAGE_DIRECTORY_ENTRY_IAT = 12 - IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13 - IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14 - IMAGE_SUBSYSTEM_WINDOWS_GUI = 2 - IMAGE_SUBSYSTEM_WINDOWS_CUI = 3 -) - -// X64 -type PE64_IMAGE_OPTIONAL_HEADER struct { - Magic uint16 - MajorLinkerVersion uint8 - MinorLinkerVersion uint8 - SizeOfCode uint32 - SizeOfInitializedData uint32 - SizeOfUninitializedData uint32 - AddressOfEntryPoint uint32 - BaseOfCode uint32 - ImageBase uint64 - SectionAlignment uint32 - FileAlignment uint32 - MajorOperatingSystemVersion uint16 - MinorOperatingSystemVersion uint16 - MajorImageVersion uint16 - MinorImageVersion uint16 - MajorSubsystemVersion uint16 - MinorSubsystemVersion uint16 - Win32VersionValue uint32 - SizeOfImage uint32 - SizeOfHeaders uint32 - CheckSum uint32 - Subsystem uint16 - DllCharacteristics uint16 - SizeOfStackReserve uint64 - SizeOfStackCommit uint64 - SizeOfHeapReserve uint64 - SizeOfHeapCommit uint64 - LoaderFlags uint32 - NumberOfRvaAndSizes uint32 - DataDirectory [16]IMAGE_DATA_DIRECTORY -} - -// Copyright 2009 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. - -// PE (Portable Executable) file writing -// http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx - -// DOS stub that prints out -// "This program cannot be run in DOS mode." -var dosstub = []uint8{ - 0x4d, - 0x5a, - 0x90, - 0x00, - 0x03, - 0x00, - 0x04, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0xff, - 0xff, - 0x00, - 0x00, - 0x8b, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x40, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x80, - 0x00, - 0x00, - 0x00, - 0x0e, - 0x1f, - 0xba, - 0x0e, - 0x00, - 0xb4, - 0x09, - 0xcd, - 0x21, - 0xb8, - 0x01, - 0x4c, - 0xcd, - 0x21, - 0x54, - 0x68, - 0x69, - 0x73, - 0x20, - 0x70, - 0x72, - 0x6f, - 0x67, - 0x72, - 0x61, - 0x6d, - 0x20, - 0x63, - 0x61, - 0x6e, - 0x6e, - 0x6f, - 0x74, - 0x20, - 0x62, - 0x65, - 0x20, - 0x72, - 0x75, - 0x6e, - 0x20, - 0x69, - 0x6e, - 0x20, - 0x44, - 0x4f, - 0x53, - 0x20, - 0x6d, - 0x6f, - 0x64, - 0x65, - 0x2e, - 0x0d, - 0x0d, - 0x0a, - 0x24, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, -} - -var rsrcsym *LSym - -var strtbl []byte - -var PESECTHEADR int32 - -var PEFILEHEADR int32 - -var pe64 int - -var pensect int - -var nextsectoff int - -var nextfileoff int - -var textsect int - -var datasect int - -var bsssect int - -var fh IMAGE_FILE_HEADER - -var oh IMAGE_OPTIONAL_HEADER - -var oh64 PE64_IMAGE_OPTIONAL_HEADER - -var sh [16]IMAGE_SECTION_HEADER - -var dd []IMAGE_DATA_DIRECTORY - -type Imp struct { - s *LSym - off uint64 - next *Imp - argsize int -} - -type Dll struct { - name string - nameoff uint64 - thunkoff uint64 - ms *Imp - next *Dll -} - -var dr *Dll - -var dexport [1024]*LSym - -var nexport int - -type COFFSym struct { - sym *LSym - strtbloff int - sect int - value int64 - typ uint16 -} - -var coffsym []COFFSym - -var ncoffsym int - -func addpesection(name string, sectsize int, filesize int) *IMAGE_SECTION_HEADER { - if pensect == 16 { - Diag("too many sections") - errorexit() - } - - h := &sh[pensect] - pensect++ - copy(h.Name[:], name) - h.VirtualSize = uint32(sectsize) - h.VirtualAddress = uint32(nextsectoff) - nextsectoff = int(Rnd(int64(nextsectoff)+int64(sectsize), PESECTALIGN)) - h.PointerToRawData = uint32(nextfileoff) - if filesize > 0 { - h.SizeOfRawData = uint32(Rnd(int64(filesize), PEFILEALIGN)) - nextfileoff += int(h.SizeOfRawData) - } - - return h -} - -func chksectoff(h *IMAGE_SECTION_HEADER, off int64) { - if off != int64(h.PointerToRawData) { - Diag("%s.PointerToRawData = %#x, want %#x", cstring(h.Name[:]), uint64(int64(h.PointerToRawData)), uint64(off)) - errorexit() - } -} - -func chksectseg(h *IMAGE_SECTION_HEADER, s *Segment) { - if s.Vaddr-PEBASE != uint64(h.VirtualAddress) { - Diag("%s.VirtualAddress = %#x, want %#x", cstring(h.Name[:]), uint64(int64(h.VirtualAddress)), uint64(int64(s.Vaddr-PEBASE))) - errorexit() - } - - if s.Fileoff != uint64(h.PointerToRawData) { - Diag("%s.PointerToRawData = %#x, want %#x", cstring(h.Name[:]), uint64(int64(h.PointerToRawData)), uint64(int64(s.Fileoff))) - errorexit() - } -} - -func Peinit() { - var l int - - switch Thearch.Thechar { - // 64-bit architectures - case '6': - pe64 = 1 - - l = binary.Size(&oh64) - dd = oh64.DataDirectory[:] - - // 32-bit architectures - default: - l = binary.Size(&oh) - - dd = oh.DataDirectory[:] - } - - PEFILEHEADR = int32(Rnd(int64(len(dosstub)+binary.Size(&fh)+l+binary.Size(&sh)), PEFILEALIGN)) - PESECTHEADR = int32(Rnd(int64(PEFILEHEADR), PESECTALIGN)) - nextsectoff = int(PESECTHEADR) - nextfileoff = int(PEFILEHEADR) - - // some mingw libs depend on this symbol, for example, FindPESectionByName - xdefine("__image_base__", obj.SDATA, PEBASE) - - xdefine("_image_base__", obj.SDATA, PEBASE) -} - -func pewrite() { - Cseek(0) - if Linkmode != LinkExternal { - Cwrite(dosstub) - strnput("PE", 4) - } - - binary.Write(&coutbuf, binary.LittleEndian, &fh) - - if pe64 != 0 { - binary.Write(&coutbuf, binary.LittleEndian, &oh64) - } else { - binary.Write(&coutbuf, binary.LittleEndian, &oh) - } - binary.Write(&coutbuf, binary.LittleEndian, sh[:pensect]) -} - -func strput(s string) { - coutbuf.WriteString(s) - Cput(0) - // string must be padded to even size - if (len(s)+1)%2 != 0 { - Cput(0) - } -} - -func initdynimport() *Dll { - var d *Dll - - dr = nil - var m *Imp - for s := Ctxt.Allsym; s != nil; s = s.Allsym { - if !s.Reachable || s.Type != obj.SDYNIMPORT { - continue - } - for d = dr; d != nil; d = d.next { - if d.name == s.Dynimplib { - m = new(Imp) - break - } - } - - if d == nil { - d = new(Dll) - d.name = s.Dynimplib - d.next = dr - dr = d - m = new(Imp) - } - - // Because external link requires properly stdcall decorated name, - // all external symbols in runtime use %n to denote that the number - // of uinptrs this function consumes. Store the argsize and discard - // the %n suffix if any. - m.argsize = -1 - if i := strings.IndexByte(s.Extname, '%'); i >= 0 { - var err error - m.argsize, err = strconv.Atoi(s.Extname[i+1:]) - if err != nil { - Diag("failed to parse stdcall decoration: %v", err) - } - m.argsize *= Thearch.Ptrsize - s.Extname = s.Extname[:i] - } - - m.s = s - m.next = d.ms - d.ms = m - } - - if Linkmode == LinkExternal { - // Add real symbol name - for d := dr; d != nil; d = d.next { - for m = d.ms; m != nil; m = m.next { - m.s.Type = obj.SDATA - Symgrow(Ctxt, m.s, int64(Thearch.Ptrsize)) - dynName := m.s.Extname - // only windows/386 requires stdcall decoration - if Thearch.Thechar == '8' && m.argsize >= 0 { - dynName += fmt.Sprintf("@%d", m.argsize) - } - dynSym := Linklookup(Ctxt, dynName, 0) - dynSym.Reachable = true - dynSym.Type = obj.SHOSTOBJ - r := Addrel(m.s) - r.Sym = dynSym - r.Off = 0 - r.Siz = uint8(Thearch.Ptrsize) - r.Type = obj.R_ADDR - - // pre-allocate symtab entries for those symbols - dynSym.Dynid = int32(ncoffsym) - ncoffsym++ - } - } - } else { - dynamic := Linklookup(Ctxt, ".windynamic", 0) - dynamic.Reachable = true - dynamic.Type = obj.SWINDOWS - for d := dr; d != nil; d = d.next { - for m = d.ms; m != nil; m = m.next { - m.s.Type = obj.SWINDOWS | obj.SSUB - m.s.Sub = dynamic.Sub - dynamic.Sub = m.s - m.s.Value = dynamic.Size - dynamic.Size += int64(Thearch.Ptrsize) - } - - dynamic.Size += int64(Thearch.Ptrsize) - } - } - - return dr -} - -// peimporteddlls returns the gcc command line argument to link all imported -// DLLs. -func peimporteddlls() []string { - var dlls []string - - for d := dr; d != nil; d = d.next { - dlls = append(dlls, "-l"+strings.TrimSuffix(d.name, ".dll")) - } - - return dlls -} - -func addimports(datsect *IMAGE_SECTION_HEADER) { - startoff := Cpos() - dynamic := Linklookup(Ctxt, ".windynamic", 0) - - // skip import descriptor table (will write it later) - n := uint64(0) - - for d := dr; d != nil; d = d.next { - n++ - } - Cseek(startoff + int64(binary.Size(&IMAGE_IMPORT_DESCRIPTOR{}))*int64(n+1)) - - // write dll names - for d := dr; d != nil; d = d.next { - d.nameoff = uint64(Cpos()) - uint64(startoff) - strput(d.name) - } - - // write function names - var m *Imp - for d := dr; d != nil; d = d.next { - for m = d.ms; m != nil; m = m.next { - m.off = uint64(nextsectoff) + uint64(Cpos()) - uint64(startoff) - Wputl(0) // hint - strput(m.s.Extname) - } - } - - // write OriginalFirstThunks - oftbase := uint64(Cpos()) - uint64(startoff) - - n = uint64(Cpos()) - for d := dr; d != nil; d = d.next { - d.thunkoff = uint64(Cpos()) - n - for m = d.ms; m != nil; m = m.next { - if pe64 != 0 { - Vputl(m.off) - } else { - Lputl(uint32(m.off)) - } - } - - if pe64 != 0 { - Vputl(0) - } else { - Lputl(0) - } - } - - // add pe section and pad it at the end - n = uint64(Cpos()) - uint64(startoff) - - isect := addpesection(".idata", int(n), int(n)) - isect.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE - chksectoff(isect, startoff) - strnput("", int(uint64(isect.SizeOfRawData)-n)) - endoff := Cpos() - - // write FirstThunks (allocated in .data section) - ftbase := uint64(dynamic.Value) - uint64(datsect.VirtualAddress) - PEBASE - - Cseek(int64(uint64(datsect.PointerToRawData) + ftbase)) - for d := dr; d != nil; d = d.next { - for m = d.ms; m != nil; m = m.next { - if pe64 != 0 { - Vputl(m.off) - } else { - Lputl(uint32(m.off)) - } - } - - if pe64 != 0 { - Vputl(0) - } else { - Lputl(0) - } - } - - // finally write import descriptor table - Cseek(startoff) - - for d := dr; d != nil; d = d.next { - Lputl(uint32(uint64(isect.VirtualAddress) + oftbase + d.thunkoff)) - Lputl(0) - Lputl(0) - Lputl(uint32(uint64(isect.VirtualAddress) + d.nameoff)) - Lputl(uint32(uint64(datsect.VirtualAddress) + ftbase + d.thunkoff)) - } - - Lputl(0) //end - Lputl(0) - Lputl(0) - Lputl(0) - Lputl(0) - - // update data directory - dd[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = isect.VirtualAddress - - dd[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = isect.VirtualSize - dd[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = uint32(dynamic.Value - PEBASE) - dd[IMAGE_DIRECTORY_ENTRY_IAT].Size = uint32(dynamic.Size) - - Cseek(endoff) -} - -type pescmp []*LSym - -func (x pescmp) Len() int { - return len(x) -} - -func (x pescmp) Swap(i, j int) { - x[i], x[j] = x[j], x[i] -} - -func (x pescmp) Less(i, j int) bool { - s1 := x[i] - s2 := x[j] - return stringsCompare(s1.Extname, s2.Extname) < 0 -} - -func initdynexport() { - nexport = 0 - for s := Ctxt.Allsym; s != nil; s = s.Allsym { - if !s.Reachable || s.Cgoexport&CgoExportDynamic == 0 { - continue - } - if nexport+1 > len(dexport) { - Diag("pe dynexport table is full") - errorexit() - } - - dexport[nexport] = s - nexport++ - } - - sort.Sort(pescmp(dexport[:nexport])) -} - -func addexports() { - var e IMAGE_EXPORT_DIRECTORY - - size := binary.Size(&e) + 10*nexport + len(outfile) + 1 - for i := 0; i < nexport; i++ { - size += len(dexport[i].Extname) + 1 - } - - if nexport == 0 { - return - } - - sect := addpesection(".edata", size, size) - sect.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ - chksectoff(sect, Cpos()) - va := int(sect.VirtualAddress) - dd[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress = uint32(va) - dd[IMAGE_DIRECTORY_ENTRY_EXPORT].Size = sect.VirtualSize - - va_name := va + binary.Size(&e) + nexport*4 - va_addr := va + binary.Size(&e) - va_na := va + binary.Size(&e) + nexport*8 - - e.Characteristics = 0 - e.MajorVersion = 0 - e.MinorVersion = 0 - e.NumberOfFunctions = uint32(nexport) - e.NumberOfNames = uint32(nexport) - e.Name = uint32(va+binary.Size(&e)) + uint32(nexport)*10 // Program names. - e.Base = 1 - e.AddressOfFunctions = uint32(va_addr) - e.AddressOfNames = uint32(va_name) - e.AddressOfNameOrdinals = uint32(va_na) - - // put IMAGE_EXPORT_DIRECTORY - binary.Write(&coutbuf, binary.LittleEndian, &e) - - // put EXPORT Address Table - for i := 0; i < nexport; i++ { - Lputl(uint32(dexport[i].Value - PEBASE)) - } - - // put EXPORT Name Pointer Table - v := int(e.Name + uint32(len(outfile)) + 1) - - for i := 0; i < nexport; i++ { - Lputl(uint32(v)) - v += len(dexport[i].Extname) + 1 - } - - // put EXPORT Ordinal Table - for i := 0; i < nexport; i++ { - Wputl(uint16(i)) - } - - // put Names - strnput(outfile, len(outfile)+1) - - for i := 0; i < nexport; i++ { - strnput(dexport[i].Extname, len(dexport[i].Extname)+1) - } - strnput("", int(sect.SizeOfRawData-uint32(size))) -} - -// perelocsect relocates symbols from first in section sect, and returns -// the total number of relocations emitted. -func perelocsect(sect *Section, first *LSym) int { - // If main section has no bits, nothing to relocate. - if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { - return 0 - } - - relocs := 0 - - sect.Reloff = uint64(Cpos()) - var sym *LSym - for sym = first; sym != nil; sym = sym.Next { - if !sym.Reachable { - continue - } - if uint64(sym.Value) >= sect.Vaddr { - break - } - } - - eaddr := int32(sect.Vaddr + sect.Length) - var r *Reloc - var ri int - for ; sym != nil; sym = sym.Next { - if !sym.Reachable { - continue - } - if sym.Value >= int64(eaddr) { - break - } - Ctxt.Cursym = sym - - for ri = 0; ri < len(sym.R); ri++ { - r = &sym.R[ri] - if r.Done != 0 { - continue - } - if r.Xsym == nil { - Diag("missing xsym in relocation") - continue - } - - if r.Xsym.Dynid < 0 { - Diag("reloc %d to non-coff symbol %s (outer=%s) %d", r.Type, r.Sym.Name, r.Xsym.Name, r.Sym.Type) - } - if !Thearch.PEreloc1(r, int64(uint64(sym.Value+int64(r.Off))-PEBASE)) { - Diag("unsupported obj reloc %d/%d to %s", r.Type, r.Siz, r.Sym.Name) - } - - relocs++ - } - } - - sect.Rellen = uint64(Cpos()) - sect.Reloff - - return relocs -} - -// peemitreloc emits relocation entries for go.o in external linking. -func peemitreloc(text, data *IMAGE_SECTION_HEADER) { - for Cpos()&7 != 0 { - Cput(0) - } - - text.PointerToRelocations = uint32(Cpos()) - // first entry: extended relocs - Lputl(0) // placeholder for number of relocation + 1 - Lputl(0) - Wputl(0) - - n := perelocsect(Segtext.Sect, Ctxt.Textp) + 1 - for sect := Segtext.Sect.Next; sect != nil; sect = sect.Next { - n += perelocsect(sect, datap) - } - - cpos := Cpos() - Cseek(int64(text.PointerToRelocations)) - Lputl(uint32(n)) - Cseek(cpos) - if n > 0x10000 { - n = 0x10000 - text.Characteristics |= IMAGE_SCN_LNK_NRELOC_OVFL - } else { - text.PointerToRelocations += 10 // skip the extend reloc entry - } - text.NumberOfRelocations = uint16(n - 1) - - data.PointerToRelocations = uint32(cpos) - // first entry: extended relocs - Lputl(0) // placeholder for number of relocation + 1 - Lputl(0) - Wputl(0) - - n = 1 - for sect := Segdata.Sect; sect != nil; sect = sect.Next { - n += perelocsect(sect, datap) - } - - cpos = Cpos() - Cseek(int64(data.PointerToRelocations)) - Lputl(uint32(n)) - Cseek(cpos) - if n > 0x10000 { - n = 0x10000 - data.Characteristics |= IMAGE_SCN_LNK_NRELOC_OVFL - } else { - data.PointerToRelocations += 10 // skip the extend reloc entry - } - data.NumberOfRelocations = uint16(n - 1) -} - -func dope() { - /* relocation table */ - rel := Linklookup(Ctxt, ".rel", 0) - - rel.Reachable = true - rel.Type = obj.SELFROSECT - - initdynimport() - initdynexport() -} - -func strtbladd(name string) int { - off := len(strtbl) + 4 // offset includes 4-byte length at beginning of table - strtbl = append(strtbl, name...) - strtbl = append(strtbl, 0) - return off -} - -/* - * For more than 8 characters section names, name contains a slash (/) that is - * followed by an ASCII representation of a decimal number that is an offset into - * the string table. - * reference: pecoff_v8.docx Page 24. - * - */ -func newPEDWARFSection(name string, size int64) *IMAGE_SECTION_HEADER { - if size == 0 { - return nil - } - - off := strtbladd(name) - s := fmt.Sprintf("/%d", off) - h := addpesection(s, int(size), int(size)) - h.Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE - - return h -} - -func addpesym(s *LSym, name string, type_ int, addr int64, size int64, ver int, gotype *LSym) { - if s == nil { - return - } - - if s.Sect == nil && type_ != 'U' { - return - } - - switch type_ { - default: - return - - case 'D', 'B', 'T', 'U': - break - } - - if coffsym != nil { - // only windows/386 requires underscore prefix on external symbols - if Thearch.Thechar == '8' && Linkmode == LinkExternal && (s.Type == obj.SHOSTOBJ || s.Cgoexport != 0) && s.Name == s.Extname { - s.Name = "_" + s.Name - } - cs := &coffsym[ncoffsym] - cs.sym = s - if len(s.Name) > 8 { - cs.strtbloff = strtbladd(s.Name) - } - // Note: although address of runtime.edata (type SDATA) is at the start of .bss section - // it still belongs to the .data section, not the .bss section. - if uint64(s.Value) >= Segdata.Vaddr+Segdata.Filelen && s.Type != obj.SDATA && Linkmode == LinkExternal { - cs.value = int64(uint64(s.Value) - Segdata.Vaddr - Segdata.Filelen) - cs.sect = bsssect - } else if uint64(s.Value) >= Segdata.Vaddr { - cs.value = int64(uint64(s.Value) - Segdata.Vaddr) - cs.sect = datasect - } else if uint64(s.Value) >= Segtext.Vaddr { - cs.value = int64(uint64(s.Value) - Segtext.Vaddr) - cs.sect = textsect - } else if type_ == 'U' { - cs.value = 0 - cs.typ = IMAGE_SYM_DTYPE_FUNCTION - } else { - cs.value = 0 - cs.sect = 0 - Diag("addpesym %#x", addr) - } - } - - s.Dynid = int32(ncoffsym) - ncoffsym++ -} - -func addpesymtable() { - if Debug['s'] == 0 { - genasmsym(addpesym) - coffsym = make([]COFFSym, ncoffsym) - ncoffsym = 0 - if Linkmode == LinkExternal { - for d := dr; d != nil; d = d.next { - for m := d.ms; m != nil; m = m.next { - s := m.s.R[0].Xsym - addpesym(s, s.Name, 'U', 0, int64(Thearch.Ptrsize), 0, nil) - } - } - } - genasmsym(addpesym) - } - size := len(strtbl) + 4 + 18*ncoffsym - - var h *IMAGE_SECTION_HEADER - if Linkmode != LinkExternal { - // We do not really need .symtab for go.o, and if we have one, ld - // will also include it in the exe, and that will confuse windows. - h = addpesection(".symtab", size, size) - h.Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE - chksectoff(h, Cpos()) - } - fh.PointerToSymbolTable = uint32(Cpos()) - fh.NumberOfSymbols = uint32(ncoffsym) - - // put COFF symbol table - var s *COFFSym - for i := 0; i < ncoffsym; i++ { - s = &coffsym[i] - if s.strtbloff == 0 { - strnput(s.sym.Name, 8) - } else { - Lputl(0) - Lputl(uint32(s.strtbloff)) - } - - Lputl(uint32(s.value)) - Wputl(uint16(s.sect)) - if s.typ != 0 { - Wputl(s.typ) - } else if Linkmode == LinkExternal { - Wputl(0) - } else { - Wputl(0x0308) // "array of structs" - } - Cput(2) // storage class: external - Cput(0) // no aux entries - } - - // put COFF string table - Lputl(uint32(len(strtbl)) + 4) - - for i := 0; i < len(strtbl); i++ { - Cput(uint8(strtbl[i])) - } - if Linkmode != LinkExternal { - strnput("", int(h.SizeOfRawData-uint32(size))) - } -} - -func setpersrc(sym *LSym) { - if rsrcsym != nil { - Diag("too many .rsrc sections") - } - - rsrcsym = sym -} - -func addpersrc() { - if rsrcsym == nil { - return - } - - h := addpesection(".rsrc", int(rsrcsym.Size), int(rsrcsym.Size)) - h.Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_INITIALIZED_DATA - chksectoff(h, Cpos()) - - // relocation - var p []byte - var r *Reloc - var val uint32 - for ri := 0; ri < len(rsrcsym.R); ri++ { - r = &rsrcsym.R[ri] - p = rsrcsym.P[r.Off:] - val = uint32(int64(h.VirtualAddress) + r.Add) - - // 32-bit little-endian - p[0] = byte(val) - - p[1] = byte(val >> 8) - p[2] = byte(val >> 16) - p[3] = byte(val >> 24) - } - - Cwrite(rsrcsym.P) - strnput("", int(int64(h.SizeOfRawData)-rsrcsym.Size)) - - // update data directory - dd[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = h.VirtualAddress - - dd[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = h.VirtualSize -} - -func Asmbpe() { - switch Thearch.Thechar { - default: - Exitf("unknown PE architecture: %v", Thearch.Thechar) - case '6': - fh.Machine = IMAGE_FILE_MACHINE_AMD64 - case '8': - fh.Machine = IMAGE_FILE_MACHINE_I386 - } - - t := addpesection(".text", int(Segtext.Length), int(Segtext.Length)) - t.Characteristics = IMAGE_SCN_CNT_CODE | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ - chksectseg(t, &Segtext) - textsect = pensect - - var d *IMAGE_SECTION_HEADER - if Linkmode != LinkExternal { - d = addpesection(".data", int(Segdata.Length), int(Segdata.Filelen)) - d.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE - chksectseg(d, &Segdata) - datasect = pensect - } else { - d = addpesection(".data", int(Segdata.Filelen), int(Segdata.Filelen)) - d.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_ALIGN_32BYTES - chksectseg(d, &Segdata) - datasect = pensect - - b := addpesection(".bss", int(Segdata.Length-Segdata.Filelen), 0) - b.Characteristics = IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_ALIGN_32BYTES - b.PointerToRawData = 0 - bsssect = pensect - } - - if Debug['s'] == 0 { - dwarfaddpeheaders() - } - - Cseek(int64(nextfileoff)) - if Linkmode != LinkExternal { - addimports(d) - addexports() - } - addpesymtable() - addpersrc() - if Linkmode == LinkExternal { - peemitreloc(t, d) - } - - fh.NumberOfSections = uint16(pensect) - - // Being able to produce identical output for identical input is - // much more beneficial than having build timestamp in the header. - fh.TimeDateStamp = 0 - - if Linkmode == LinkExternal { - fh.Characteristics = IMAGE_FILE_LINE_NUMS_STRIPPED - } else { - fh.Characteristics = IMAGE_FILE_RELOCS_STRIPPED | IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DEBUG_STRIPPED - } - if pe64 != 0 { - fh.SizeOfOptionalHeader = uint16(binary.Size(&oh64)) - fh.Characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE - oh64.Magic = 0x20b // PE32+ - } else { - fh.SizeOfOptionalHeader = uint16(binary.Size(&oh)) - fh.Characteristics |= IMAGE_FILE_32BIT_MACHINE - oh.Magic = 0x10b // PE32 - oh.BaseOfData = d.VirtualAddress - } - - // Fill out both oh64 and oh. We only use one. Oh well. - oh64.MajorLinkerVersion = 3 - - oh.MajorLinkerVersion = 3 - oh64.MinorLinkerVersion = 0 - oh.MinorLinkerVersion = 0 - oh64.SizeOfCode = t.SizeOfRawData - oh.SizeOfCode = t.SizeOfRawData - oh64.SizeOfInitializedData = d.SizeOfRawData - oh.SizeOfInitializedData = d.SizeOfRawData - oh64.SizeOfUninitializedData = 0 - oh.SizeOfUninitializedData = 0 - if Linkmode != LinkExternal { - oh64.AddressOfEntryPoint = uint32(Entryvalue() - PEBASE) - oh.AddressOfEntryPoint = uint32(Entryvalue() - PEBASE) - } - oh64.BaseOfCode = t.VirtualAddress - oh.BaseOfCode = t.VirtualAddress - oh64.ImageBase = PEBASE - oh.ImageBase = PEBASE - oh64.SectionAlignment = PESECTALIGN - oh.SectionAlignment = PESECTALIGN - oh64.FileAlignment = PEFILEALIGN - oh.FileAlignment = PEFILEALIGN - oh64.MajorOperatingSystemVersion = 4 - oh.MajorOperatingSystemVersion = 4 - oh64.MinorOperatingSystemVersion = 0 - oh.MinorOperatingSystemVersion = 0 - oh64.MajorImageVersion = 1 - oh.MajorImageVersion = 1 - oh64.MinorImageVersion = 0 - oh.MinorImageVersion = 0 - oh64.MajorSubsystemVersion = 4 - oh.MajorSubsystemVersion = 4 - oh64.MinorSubsystemVersion = 0 - oh.MinorSubsystemVersion = 0 - oh64.SizeOfImage = uint32(nextsectoff) - oh.SizeOfImage = uint32(nextsectoff) - oh64.SizeOfHeaders = uint32(PEFILEHEADR) - oh.SizeOfHeaders = uint32(PEFILEHEADR) - if headstring == "windowsgui" { - oh64.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI - oh.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI - } else { - oh64.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI - oh.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI - } - - // Disable stack growth as we don't want Windows to - // fiddle with the thread stack limits, which we set - // ourselves to circumvent the stack checks in the - // Windows exception dispatcher. - // Commit size must be strictly less than reserve - // size otherwise reserve will be rounded up to a - // larger size, as verified with VMMap. - - // Go code would be OK with 64k stacks, but we need larger stacks for cgo. - // That default stack reserve size affects only the main thread, - // for other threads we specify stack size in runtime explicitly - // (runtime knows whether cgo is enabled or not). - // If you change stack reserve sizes here, - // change STACKSIZE in runtime/cgo/gcc_windows_{386,amd64}.c and correspondent - // CreateThread parameter in runtime.newosproc as well. - if !iscgo { - oh64.SizeOfStackReserve = 0x00020000 - oh.SizeOfStackReserve = 0x00020000 - oh64.SizeOfStackCommit = 0x00001000 - oh.SizeOfStackCommit = 0x00001000 - } else { - oh64.SizeOfStackReserve = 0x00200000 - oh.SizeOfStackReserve = 0x00100000 - - // account for 2 guard pages - oh64.SizeOfStackCommit = 0x00200000 - 0x2000 - - oh.SizeOfStackCommit = 0x00100000 - 0x2000 - } - - oh64.SizeOfHeapReserve = 0x00100000 - oh.SizeOfHeapReserve = 0x00100000 - oh64.SizeOfHeapCommit = 0x00001000 - oh.SizeOfHeapCommit = 0x00001000 - oh64.NumberOfRvaAndSizes = 16 - oh.NumberOfRvaAndSizes = 16 - - pewrite() -} diff --git a/src/cmd/internal/ld/pobj.go b/src/cmd/internal/ld/pobj.go deleted file mode 100644 index 8568744c3d..0000000000 --- a/src/cmd/internal/ld/pobj.go +++ /dev/null @@ -1,253 +0,0 @@ -// Inferno utils/6l/obj.c -// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package ld - -import ( - "cmd/internal/obj" - "flag" - "fmt" - "os" - "strings" -) - -var pkglistfornote []byte - -// Reading object files. - -func Ldmain() { - Ctxt = linknew(Thelinkarch) - Ctxt.Thechar = int32(Thearch.Thechar) - Ctxt.Thestring = Thestring - Ctxt.Diag = Diag - Ctxt.Bso = &Bso - - Bso = *obj.Binitw(os.Stdout) - Debug = [128]int{} - nerrors = 0 - outfile = "" - HEADTYPE = -1 - INITTEXT = -1 - INITDAT = -1 - INITRND = -1 - INITENTRY = "" - Linkmode = LinkAuto - - // For testing behavior of go command when tools crash. - // Undocumented, not in standard flag parser to avoid - // exposing in usage message. - for _, arg := range os.Args { - if arg == "-crash_for_testing" { - *(*int)(nil) = 0 - } - } - - if Thearch.Thechar == '5' && Ctxt.Goarm == 5 { - Debug['F'] = 1 - } - - obj.Flagcount("1", "use alternate profiling code", &Debug['1']) - if Thearch.Thechar == '6' { - obj.Flagcount("8", "assume 64-bit addresses", &Debug['8']) - } - obj.Flagfn1("B", "add an ELF NT_GNU_BUILD_ID `note` when using ELF", addbuildinfo) - obj.Flagcount("C", "check Go calls to C code", &Debug['C']) - obj.Flagint64("D", "set data segment `address`", &INITDAT) - obj.Flagstr("E", "set `entry` symbol name", &INITENTRY) - if Thearch.Thechar == '5' { - obj.Flagcount("G", "debug pseudo-ops", &Debug['G']) - } - obj.Flagfn1("I", "use `linker` as ELF dynamic linker", setinterp) - obj.Flagfn1("L", "add specified `directory` to library path", Lflag) - obj.Flagfn1("H", "set header `type`", setheadtype) - obj.Flagcount("K", "add stack underflow checks", &Debug['K']) - if Thearch.Thechar == '5' { - obj.Flagcount("M", "disable software div/mod", &Debug['M']) - } - obj.Flagcount("O", "print pc-line tables", &Debug['O']) - obj.Flagcount("Q", "debug byte-register code gen", &Debug['Q']) - if Thearch.Thechar == '5' { - obj.Flagcount("P", "debug code generation", &Debug['P']) - } - obj.Flagint32("R", "set address rounding `quantum`", &INITRND) - obj.Flagcount("nil", "check type signatures", &Debug['S']) - obj.Flagint64("T", "set text segment `address`", &INITTEXT) - obj.Flagfn0("V", "print version and exit", doversion) - obj.Flagcount("W", "disassemble input", &Debug['W']) - obj.Flagfn1("X", "set the value of a string variable; the next two arguments are its name and value", addstrdata1) - obj.Flagcount("Z", "clear stack frame on entry", &Debug['Z']) - obj.Flagcount("a", "disassemble output", &Debug['a']) - flag.Var(&Buildmode, "buildmode", "set build `mode`") - obj.Flagcount("c", "dump call graph", &Debug['c']) - obj.Flagcount("d", "disable dynamic executable", &Debug['d']) - obj.Flagstr("extld", "use `linker` when linking in external mode", &extld) - obj.Flagstr("extldflags", "pass `flags` to external linker", &extldflags) - obj.Flagcount("f", "ignore version mismatch", &Debug['f']) - obj.Flagcount("g", "disable go package data checks", &Debug['g']) - obj.Flagstr("installsuffix", "set package directory `suffix`", &flag_installsuffix) - obj.Flagstr("k", "set field tracking `symbol`", &tracksym) - obj.Flagfn1("linkmode", "set link `mode` (internal, external, auto)", setlinkmode) - flag.BoolVar(&Linkshared, "linkshared", false, "link against installed Go shared libraries") - obj.Flagcount("n", "dump symbol table", &Debug['n']) - obj.Flagstr("o", "write output to `file`", &outfile) - flag.Var(&rpath, "r", "set the ELF dynamic linker search `path` to dir1:dir2:...") - obj.Flagcount("race", "enable race detector", &flag_race) - obj.Flagcount("s", "disable symbol table", &Debug['s']) - var flagShared int - if Thearch.Thechar == '5' || Thearch.Thechar == '6' { - obj.Flagcount("shared", "generate shared object (implies -linkmode external)", &flagShared) - } - obj.Flagstr("tmpdir", "use `directory` for temporary files", &tmpdir) - obj.Flagcount("u", "reject unsafe packages", &Debug['u']) - obj.Flagcount("v", "print link trace", &Debug['v']) - obj.Flagcount("w", "disable DWARF generation", &Debug['w']) - - // Clumsy hack to preserve old behavior of -X taking two arguments. - for i := 0; i < len(os.Args); i++ { - arg := os.Args[i] - if (arg == "--X" || arg == "-X") && i+2 < len(os.Args) { - os.Args[i+2] = "-X=VALUE:" + os.Args[i+2] - i += 2 - } else if (strings.HasPrefix(arg, "--X=") || strings.HasPrefix(arg, "-X=")) && i+1 < len(os.Args) { - os.Args[i+1] = "-X=VALUE:" + os.Args[i+1] - i++ - } - } - obj.Flagstr("cpuprofile", "write cpu profile to `file`", &cpuprofile) - obj.Flagstr("memprofile", "write memory profile to `file`", &memprofile) - obj.Flagint64("memprofilerate", "set runtime.MemProfileRate to `rate`", &memprofilerate) - obj.Flagparse(usage) - startProfile() - Ctxt.Bso = &Bso - Ctxt.Debugvlog = int32(Debug['v']) - if flagShared != 0 { - if Buildmode == BuildmodeExe { - Buildmode = BuildmodeCShared - } else if Buildmode != BuildmodeCShared { - Exitf("-shared and -buildmode=%s are incompatible", Buildmode.String()) - } - } - - if Buildmode != BuildmodeShared && flag.NArg() != 1 { - usage() - } - - if outfile == "" { - if HEADTYPE == obj.Hwindows { - outfile = fmt.Sprintf("%c.out.exe", Thearch.Thechar) - } else { - outfile = fmt.Sprintf("%c.out", Thearch.Thechar) - } - } - - libinit() // creates outfile - - if HEADTYPE == -1 { - HEADTYPE = int32(headtype(goos)) - } - Ctxt.Headtype = int(HEADTYPE) - if headstring == "" { - headstring = Headstr(int(HEADTYPE)) - } - - Thearch.Archinit() - - if Linkshared && !Iself { - Exitf("-linkshared can only be used on elf systems") - } - - if Debug['v'] != 0 { - fmt.Fprintf(&Bso, "HEADER = -H%d -T0x%x -D0x%x -R0x%x\n", HEADTYPE, uint64(INITTEXT), uint64(INITDAT), uint32(INITRND)) - } - Bso.Flush() - - if Buildmode == BuildmodeShared { - for i := 0; i < flag.NArg(); i++ { - arg := flag.Arg(i) - parts := strings.SplitN(arg, "=", 2) - var pkgpath, file string - if len(parts) == 1 { - pkgpath, file = "main", arg - } else { - pkgpath, file = parts[0], parts[1] - } - pkglistfornote = append(pkglistfornote, pkgpath...) - pkglistfornote = append(pkglistfornote, '\n') - addlibpath(Ctxt, "command line", "command line", file, pkgpath, "") - } - } else { - addlibpath(Ctxt, "command line", "command line", flag.Arg(0), "main", "") - } - loadlib() - - if Thearch.Thechar == '5' { - // mark some functions that are only referenced after linker code editing - if Debug['F'] != 0 { - mark(Linkrlookup(Ctxt, "_sfloat", 0)) - } - mark(Linklookup(Ctxt, "runtime.read_tls_fallback", 0)) - } - - checkgo() - deadcode() - callgraph() - - doelf() - if HEADTYPE == obj.Hdarwin { - domacho() - } - dostkcheck() - if HEADTYPE == obj.Hwindows { - dope() - } - addexport() - Thearch.Gentext() // trampolines, call stubs, etc. - textaddress() - pclntab() - findfunctab() - symtab() - dodata() - address() - doweak() - reloc() - Thearch.Asmb() - undef() - hostlink() - archive() - if Debug['v'] != 0 { - fmt.Fprintf(&Bso, "%5.2f cpu time\n", obj.Cputime()) - fmt.Fprintf(&Bso, "%d symbols\n", Ctxt.Nsymbol) - fmt.Fprintf(&Bso, "%d liveness data\n", liveness) - } - - Bso.Flush() - - errorexit() -} diff --git a/src/cmd/internal/ld/sym.go b/src/cmd/internal/ld/sym.go deleted file mode 100644 index 652109d3e3..0000000000 --- a/src/cmd/internal/ld/sym.go +++ /dev/null @@ -1,227 +0,0 @@ -// Derived from Inferno utils/6l/obj.c and utils/6l/span.c -// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c -// http://code.google.com/p/inferno-os/source/browse/utils/6l/span.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package ld - -import ( - "cmd/internal/obj" - "log" - "os" - "path/filepath" - "strconv" -) - -func yy_isalpha(c int) bool { - return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' -} - -var headers = []struct { - name string - val int -}{ - {"darwin", obj.Hdarwin}, - {"dragonfly", obj.Hdragonfly}, - {"elf", obj.Helf}, - {"freebsd", obj.Hfreebsd}, - {"linux", obj.Hlinux}, - {"android", obj.Hlinux}, // must be after "linux" entry or else headstr(Hlinux) == "android" - {"nacl", obj.Hnacl}, - {"netbsd", obj.Hnetbsd}, - {"openbsd", obj.Hopenbsd}, - {"plan9", obj.Hplan9}, - {"solaris", obj.Hsolaris}, - {"windows", obj.Hwindows}, - {"windowsgui", obj.Hwindows}, -} - -func linknew(arch *LinkArch) *Link { - ctxt := new(Link) - ctxt.Hash = make(map[symVer]*LSym) - ctxt.Arch = arch - ctxt.Version = obj.HistVersion - ctxt.Goroot = obj.Getgoroot() - - p := obj.Getgoarch() - if p != arch.Name { - log.Fatalf("invalid goarch %s (want %s)", p, arch.Name) - } - - var buf string - buf, _ = os.Getwd() - if buf == "" { - buf = "/???" - } - buf = filepath.ToSlash(buf) - - ctxt.Headtype = headtype(obj.Getgoos()) - if ctxt.Headtype < 0 { - log.Fatalf("unknown goos %s", obj.Getgoos()) - } - - // Record thread-local storage offset. - // TODO(rsc): Move tlsoffset back into the linker. - switch ctxt.Headtype { - default: - log.Fatalf("unknown thread-local storage offset for %s", Headstr(ctxt.Headtype)) - - case obj.Hplan9, obj.Hwindows: - break - - /* - * ELF uses TLS offset negative from FS. - * Translate 0(FS) and 8(FS) into -16(FS) and -8(FS). - * Known to low-level assembly in package runtime and runtime/cgo. - */ - case obj.Hlinux, - obj.Hfreebsd, - obj.Hnetbsd, - obj.Hopenbsd, - obj.Hdragonfly, - obj.Hsolaris: - ctxt.Tlsoffset = -1 * ctxt.Arch.Ptrsize - - case obj.Hnacl: - switch ctxt.Arch.Thechar { - default: - log.Fatalf("unknown thread-local storage offset for nacl/%s", ctxt.Arch.Name) - - case '5': - ctxt.Tlsoffset = 0 - - case '6': - ctxt.Tlsoffset = 0 - - case '8': - ctxt.Tlsoffset = -8 - } - - /* - * OS X system constants - offset from 0(GS) to our TLS. - * Explained in ../../runtime/cgo/gcc_darwin_*.c. - */ - case obj.Hdarwin: - switch ctxt.Arch.Thechar { - default: - log.Fatalf("unknown thread-local storage offset for darwin/%s", ctxt.Arch.Name) - - case '5': - ctxt.Tlsoffset = 0 // dummy value, not needed - - case '6': - ctxt.Tlsoffset = 0x8a0 - - case '7': - ctxt.Tlsoffset = 0 // dummy value, not needed - - case '8': - ctxt.Tlsoffset = 0x468 - } - } - - // On arm, record goarm. - if ctxt.Arch.Thechar == '5' { - p := obj.Getgoarm() - if p != "" { - ctxt.Goarm = int32(obj.Atoi(p)) - } else { - ctxt.Goarm = 6 - } - } - - return ctxt -} - -func linknewsym(ctxt *Link, symb string, v int) *LSym { - s := new(LSym) - *s = LSym{} - - s.Dynid = -1 - s.Plt = -1 - s.Got = -1 - s.Name = symb - s.Type = 0 - s.Version = int16(v) - s.Value = 0 - s.Size = 0 - ctxt.Nsymbol++ - - s.Allsym = ctxt.Allsym - ctxt.Allsym = s - - return s -} - -type symVer struct { - sym string - ver int -} - -func _lookup(ctxt *Link, symb string, v int, creat int) *LSym { - s := ctxt.Hash[symVer{symb, v}] - if s != nil { - return s - } - if creat == 0 { - return nil - } - - s = linknewsym(ctxt, symb, v) - s.Extname = s.Name - ctxt.Hash[symVer{symb, v}] = s - return s -} - -func Linklookup(ctxt *Link, name string, v int) *LSym { - return _lookup(ctxt, name, v, 1) -} - -// read-only lookup -func Linkrlookup(ctxt *Link, name string, v int) *LSym { - return _lookup(ctxt, name, v, 0) -} - -func Headstr(v int) string { - for i := 0; i < len(headers); i++ { - if v == headers[i].val { - return headers[i].name - } - } - return strconv.Itoa(v) -} - -func headtype(name string) int { - for i := 0; i < len(headers); i++ { - if name == headers[i].name { - return headers[i].val - } - } - return -1 -} diff --git a/src/cmd/internal/ld/symtab.go b/src/cmd/internal/ld/symtab.go deleted file mode 100644 index ca66541935..0000000000 --- a/src/cmd/internal/ld/symtab.go +++ /dev/null @@ -1,534 +0,0 @@ -// Inferno utils/6l/span.c -// http://code.google.com/p/inferno-os/source/browse/utils/6l/span.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package ld - -import ( - "cmd/internal/obj" - "crypto/sha1" - "fmt" - "path/filepath" - "sort" - "strings" -) - -// Symbol table. - -func putelfstr(s string) int { - if len(Elfstrdat) == 0 && s != "" { - // first entry must be empty string - putelfstr("") - } - - // When dynamically linking, we create LSym's by reading the names from - // the symbol tables of the shared libraries and so the names need to - // match exactly. Tools like DTrace will have to wait for now. - if !DynlinkingGo() { - // Rewrite · to . for ASCII-only tools like DTrace (sigh) - s = strings.Replace(s, "·", ".", -1) - } - - n := len(s) + 1 - for len(Elfstrdat)+n > cap(Elfstrdat) { - Elfstrdat = append(Elfstrdat[:cap(Elfstrdat)], 0)[:len(Elfstrdat)] - } - - off := len(Elfstrdat) - Elfstrdat = Elfstrdat[:off+n] - copy(Elfstrdat[off:], s) - - return off -} - -func putelfsyment(off int, addr int64, size int64, info int, shndx int, other int) { - switch Thearch.Thechar { - case '6', '7', '9': - Thearch.Lput(uint32(off)) - Cput(uint8(info)) - Cput(uint8(other)) - Thearch.Wput(uint16(shndx)) - Thearch.Vput(uint64(addr)) - Thearch.Vput(uint64(size)) - Symsize += ELF64SYMSIZE - - default: - Thearch.Lput(uint32(off)) - Thearch.Lput(uint32(addr)) - Thearch.Lput(uint32(size)) - Cput(uint8(info)) - Cput(uint8(other)) - Thearch.Wput(uint16(shndx)) - Symsize += ELF32SYMSIZE - } -} - -var numelfsym int = 1 // 0 is reserved - -var elfbind int - -func putelfsym(x *LSym, s string, t int, addr int64, size int64, ver int, go_ *LSym) { - var type_ int - - switch t { - default: - return - - case 'T': - type_ = STT_FUNC - - case 'D': - type_ = STT_OBJECT - - case 'B': - type_ = STT_OBJECT - - case 'U': - // ElfType is only set for symbols read from Go shared libraries, but - // for other symbols it is left as STT_NOTYPE which is fine. - type_ = int(x.ElfType) - - case 't': - type_ = STT_TLS - } - - xo := x - for xo.Outer != nil { - xo = xo.Outer - } - - var elfshnum int - if xo.Type == obj.SDYNIMPORT || xo.Type == obj.SHOSTOBJ { - elfshnum = SHN_UNDEF - } else { - if xo.Sect == nil { - Ctxt.Cursym = x - Diag("missing section in putelfsym") - return - } - if xo.Sect.(*Section).Elfsect == nil { - Ctxt.Cursym = x - Diag("missing ELF section in putelfsym") - return - } - elfshnum = xo.Sect.(*Section).Elfsect.(*ElfShdr).shnum - } - - // One pass for each binding: STB_LOCAL, STB_GLOBAL, - // maybe one day STB_WEAK. - bind := STB_GLOBAL - - if ver != 0 || (x.Type&obj.SHIDDEN != 0) || x.Local { - bind = STB_LOCAL - } - - // In external linking mode, we have to invoke gcc with -rdynamic - // to get the exported symbols put into the dynamic symbol table. - // To avoid filling the dynamic table with lots of unnecessary symbols, - // mark all Go symbols local (not global) in the final executable. - // But when we're dynamically linking, we need all those global symbols. - if !DynlinkingGo() && Linkmode == LinkExternal && x.Cgoexport&CgoExportStatic == 0 && elfshnum != SHN_UNDEF { - bind = STB_LOCAL - } - - if bind != elfbind { - return - } - - off := putelfstr(s) - if Linkmode == LinkExternal && elfshnum != SHN_UNDEF { - addr -= int64(xo.Sect.(*Section).Vaddr) - } - other := STV_DEFAULT - if x.Type&obj.SHIDDEN != 0 { - other = STV_HIDDEN - } - putelfsyment(off, addr, size, bind<<4|type_&0xf, elfshnum, other) - x.Elfsym = int32(numelfsym) - numelfsym++ -} - -func putelfsectionsym(s *LSym, shndx int) { - putelfsyment(0, 0, 0, STB_LOCAL<<4|STT_SECTION, shndx, 0) - s.Elfsym = int32(numelfsym) - numelfsym++ -} - -func putelfsymshndx(sympos int64, shndx int) { - here := Cpos() - if elf64 { - Cseek(sympos + 6) - } else { - Cseek(sympos + 14) - } - - Thearch.Wput(uint16(shndx)) - Cseek(here) -} - -func Asmelfsym() { - // the first symbol entry is reserved - putelfsyment(0, 0, 0, STB_LOCAL<<4|STT_NOTYPE, 0, 0) - - dwarfaddelfsectionsyms() - - elfbind = STB_LOCAL - genasmsym(putelfsym) - - elfbind = STB_GLOBAL - elfglobalsymndx = numelfsym - genasmsym(putelfsym) -} - -func putplan9sym(x *LSym, s string, t int, addr int64, size int64, ver int, go_ *LSym) { - switch t { - case 'T', 'L', 'D', 'B': - if ver != 0 { - t += 'a' - 'A' - } - fallthrough - - case 'a', - 'p', - 'f', - 'z', - 'Z', - 'm': - l := 4 - if HEADTYPE == obj.Hplan9 && Thearch.Thechar == '6' && Debug['8'] == 0 { - Lputb(uint32(addr >> 32)) - l = 8 - } - - Lputb(uint32(addr)) - Cput(uint8(t + 0x80)) /* 0x80 is variable length */ - - var i int - if t == 'z' || t == 'Z' { - Cput(uint8(s[0])) - for i = 1; s[i] != 0 || s[i+1] != 0; i += 2 { - Cput(uint8(s[i])) - Cput(uint8(s[i+1])) - } - - Cput(0) - Cput(0) - i++ - } else { - /* skip the '<' in filenames */ - if t == 'f' { - s = s[1:] - } - for i = 0; i < len(s); i++ { - Cput(uint8(s[i])) - } - Cput(0) - } - - Symsize += int32(l) + 1 + int32(i) + 1 - - default: - return - } -} - -func Asmplan9sym() { - genasmsym(putplan9sym) -} - -var symt *LSym - -func Wputl(w uint16) { - Cput(uint8(w)) - Cput(uint8(w >> 8)) -} - -func Wputb(w uint16) { - Cput(uint8(w >> 8)) - Cput(uint8(w)) -} - -func Lputb(l uint32) { - Cput(uint8(l >> 24)) - Cput(uint8(l >> 16)) - Cput(uint8(l >> 8)) - Cput(uint8(l)) -} - -func Lputl(l uint32) { - Cput(uint8(l)) - Cput(uint8(l >> 8)) - Cput(uint8(l >> 16)) - Cput(uint8(l >> 24)) -} - -func Vputb(v uint64) { - Lputb(uint32(v >> 32)) - Lputb(uint32(v)) -} - -func Vputl(v uint64) { - Lputl(uint32(v)) - 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() - - // Define these so that they'll get put into the symbol table. - // data.c:/^address will provide the actual values. - xdefine("runtime.text", obj.STEXT, 0) - - xdefine("runtime.etext", obj.STEXT, 0) - xdefine("runtime.typelink", obj.SRODATA, 0) - xdefine("runtime.etypelink", obj.SRODATA, 0) - xdefine("runtime.rodata", obj.SRODATA, 0) - xdefine("runtime.erodata", obj.SRODATA, 0) - xdefine("runtime.noptrdata", obj.SNOPTRDATA, 0) - xdefine("runtime.enoptrdata", obj.SNOPTRDATA, 0) - xdefine("runtime.data", obj.SDATA, 0) - xdefine("runtime.edata", obj.SDATA, 0) - xdefine("runtime.bss", obj.SBSS, 0) - xdefine("runtime.ebss", obj.SBSS, 0) - xdefine("runtime.noptrbss", obj.SNOPTRBSS, 0) - xdefine("runtime.enoptrbss", obj.SNOPTRBSS, 0) - xdefine("runtime.end", obj.SBSS, 0) - xdefine("runtime.epclntab", obj.SRODATA, 0) - xdefine("runtime.esymtab", obj.SRODATA, 0) - - // garbage collection symbols - s := Linklookup(Ctxt, "runtime.gcdata", 0) - - s.Type = obj.SRODATA - s.Size = 0 - s.Reachable = true - xdefine("runtime.egcdata", obj.SRODATA, 0) - - s = Linklookup(Ctxt, "runtime.gcbss", 0) - s.Type = obj.SRODATA - s.Size = 0 - s.Reachable = true - xdefine("runtime.egcbss", obj.SRODATA, 0) - - // pseudo-symbols to mark locations of type, string, and go string data. - var symtype *LSym - if !DynlinkingGo() { - s = Linklookup(Ctxt, "type.*", 0) - - s.Type = obj.STYPE - s.Size = 0 - s.Reachable = true - symtype = s - } - - s = Linklookup(Ctxt, "go.string.*", 0) - s.Type = obj.SGOSTRING - s.Local = true - s.Size = 0 - s.Reachable = true - symgostring := s - - s = Linklookup(Ctxt, "go.func.*", 0) - s.Type = obj.SGOFUNC - s.Local = true - s.Size = 0 - s.Reachable = true - symgofunc := s - - symtypelink := Linklookup(Ctxt, "runtime.typelink", 0) - - symt = Linklookup(Ctxt, "runtime.symtab", 0) - symt.Local = true - symt.Type = obj.SSYMTAB - symt.Size = 0 - symt.Reachable = true - - ntypelinks := 0 - - // assign specific types so that they sort together. - // within a type they sort by size, so the .* symbols - // just defined above will be first. - // hide the specific symbols. - for s := Ctxt.Allsym; s != nil; s = s.Allsym { - if !s.Reachable || s.Special != 0 || s.Type != obj.SRODATA { - continue - } - - if strings.HasPrefix(s.Name, "type.") && !DynlinkingGo() { - s.Type = obj.STYPE - s.Hide = 1 - s.Outer = symtype - } - - if strings.HasPrefix(s.Name, "go.typelink.") { - ntypelinks++ - s.Type = obj.STYPELINK - s.Hide = 1 - s.Outer = symtypelink - } - - if strings.HasPrefix(s.Name, "go.string.") { - s.Type = obj.SGOSTRING - s.Hide = 1 - s.Outer = symgostring - } - - if strings.HasPrefix(s.Name, "go.func.") { - s.Type = obj.SGOFUNC - s.Hide = 1 - s.Outer = symgofunc - } - - if strings.HasPrefix(s.Name, "gcargs.") || strings.HasPrefix(s.Name, "gclocals.") || strings.HasPrefix(s.Name, "gclocals·") { - s.Type = obj.SGOFUNC - s.Hide = 1 - s.Outer = symgofunc - s.Align = 4 - liveness += (s.Size + int64(s.Align) - 1) &^ (int64(s.Align) - 1) - } - } - - 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. - // This code uses several global variables that are set by pcln.go:pclntab. - moduledata := Linklookup(Ctxt, "runtime.firstmoduledata", 0) - moduledata.Type = obj.SNOPTRDATA - moduledata.Size = 0 // truncate symbol back to 0 bytes to reinitialize - moduledata.Reachable = true - moduledata.Local = true - // The pclntab slice - Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.pclntab", 0)) - adduint(Ctxt, moduledata, uint64(Linklookup(Ctxt, "runtime.pclntab", 0).Size)) - adduint(Ctxt, moduledata, uint64(Linklookup(Ctxt, "runtime.pclntab", 0).Size)) - // The ftab slice - Addaddrplus(Ctxt, moduledata, Linklookup(Ctxt, "runtime.pclntab", 0), int64(pclntabPclntabOffset)) - adduint(Ctxt, moduledata, uint64(pclntabNfunc+1)) - adduint(Ctxt, moduledata, uint64(pclntabNfunc+1)) - // The filetab slice - Addaddrplus(Ctxt, moduledata, Linklookup(Ctxt, "runtime.pclntab", 0), int64(pclntabFiletabOffset)) - adduint(Ctxt, moduledata, uint64(Ctxt.Nhistfile)) - adduint(Ctxt, moduledata, uint64(Ctxt.Nhistfile)) - // findfunctab - Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.findfunctab", 0)) - // minpc, maxpc - Addaddr(Ctxt, moduledata, pclntabFirstFunc) - Addaddrplus(Ctxt, moduledata, pclntabLastFunc, pclntabLastFunc.Size) - // pointers to specific parts of the module - Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.text", 0)) - Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.etext", 0)) - Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.noptrdata", 0)) - Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.enoptrdata", 0)) - Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.data", 0)) - Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.edata", 0)) - Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.bss", 0)) - Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.ebss", 0)) - Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.noptrbss", 0)) - Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.enoptrbss", 0)) - Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.end", 0)) - Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.gcdata", 0)) - Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.gcbss", 0)) - // The typelinks slice - 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 - // compiler-provided size, so read it from the type data. - moduledatatype := Linkrlookup(Ctxt, "type.runtime.moduledata", 0) - moduledata.Size = decodetype_size(moduledatatype) - Symgrow(Ctxt, moduledata, moduledata.Size) - - lastmoduledatap := Linklookup(Ctxt, "runtime.lastmoduledatap", 0) - if lastmoduledatap.Type != obj.SDYNIMPORT { - lastmoduledatap.Type = obj.SNOPTRDATA - lastmoduledatap.Size = 0 // overwrite existing value - Addaddr(Ctxt, lastmoduledatap, moduledata) - } -} diff --git a/src/cmd/internal/ld/textflag.go b/src/cmd/internal/ld/textflag.go deleted file mode 100644 index 335f20d21d..0000000000 --- a/src/cmd/internal/ld/textflag.go +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2013 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 - -// Writing and reading of Go object files. -// -// Originally, Go object files were Plan 9 object files, but no longer. -// Now they are more like standard object files, in that each symbol is defined -// by an associated memory image (bytes) and a list of relocations to apply -// during linking. We do not (yet?) use a standard file format, however. -// For now, the format is chosen to be as simple as possible to read and write. -// It may change for reasons of efficiency, or we may even switch to a -// standard file format if there are compelling benefits to doing so. -// See golang.org/s/go13linker for more background. -// -// The file format is: -// -// - magic header: "\x00\x00go13ld" -// - byte 1 - version number -// - sequence of strings giving dependencies (imported packages) -// - empty string (marks end of sequence) -// - sequence of defined symbols -// - byte 0xff (marks end of sequence) -// - magic footer: "\xff\xffgo13ld" -// -// All integers are stored in a zigzag varint format. -// See golang.org/s/go12symtab for a definition. -// -// Data blocks and strings are both stored as an integer -// followed by that many bytes. -// -// A symbol reference is a string name followed by a version. -// An empty name corresponds to a nil LSym* pointer. -// -// Each symbol is laid out as the following fields (taken from LSym*): -// -// - byte 0xfe (sanity check for synchronization) -// - type [int] -// - name [string] -// - version [int] -// - flags [int] -// 1 dupok -// - size [int] -// - gotype [symbol reference] -// - p [data block] -// - nr [int] -// - r [nr relocations, sorted by off] -// -// If type == STEXT, there are a few more fields: -// -// - args [int] -// - locals [int] -// - nosplit [int] -// - flags [int] -// 1 leaf -// 2 C function -// - nlocal [int] -// - local [nlocal automatics] -// - pcln [pcln table] -// -// Each relocation has the encoding: -// -// - off [int] -// - siz [int] -// - type [int] -// - add [int] -// - xadd [int] -// - sym [symbol reference] -// - xsym [symbol reference] -// -// Each local has the encoding: -// -// - asym [symbol reference] -// - offset [int] -// - type [int] -// - gotype [symbol reference] -// -// The pcln table has the encoding: -// -// - pcsp [data block] -// - pcfile [data block] -// - pcline [data block] -// - npcdata [int] -// - pcdata [npcdata data blocks] -// - nfuncdata [int] -// - funcdata [nfuncdata symbol references] -// - funcdatasym [nfuncdata ints] -// - nfile [int] -// - file [nfile symbol references] -// -// The file layout and meaning of type integers are architecture-independent. -// -// TODO(rsc): The file format is good for a first pass but needs work. -// - There are SymID in the object file that should really just be strings. -// - The actual symbol memory images are interlaced with the symbol -// metadata. They should be separated, to reduce the I/O required to -// load just the metadata. -// - The symbol references should be shortened, either with a symbol -// table or by using a simple backward index to an earlier mentioned symbol. - -// Copyright 2013 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. - -// This file defines flags attached to various functions -// and data objects. The compilers, assemblers, and linker must -// all agree on these values. - -// Don't profile the marked routine. This flag is deprecated. - -// It is ok for the linker to get multiple of these symbols. It will -// pick one of the duplicates to use. - -// Don't insert stack check preamble. - -// Put this data in a read-only section. - -// This data contains no pointers. - -// This is a wrapper function and should not count as disabling 'recover'. - -// This function uses its incoming context register. -const ( - NOPROF = 1 - DUPOK = 2 - NOSPLIT = 4 - RODATA = 8 - NOPTR = 16 - WRAPPER = 32 - NEEDCTXT = 64 -) diff --git a/src/cmd/internal/ld/util.go b/src/cmd/internal/ld/util.go deleted file mode 100644 index f38f05ce9d..0000000000 --- a/src/cmd/internal/ld/util.go +++ /dev/null @@ -1,172 +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" - "encoding/binary" - "log" - "os" - "runtime" - "runtime/pprof" - "strings" - "time" -) - -func cstring(x []byte) string { - i := bytes.IndexByte(x, '\x00') - if i >= 0 { - x = x[:i] - } - return string(x) -} - -func tokenize(s string) []string { - var f []string - for { - s = strings.TrimLeft(s, " \t\r\n") - if s == "" { - break - } - quote := false - i := 0 - for ; i < len(s); i++ { - if s[i] == '\'' { - if quote && i+1 < len(s) && s[i+1] == '\'' { - i++ - continue - } - quote = !quote - } - if !quote && (s[i] == ' ' || s[i] == '\t' || s[i] == '\r' || s[i] == '\n') { - break - } - } - next := s[:i] - s = s[i:] - if strings.Contains(next, "'") { - var buf []byte - quote := false - for i := 0; i < len(next); i++ { - if next[i] == '\'' { - if quote && i+1 < len(next) && next[i+1] == '\'' { - i++ - buf = append(buf, '\'') - } - quote = !quote - continue - } - buf = append(buf, next[i]) - } - next = string(buf) - } - f = append(f, next) - } - return f -} - -func cutStringAtNUL(s string) string { - if i := strings.Index(s, "\x00"); i >= 0 { - s = s[:i] - } - return s -} - -func Access(name string, mode int) int { - if mode != 0 { - panic("bad access") - } - _, err := os.Stat(name) - if err != nil { - return -1 - } - return 0 -} - -// strings.Compare, introduced in Go 1.5. -func stringsCompare(a, b string) int { - if a == b { - return 0 - } - if a < b { - return -1 - } - return +1 -} - -var atExitFuncs []func() - -func AtExit(f func()) { - atExitFuncs = append(atExitFuncs, f) -} - -func Exit(code int) { - for i := len(atExitFuncs) - 1; i >= 0; i-- { - f := atExitFuncs[i] - atExitFuncs = atExitFuncs[:i] - f() - } - os.Exit(code) -} - -var ( - cpuprofile string - memprofile string - memprofilerate int64 -) - -func startProfile() { - if cpuprofile != "" { - f, err := os.Create(cpuprofile) - if err != nil { - log.Fatalf("%v", err) - } - if err := pprof.StartCPUProfile(f); err != nil { - log.Fatalf("%v", err) - } - AtExit(pprof.StopCPUProfile) - } - if memprofile != "" { - if memprofilerate != 0 { - runtime.MemProfileRate = int(memprofilerate) - } - f, err := os.Create(memprofile) - if err != nil { - log.Fatalf("%v", err) - } - AtExit(func() { - runtime.GC() // profile all outstanding allocations - if err := pprof.WriteHeapProfile(f); err != nil { - log.Fatalf("%v", err) - } - }) - } -} - -func artrim(x []byte) string { - i := 0 - j := len(x) - for i < len(x) && x[i] == ' ' { - i++ - } - for j > i && x[j-1] == ' ' { - j-- - } - return string(x[i:j]) -} - -func stringtouint32(x []uint32, s string) { - for i := 0; len(s) > 0; i++ { - var buf [4]byte - s = s[copy(buf[:], s):] - x[i] = binary.LittleEndian.Uint32(buf[:]) - } -} - -var start = time.Now() - -func elapsed() float64 { - return time.Since(start).Seconds() -} diff --git a/src/cmd/link/internal/amd64/asm.go b/src/cmd/link/internal/amd64/asm.go new file mode 100644 index 0000000000..a4883f1a33 --- /dev/null +++ b/src/cmd/link/internal/amd64/asm.go @@ -0,0 +1,817 @@ +// Inferno utils/6l/asm.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package amd64 + +import ( + "cmd/internal/obj" + "cmd/link/internal/ld" + "debug/elf" + "fmt" + "log" +) + +func PADDR(x uint32) uint32 { + return x &^ 0x80000000 +} + +var zeroes string + +func Addcall(ctxt *ld.Link, s *ld.LSym, t *ld.LSym) int64 { + s.Reachable = true + i := s.Size + s.Size += 4 + ld.Symgrow(ctxt, s, s.Size) + r := ld.Addrel(s) + r.Sym = t + r.Off = int32(i) + r.Type = obj.R_CALL + r.Siz = 4 + return i + int64(r.Siz) +} + +func gentext() { + if !ld.DynlinkingGo() { + return + } + addmoduledata := ld.Linklookup(ld.Ctxt, "runtime.addmoduledata", 0) + if addmoduledata.Type == obj.STEXT { + // we're linking a module containing the runtime -> no need for + // an init function + return + } + addmoduledata.Reachable = true + initfunc := ld.Linklookup(ld.Ctxt, "go.link.addmoduledata", 0) + initfunc.Type = obj.STEXT + initfunc.Local = true + initfunc.Reachable = true + o := func(op ...uint8) { + for _, op1 := range op { + ld.Adduint8(ld.Ctxt, initfunc, op1) + } + } + // 0000000000000000 : + // 0: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 7 + // 3: R_X86_64_PC32 runtime.firstmoduledata-0x4 + o(0x48, 0x8d, 0x3d) + ld.Addpcrelplus(ld.Ctxt, initfunc, ld.Linklookup(ld.Ctxt, "runtime.firstmoduledata", 0), 0) + // 7: e8 00 00 00 00 callq c + // 8: R_X86_64_PLT32 runtime.addmoduledata-0x4 + o(0xe8) + Addcall(ld.Ctxt, initfunc, addmoduledata) + // c: c3 retq + o(0xc3) + if ld.Ctxt.Etextp != nil { + ld.Ctxt.Etextp.Next = initfunc + } else { + ld.Ctxt.Textp = initfunc + } + ld.Ctxt.Etextp = initfunc + initarray_entry := ld.Linklookup(ld.Ctxt, "go.link.addmoduledatainit", 0) + initarray_entry.Reachable = true + initarray_entry.Local = true + initarray_entry.Type = obj.SINITARR + ld.Addaddr(ld.Ctxt, initarray_entry, initfunc) +} + +func adddynrela(rela *ld.LSym, s *ld.LSym, r *ld.Reloc) { + ld.Addaddrplus(ld.Ctxt, rela, s, int64(r.Off)) + ld.Adduint64(ld.Ctxt, rela, ld.R_X86_64_RELATIVE) + ld.Addaddrplus(ld.Ctxt, rela, r.Sym, r.Add) // Addend +} + +func adddynrel(s *ld.LSym, r *ld.Reloc) { + targ := r.Sym + ld.Ctxt.Cursym = s + + switch r.Type { + default: + if r.Type >= 256 { + ld.Diag("unexpected relocation type %d", r.Type) + return + } + + // Handle relocations found in ELF object files. + case 256 + ld.R_X86_64_PC32: + if targ.Type == obj.SDYNIMPORT { + ld.Diag("unexpected R_X86_64_PC32 relocation for dynamic symbol %s", targ.Name) + } + if targ.Type == 0 || targ.Type == obj.SXREF { + ld.Diag("unknown symbol %s in pcrel", targ.Name) + } + r.Type = obj.R_PCREL + r.Add += 4 + return + + case 256 + ld.R_X86_64_PLT32: + r.Type = obj.R_PCREL + r.Add += 4 + if targ.Type == obj.SDYNIMPORT { + addpltsym(targ) + r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) + r.Add += int64(targ.Plt) + } + + return + + case 256 + ld.R_X86_64_GOTPCREL: + if targ.Type != obj.SDYNIMPORT { + // have symbol + if r.Off >= 2 && s.P[r.Off-2] == 0x8b { + // turn MOVQ of GOT entry into LEAQ of symbol itself + s.P[r.Off-2] = 0x8d + + r.Type = obj.R_PCREL + r.Add += 4 + return + } + } + + // fall back to using GOT and hope for the best (CMOV*) + // TODO: just needs relocation, no need to put in .dynsym + addgotsym(targ) + + r.Type = obj.R_PCREL + r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0) + r.Add += 4 + r.Add += int64(targ.Got) + return + + case 256 + ld.R_X86_64_64: + if targ.Type == obj.SDYNIMPORT { + ld.Diag("unexpected R_X86_64_64 relocation for dynamic symbol %s", targ.Name) + } + r.Type = obj.R_ADDR + return + + // Handle relocations found in Mach-O object files. + case 512 + ld.MACHO_X86_64_RELOC_UNSIGNED*2 + 0, + 512 + ld.MACHO_X86_64_RELOC_SIGNED*2 + 0, + 512 + ld.MACHO_X86_64_RELOC_BRANCH*2 + 0: + // TODO: What is the difference between all these? + r.Type = obj.R_ADDR + + if targ.Type == obj.SDYNIMPORT { + ld.Diag("unexpected reloc for dynamic symbol %s", targ.Name) + } + return + + case 512 + ld.MACHO_X86_64_RELOC_BRANCH*2 + 1: + if targ.Type == obj.SDYNIMPORT { + addpltsym(targ) + r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) + r.Add = int64(targ.Plt) + r.Type = obj.R_PCREL + return + } + fallthrough + + // fall through + case 512 + ld.MACHO_X86_64_RELOC_UNSIGNED*2 + 1, + 512 + ld.MACHO_X86_64_RELOC_SIGNED*2 + 1, + 512 + ld.MACHO_X86_64_RELOC_SIGNED_1*2 + 1, + 512 + ld.MACHO_X86_64_RELOC_SIGNED_2*2 + 1, + 512 + ld.MACHO_X86_64_RELOC_SIGNED_4*2 + 1: + r.Type = obj.R_PCREL + + if targ.Type == obj.SDYNIMPORT { + ld.Diag("unexpected pc-relative reloc for dynamic symbol %s", targ.Name) + } + return + + case 512 + ld.MACHO_X86_64_RELOC_GOT_LOAD*2 + 1: + if targ.Type != obj.SDYNIMPORT { + // have symbol + // turn MOVQ of GOT entry into LEAQ of symbol itself + if r.Off < 2 || s.P[r.Off-2] != 0x8b { + ld.Diag("unexpected GOT_LOAD reloc for non-dynamic symbol %s", targ.Name) + return + } + + s.P[r.Off-2] = 0x8d + r.Type = obj.R_PCREL + return + } + fallthrough + + // fall through + case 512 + ld.MACHO_X86_64_RELOC_GOT*2 + 1: + if targ.Type != obj.SDYNIMPORT { + ld.Diag("unexpected GOT reloc for non-dynamic symbol %s", targ.Name) + } + addgotsym(targ) + r.Type = obj.R_PCREL + r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0) + r.Add += int64(targ.Got) + return + } + + // Handle references to ELF symbols from our own object files. + if targ.Type != obj.SDYNIMPORT { + return + } + + switch r.Type { + case obj.R_CALL, + obj.R_PCREL: + if ld.HEADTYPE == obj.Hwindows { + // nothing to do, the relocation will be laid out in pereloc1 + return + } else { + // for both ELF and Mach-O + addpltsym(targ) + r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) + r.Add = int64(targ.Plt) + return + } + + case obj.R_ADDR: + if s.Type == obj.STEXT && ld.Iself { + if ld.HEADTYPE == obj.Hsolaris { + addpltsym(targ) + r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) + r.Add += int64(targ.Plt) + return + } + // The code is asking for the address of an external + // function. We provide it with the address of the + // correspondent GOT symbol. + addgotsym(targ) + + r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0) + r.Add += int64(targ.Got) + return + } + + if s.Type != obj.SDATA { + break + } + if ld.Iself { + 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 { + ld.Adduint64(ld.Ctxt, rela, ld.ELF64_R_INFO(uint32(targ.Dynid), ld.R_X86_64_64)) + } else { + ld.Adduint64(ld.Ctxt, rela, ld.ELF64_R_INFO(uint32(targ.Dynid), ld.R_X86_64_32)) + } + ld.Adduint64(ld.Ctxt, rela, uint64(r.Add)) + r.Type = 256 // ignore during relocsym + return + } + + if ld.HEADTYPE == obj.Hdarwin && s.Size == int64(ld.Thearch.Ptrsize) && r.Off == 0 { + // Mach-O relocations are a royal pain to lay out. + // They use a compact stateful bytecode representation + // that is too much bother to deal with. + // Instead, interpret the C declaration + // void *_Cvar_stderr = &stderr; + // as making _Cvar_stderr the name of a GOT entry + // for stderr. This is separate from the usual GOT entry, + // 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. + ld.Adddynsym(ld.Ctxt, targ) + + got := ld.Linklookup(ld.Ctxt, ".got", 0) + s.Type = got.Type | obj.SSUB + s.Outer = got + s.Sub = got.Sub + got.Sub = s + s.Value = got.Size + ld.Adduint64(ld.Ctxt, got, 0) + ld.Adduint32(ld.Ctxt, ld.Linklookup(ld.Ctxt, ".linkedit.got", 0), uint32(targ.Dynid)) + r.Type = 256 // ignore during relocsym + return + } + + if ld.HEADTYPE == obj.Hwindows { + // nothing to do, the relocation will be laid out in pereloc1 + return + } + } + + ld.Ctxt.Cursym = s + ld.Diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ.Name, r.Type, targ.Type) +} + +func elfreloc1(r *ld.Reloc, sectoff int64) int { + ld.Thearch.Vput(uint64(sectoff)) + + elfsym := r.Xsym.Elfsym + switch r.Type { + default: + return -1 + + case obj.R_ADDR: + if r.Siz == 4 { + ld.Thearch.Vput(ld.R_X86_64_32 | uint64(elfsym)<<32) + } else if r.Siz == 8 { + ld.Thearch.Vput(ld.R_X86_64_64 | uint64(elfsym)<<32) + } else { + return -1 + } + + case obj.R_TLS_LE: + if r.Siz == 4 { + ld.Thearch.Vput(ld.R_X86_64_TPOFF32 | uint64(elfsym)<<32) + } else { + return -1 + } + + case obj.R_TLS_IE: + if r.Siz == 4 { + ld.Thearch.Vput(ld.R_X86_64_GOTTPOFF | uint64(elfsym)<<32) + } else { + return -1 + } + + case obj.R_CALL: + if r.Siz == 4 { + if r.Xsym.Type == obj.SDYNIMPORT { + if ld.DynlinkingGo() { + ld.Thearch.Vput(ld.R_X86_64_PLT32 | uint64(elfsym)<<32) + } else { + ld.Thearch.Vput(ld.R_X86_64_GOTPCREL | uint64(elfsym)<<32) + } + } else { + ld.Thearch.Vput(ld.R_X86_64_PC32 | uint64(elfsym)<<32) + } + } else { + return -1 + } + + case obj.R_PCREL: + if r.Siz == 4 { + if r.Xsym.Type == obj.SDYNIMPORT && r.Xsym.ElfType == elf.STT_FUNC { + ld.Thearch.Vput(ld.R_X86_64_PLT32 | uint64(elfsym)<<32) + } else { + ld.Thearch.Vput(ld.R_X86_64_PC32 | uint64(elfsym)<<32) + } + } else { + return -1 + } + + case obj.R_GOTPCREL: + if r.Siz == 4 { + ld.Thearch.Vput(ld.R_X86_64_GOTPCREL | uint64(elfsym)<<32) + } else { + return -1 + } + } + + ld.Thearch.Vput(uint64(r.Xadd)) + return 0 +} + +func machoreloc1(r *ld.Reloc, sectoff int64) int { + var v uint32 + + rs := r.Xsym + + if rs.Type == obj.SHOSTOBJ || r.Type == obj.R_PCREL { + if rs.Dynid < 0 { + ld.Diag("reloc %d to non-macho symbol %s type=%d", r.Type, rs.Name, rs.Type) + return -1 + } + + v = uint32(rs.Dynid) + v |= 1 << 27 // external relocation + } else { + v = uint32((rs.Sect.(*ld.Section)).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) + return -1 + } + } + + switch r.Type { + default: + return -1 + + case obj.R_ADDR: + v |= ld.MACHO_X86_64_RELOC_UNSIGNED << 28 + + case obj.R_CALL: + v |= 1 << 24 // pc-relative bit + v |= ld.MACHO_X86_64_RELOC_BRANCH << 28 + + // NOTE: Only works with 'external' relocation. Forced above. + case obj.R_PCREL: + v |= 1 << 24 // pc-relative bit + v |= ld.MACHO_X86_64_RELOC_SIGNED << 28 + } + + switch r.Siz { + default: + return -1 + + case 1: + v |= 0 << 25 + + case 2: + v |= 1 << 25 + + case 4: + v |= 2 << 25 + + case 8: + v |= 3 << 25 + } + + ld.Thearch.Lput(uint32(sectoff)) + ld.Thearch.Lput(v) + return 0 +} + +func pereloc1(r *ld.Reloc, sectoff int64) bool { + var v uint32 + + rs := r.Xsym + + if rs.Dynid < 0 { + ld.Diag("reloc %d to non-coff symbol %s type=%d", r.Type, rs.Name, rs.Type) + return false + } + + ld.Thearch.Lput(uint32(sectoff)) + ld.Thearch.Lput(uint32(rs.Dynid)) + + switch r.Type { + default: + return false + + case obj.R_ADDR: + if r.Siz == 8 { + v = ld.IMAGE_REL_AMD64_ADDR64 + } else { + v = ld.IMAGE_REL_AMD64_ADDR32 + } + + case obj.R_CALL, + obj.R_PCREL: + v = ld.IMAGE_REL_AMD64_REL32 + } + + ld.Thearch.Wput(uint16(v)) + + return true +} + +func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int { + return -1 +} + +func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 { + log.Fatalf("unexpected relocation variant") + return t +} + +func elfsetupplt() { + plt := ld.Linklookup(ld.Ctxt, ".plt", 0) + got := ld.Linklookup(ld.Ctxt, ".got.plt", 0) + if plt.Size == 0 { + // pushq got+8(IP) + ld.Adduint8(ld.Ctxt, plt, 0xff) + + ld.Adduint8(ld.Ctxt, plt, 0x35) + ld.Addpcrelplus(ld.Ctxt, plt, got, 8) + + // jmpq got+16(IP) + ld.Adduint8(ld.Ctxt, plt, 0xff) + + ld.Adduint8(ld.Ctxt, plt, 0x25) + ld.Addpcrelplus(ld.Ctxt, plt, got, 16) + + // nopl 0(AX) + ld.Adduint32(ld.Ctxt, plt, 0x00401f0f) + + // assume got->size == 0 too + ld.Addaddrplus(ld.Ctxt, got, ld.Linklookup(ld.Ctxt, ".dynamic", 0), 0) + + ld.Adduint64(ld.Ctxt, got, 0) + ld.Adduint64(ld.Ctxt, got, 0) + } +} + +func addpltsym(s *ld.LSym) { + if s.Plt >= 0 { + return + } + + ld.Adddynsym(ld.Ctxt, s) + + if ld.Iself { + plt := ld.Linklookup(ld.Ctxt, ".plt", 0) + got := ld.Linklookup(ld.Ctxt, ".got.plt", 0) + rela := ld.Linklookup(ld.Ctxt, ".rela.plt", 0) + if plt.Size == 0 { + elfsetupplt() + } + + // jmpq *got+size(IP) + ld.Adduint8(ld.Ctxt, plt, 0xff) + + ld.Adduint8(ld.Ctxt, plt, 0x25) + ld.Addpcrelplus(ld.Ctxt, plt, got, got.Size) + + // add to got: pointer to current pos in plt + ld.Addaddrplus(ld.Ctxt, got, plt, plt.Size) + + // pushq $x + ld.Adduint8(ld.Ctxt, plt, 0x68) + + ld.Adduint32(ld.Ctxt, plt, uint32((got.Size-24-8)/8)) + + // jmpq .plt + ld.Adduint8(ld.Ctxt, plt, 0xe9) + + ld.Adduint32(ld.Ctxt, plt, uint32(-(plt.Size + 4))) + + // rela + ld.Addaddrplus(ld.Ctxt, rela, got, got.Size-8) + + ld.Adduint64(ld.Ctxt, rela, ld.ELF64_R_INFO(uint32(s.Dynid), ld.R_X86_64_JMP_SLOT)) + ld.Adduint64(ld.Ctxt, rela, 0) + + s.Plt = int32(plt.Size - 16) + } else if ld.HEADTYPE == obj.Hdarwin { + // To do lazy symbol lookup right, we're supposed + // to tell the dynamic loader which library each + // symbol comes from and format the link info + // section just so. I'm too lazy (ha!) to do that + // so for now we'll just use non-lazy pointers, + // which don't need to be told which library to use. + // + // http://networkpx.blogspot.com/2009/09/about-lcdyldinfoonly-command.html + // has details about what we're avoiding. + + addgotsym(s) + plt := ld.Linklookup(ld.Ctxt, ".plt", 0) + + ld.Adduint32(ld.Ctxt, ld.Linklookup(ld.Ctxt, ".linkedit.plt", 0), uint32(s.Dynid)) + + // jmpq *got+size(IP) + s.Plt = int32(plt.Size) + + ld.Adduint8(ld.Ctxt, plt, 0xff) + ld.Adduint8(ld.Ctxt, plt, 0x25) + ld.Addpcrelplus(ld.Ctxt, plt, ld.Linklookup(ld.Ctxt, ".got", 0), int64(s.Got)) + } else { + ld.Diag("addpltsym: unsupported binary format") + } +} + +func addgotsym(s *ld.LSym) { + if s.Got >= 0 { + return + } + + ld.Adddynsym(ld.Ctxt, s) + got := ld.Linklookup(ld.Ctxt, ".got", 0) + s.Got = int32(got.Size) + ld.Adduint64(ld.Ctxt, got, 0) + + if ld.Iself { + rela := ld.Linklookup(ld.Ctxt, ".rela", 0) + ld.Addaddrplus(ld.Ctxt, rela, got, int64(s.Got)) + ld.Adduint64(ld.Ctxt, rela, ld.ELF64_R_INFO(uint32(s.Dynid), ld.R_X86_64_GLOB_DAT)) + ld.Adduint64(ld.Ctxt, rela, 0) + } else if ld.HEADTYPE == obj.Hdarwin { + ld.Adduint32(ld.Ctxt, ld.Linklookup(ld.Ctxt, ".linkedit.got", 0), uint32(s.Dynid)) + } else { + ld.Diag("addgotsym: unsupported binary format") + } +} + +func asmb() { + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime()) + } + ld.Bso.Flush() + + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f codeblk\n", obj.Cputime()) + } + ld.Bso.Flush() + + if ld.Iself { + ld.Asmbelfsetup() + } + + sect := ld.Segtext.Sect + ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Codeblk(int64(sect.Vaddr), int64(sect.Length)) + for sect = sect.Next; sect != nil; sect = sect.Next { + ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Datblk(int64(sect.Vaddr), int64(sect.Length)) + } + + if ld.Segrodata.Filelen > 0 { + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) + } + ld.Bso.Flush() + + ld.Cseek(int64(ld.Segrodata.Fileoff)) + ld.Datblk(int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) + } + + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f datblk\n", obj.Cputime()) + } + ld.Bso.Flush() + + ld.Cseek(int64(ld.Segdata.Fileoff)) + ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + + machlink := int64(0) + if ld.HEADTYPE == obj.Hdarwin { + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) + } + + dwarfoff := ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Length), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND)) + ld.Cseek(dwarfoff) + + ld.Segdwarf.Fileoff = uint64(ld.Cpos()) + ld.Dwarfemitdebugsections() + ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff + + machlink = ld.Domacholink() + } + + switch ld.HEADTYPE { + default: + ld.Diag("unknown header type %d", ld.HEADTYPE) + fallthrough + + case obj.Hplan9, + obj.Helf: + break + + case obj.Hdarwin: + ld.Debug['8'] = 1 /* 64-bit addresses */ + + case obj.Hlinux, + obj.Hfreebsd, + obj.Hnetbsd, + obj.Hopenbsd, + obj.Hdragonfly, + obj.Hsolaris: + ld.Debug['8'] = 1 /* 64-bit addresses */ + + case obj.Hnacl, + obj.Hwindows: + break + } + + ld.Symsize = 0 + ld.Spsize = 0 + ld.Lcsize = 0 + symo := int64(0) + if ld.Debug['s'] == 0 { + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime()) + } + ld.Bso.Flush() + switch ld.HEADTYPE { + default: + case obj.Hplan9, + obj.Helf: + ld.Debug['s'] = 1 + 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)) + + case obj.Hlinux, + obj.Hfreebsd, + obj.Hnetbsd, + obj.Hopenbsd, + obj.Hdragonfly, + obj.Hsolaris, + obj.Hnacl: + symo = int64(ld.Segdata.Fileoff + ld.Segdata.Filelen) + symo = ld.Rnd(symo, int64(ld.INITRND)) + + case obj.Hwindows: + symo = int64(ld.Segdata.Fileoff + ld.Segdata.Filelen) + symo = ld.Rnd(symo, ld.PEFILEALIGN) + } + + ld.Cseek(symo) + switch ld.HEADTYPE { + default: + if ld.Iself { + ld.Cseek(symo) + ld.Asmelfsym() + ld.Cflush() + ld.Cwrite(ld.Elfstrdat) + + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) + } + + ld.Dwarfemitdebugsections() + + if ld.Linkmode == ld.LinkExternal { + ld.Elfemitreloc() + } + } + + case obj.Hplan9: + ld.Asmplan9sym() + ld.Cflush() + + sym := ld.Linklookup(ld.Ctxt, "pclntab", 0) + if sym != nil { + ld.Lcsize = int32(len(sym.P)) + for i := 0; int32(i) < ld.Lcsize; i++ { + ld.Cput(uint8(sym.P[i])) + } + + ld.Cflush() + } + + case obj.Hwindows: + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) + } + + ld.Dwarfemitdebugsections() + + case obj.Hdarwin: + if ld.Linkmode == ld.LinkExternal { + ld.Machoemitreloc() + } + } + } + + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f headr\n", obj.Cputime()) + } + ld.Bso.Flush() + ld.Cseek(0) + switch ld.HEADTYPE { + default: + case obj.Hplan9: /* plan9 */ + magic := int32(4*26*26 + 7) + + magic |= 0x00008000 /* fat header */ + ld.Lputb(uint32(magic)) /* magic */ + ld.Lputb(uint32(ld.Segtext.Filelen)) /* sizes */ + ld.Lputb(uint32(ld.Segdata.Filelen)) + ld.Lputb(uint32(ld.Segdata.Length - ld.Segdata.Filelen)) + ld.Lputb(uint32(ld.Symsize)) /* nsyms */ + vl := ld.Entryvalue() + ld.Lputb(PADDR(uint32(vl))) /* va of entry */ + ld.Lputb(uint32(ld.Spsize)) /* sp offsets */ + ld.Lputb(uint32(ld.Lcsize)) /* line offsets */ + ld.Vputb(uint64(vl)) /* va of entry */ + + case obj.Hdarwin: + ld.Asmbmacho() + + case obj.Hlinux, + obj.Hfreebsd, + obj.Hnetbsd, + obj.Hopenbsd, + obj.Hdragonfly, + obj.Hsolaris, + obj.Hnacl: + ld.Asmbelf(symo) + + case obj.Hwindows: + ld.Asmbpe() + } + + ld.Cflush() +} diff --git a/src/cmd/link/internal/amd64/l.go b/src/cmd/link/internal/amd64/l.go new file mode 100644 index 0000000000..2537419eff --- /dev/null +++ b/src/cmd/link/internal/amd64/l.go @@ -0,0 +1,47 @@ +// Inferno utils/6l/l.h +// http://code.google.com/p/inferno-os/source/browse/utils/6l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package amd64 + +const ( + thechar = '6' + MaxAlign = 32 // max data alignment + FuncAlign = 16 +) + +const ( + MINLC = 1 +) + +/* Used by ../internal/ld/dwarf.go */ +const ( + DWARFREGSP = 7 + DWARFREGLR = 16 +) diff --git a/src/cmd/link/internal/amd64/obj.go b/src/cmd/link/internal/amd64/obj.go new file mode 100644 index 0000000000..e489bb75a9 --- /dev/null +++ b/src/cmd/link/internal/amd64/obj.go @@ -0,0 +1,222 @@ +// Inferno utils/6l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package amd64 + +import ( + "cmd/internal/obj" + "cmd/link/internal/ld" + "fmt" + "log" +) + +// Reading object files. + +func Main() { + linkarchinit() + ld.Ldmain() +} + +func linkarchinit() { + ld.Thestring = "amd64" + ld.Thelinkarch = &ld.Linkamd64 + if obj.Getgoarch() == "amd64p32" { + ld.Thelinkarch = &ld.Linkamd64p32 + } + + ld.Thearch.Thechar = thechar + ld.Thearch.Ptrsize = ld.Thelinkarch.Ptrsize + ld.Thearch.Intsize = ld.Thelinkarch.Ptrsize + ld.Thearch.Regsize = ld.Thelinkarch.Regsize + ld.Thearch.Funcalign = FuncAlign + ld.Thearch.Maxalign = MaxAlign + ld.Thearch.Minlc = MINLC + ld.Thearch.Dwarfregsp = DWARFREGSP + ld.Thearch.Dwarfreglr = DWARFREGLR + + ld.Thearch.Adddynrel = adddynrel + ld.Thearch.Archinit = archinit + ld.Thearch.Archreloc = archreloc + ld.Thearch.Archrelocvariant = archrelocvariant + ld.Thearch.Asmb = asmb + ld.Thearch.Elfreloc1 = elfreloc1 + ld.Thearch.Elfsetupplt = elfsetupplt + ld.Thearch.Gentext = gentext + ld.Thearch.Machoreloc1 = machoreloc1 + ld.Thearch.PEreloc1 = pereloc1 + ld.Thearch.Lput = ld.Lputl + ld.Thearch.Wput = ld.Wputl + ld.Thearch.Vput = ld.Vputl + + ld.Thearch.Linuxdynld = "/lib64/ld-linux-x86-64.so.2" + ld.Thearch.Freebsddynld = "/libexec/ld-elf.so.1" + ld.Thearch.Openbsddynld = "/usr/libexec/ld.so" + ld.Thearch.Netbsddynld = "/libexec/ld.elf_so" + ld.Thearch.Dragonflydynld = "/usr/libexec/ld-elf.so.2" + ld.Thearch.Solarisdynld = "/lib/amd64/ld.so.1" +} + +func archinit() { + // getgoextlinkenabled is based on GO_EXTLINK_ENABLED when + // Go was built; see ../../make.bash. + if ld.Linkmode == ld.LinkAuto && obj.Getgoextlinkenabled() == "0" { + ld.Linkmode = ld.LinkInternal + } + + if ld.Buildmode == ld.BuildmodeCShared || ld.DynlinkingGo() { + ld.Linkmode = ld.LinkExternal + } + + switch ld.HEADTYPE { + default: + if ld.Linkmode == ld.LinkAuto { + ld.Linkmode = ld.LinkInternal + } + if ld.Linkmode == ld.LinkExternal && obj.Getgoextlinkenabled() != "1" { + log.Fatalf("cannot use -linkmode=external with -H %s", ld.Headstr(int(ld.HEADTYPE))) + } + + case obj.Hdarwin, + obj.Hdragonfly, + obj.Hfreebsd, + obj.Hlinux, + obj.Hnacl, + obj.Hnetbsd, + obj.Hopenbsd, + obj.Hsolaris, + obj.Hwindows: + break + } + + switch ld.HEADTYPE { + default: + ld.Exitf("unknown -H option: %v", ld.HEADTYPE) + + case obj.Hplan9: /* plan 9 */ + ld.HEADR = 32 + 8 + + if ld.INITTEXT == -1 { + ld.INITTEXT = 0x200000 + int64(ld.HEADR) + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 0x200000 + } + + case obj.Helf: /* elf32 executable */ + ld.HEADR = int32(ld.Rnd(52+3*32, 16)) + + if ld.INITTEXT == -1 { + ld.INITTEXT = 0x80110000 + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 4096 + } + + case obj.Hdarwin: /* apple MACH */ + ld.Machoinit() + + ld.HEADR = ld.INITIAL_MACHO_HEADR + if ld.INITRND == -1 { + ld.INITRND = 4096 + } + if ld.INITTEXT == -1 { + ld.INITTEXT = 4096 + int64(ld.HEADR) + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + + case obj.Hlinux, /* elf64 executable */ + obj.Hfreebsd, /* freebsd */ + obj.Hnetbsd, /* netbsd */ + obj.Hopenbsd, /* openbsd */ + obj.Hdragonfly, /* dragonfly */ + obj.Hsolaris: /* solaris */ + 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) + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 4096 + } + + case obj.Hnacl: + ld.Elfinit() + ld.Debug['w']++ // disable dwarf, which gets confused and is useless anyway + ld.HEADR = 0x10000 + ld.Funcalign = 32 + if ld.INITTEXT == -1 { + ld.INITTEXT = 0x20000 + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 0x10000 + } + + case obj.Hwindows: /* PE executable */ + ld.Peinit() + + ld.HEADR = ld.PEFILEHEADR + if ld.INITTEXT == -1 { + ld.INITTEXT = ld.PEBASE + int64(ld.PESECTHEADR) + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = ld.PESECTALIGN + } + } + + if ld.INITDAT != 0 && ld.INITRND != 0 { + fmt.Printf("warning: -D0x%x is ignored because of -R0x%x\n", uint64(ld.INITDAT), uint32(ld.INITRND)) + } +} 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/link/internal/arm/asm.go b/src/cmd/link/internal/arm/asm.go new file mode 100644 index 0000000000..e310d29e9a --- /dev/null +++ b/src/cmd/link/internal/arm/asm.go @@ -0,0 +1,651 @@ +// Inferno utils/5l/asm.c +// http://code.google.com/p/inferno-os/source/browse/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package arm + +import ( + "cmd/internal/obj" + "cmd/link/internal/ld" + "fmt" + "log" +) + +func gentext() { +} + +// Preserve highest 8 bits of a, and do addition to lower 24-bit +// of a and b; used to adjust ARM branch intruction's target +func braddoff(a int32, b int32) int32 { + return int32((uint32(a))&0xff000000 | 0x00ffffff&uint32(a+b)) +} + +func adddynrela(rel *ld.LSym, s *ld.LSym, r *ld.Reloc) { + ld.Addaddrplus(ld.Ctxt, rel, s, int64(r.Off)) + ld.Adduint32(ld.Ctxt, rel, ld.R_ARM_RELATIVE) +} + +func adddynrel(s *ld.LSym, r *ld.Reloc) { + targ := r.Sym + ld.Ctxt.Cursym = s + + switch r.Type { + default: + if r.Type >= 256 { + ld.Diag("unexpected relocation type %d", r.Type) + return + } + + // Handle relocations found in ELF object files. + case 256 + ld.R_ARM_PLT32: + r.Type = obj.R_CALLARM + + if targ.Type == obj.SDYNIMPORT { + addpltsym(ld.Ctxt, targ) + r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) + r.Add = int64(braddoff(int32(r.Add), targ.Plt/4)) + } + + return + + case 256 + ld.R_ARM_THM_PC22: // R_ARM_THM_CALL + ld.Exitf("R_ARM_THM_CALL, are you using -marm?") + return + + case 256 + ld.R_ARM_GOT32: // R_ARM_GOT_BREL + if targ.Type != obj.SDYNIMPORT { + addgotsyminternal(ld.Ctxt, targ) + } else { + addgotsym(ld.Ctxt, targ) + } + + r.Type = obj.R_CONST // write r->add during relocsym + r.Sym = nil + r.Add += int64(targ.Got) + return + + case 256 + ld.R_ARM_GOT_PREL: // GOT(nil) + A - nil + if targ.Type != obj.SDYNIMPORT { + addgotsyminternal(ld.Ctxt, targ) + } else { + addgotsym(ld.Ctxt, targ) + } + + r.Type = obj.R_PCREL + r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0) + r.Add += int64(targ.Got) + 4 + return + + case 256 + ld.R_ARM_GOTOFF: // R_ARM_GOTOFF32 + r.Type = obj.R_GOTOFF + + return + + case 256 + ld.R_ARM_GOTPC: // R_ARM_BASE_PREL + r.Type = obj.R_PCREL + + r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0) + r.Add += 4 + return + + case 256 + ld.R_ARM_CALL: + r.Type = obj.R_CALLARM + if targ.Type == obj.SDYNIMPORT { + addpltsym(ld.Ctxt, targ) + r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) + r.Add = int64(braddoff(int32(r.Add), targ.Plt/4)) + } + + return + + case 256 + ld.R_ARM_REL32: // R_ARM_REL32 + r.Type = obj.R_PCREL + + r.Add += 4 + return + + case 256 + ld.R_ARM_ABS32: + if targ.Type == obj.SDYNIMPORT { + ld.Diag("unexpected R_ARM_ABS32 relocation for dynamic symbol %s", targ.Name) + } + r.Type = obj.R_ADDR + return + + // we can just ignore this, because we are targeting ARM V5+ anyway + case 256 + ld.R_ARM_V4BX: + if r.Sym != nil { + // R_ARM_V4BX is ABS relocation, so this symbol is a dummy symbol, ignore it + r.Sym.Type = 0 + } + + r.Sym = nil + return + + case 256 + ld.R_ARM_PC24, + 256 + ld.R_ARM_JUMP24: + r.Type = obj.R_CALLARM + if targ.Type == obj.SDYNIMPORT { + addpltsym(ld.Ctxt, targ) + r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) + r.Add = int64(braddoff(int32(r.Add), targ.Plt/4)) + } + + return + } + + // Handle references to ELF symbols from our own object files. + if targ.Type != obj.SDYNIMPORT { + return + } + + switch r.Type { + case obj.R_CALLARM: + addpltsym(ld.Ctxt, targ) + r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) + r.Add = int64(targ.Plt) + return + + case obj.R_ADDR: + if s.Type != obj.SDATA { + break + } + if ld.Iself { + 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 + r.Type = obj.R_CONST // write r->add during relocsym + r.Sym = nil + return + } + } + + ld.Ctxt.Cursym = s + ld.Diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ.Name, r.Type, targ.Type) +} + +func elfreloc1(r *ld.Reloc, sectoff int64) int { + ld.Thearch.Lput(uint32(sectoff)) + + elfsym := r.Xsym.Elfsym + switch r.Type { + default: + return -1 + + case obj.R_ADDR: + if r.Siz == 4 { + ld.Thearch.Lput(ld.R_ARM_ABS32 | uint32(elfsym)<<8) + } else { + return -1 + } + + case obj.R_PCREL: + if r.Siz == 4 { + ld.Thearch.Lput(ld.R_ARM_REL32 | uint32(elfsym)<<8) + } else { + return -1 + } + + case obj.R_CALLARM: + if r.Siz == 4 { + if r.Add&0xff000000 == 0xeb000000 { // BL + ld.Thearch.Lput(ld.R_ARM_CALL | uint32(elfsym)<<8) + } else { + ld.Thearch.Lput(ld.R_ARM_JUMP24 | uint32(elfsym)<<8) + } + } else { + return -1 + } + + case obj.R_TLS: + if r.Siz == 4 { + if ld.Buildmode == ld.BuildmodeCShared { + ld.Thearch.Lput(ld.R_ARM_TLS_IE32 | uint32(elfsym)<<8) + } else { + ld.Thearch.Lput(ld.R_ARM_TLS_LE32 | uint32(elfsym)<<8) + } + } else { + return -1 + } + } + + return 0 +} + +func elfsetupplt() { + plt := ld.Linklookup(ld.Ctxt, ".plt", 0) + got := ld.Linklookup(ld.Ctxt, ".got.plt", 0) + if plt.Size == 0 { + // str lr, [sp, #-4]! + ld.Adduint32(ld.Ctxt, plt, 0xe52de004) + + // ldr lr, [pc, #4] + ld.Adduint32(ld.Ctxt, plt, 0xe59fe004) + + // add lr, pc, lr + ld.Adduint32(ld.Ctxt, plt, 0xe08fe00e) + + // ldr pc, [lr, #8]! + ld.Adduint32(ld.Ctxt, plt, 0xe5bef008) + + // .word &GLOBAL_OFFSET_TABLE[0] - . + ld.Addpcrelplus(ld.Ctxt, plt, got, 4) + + // the first .plt entry requires 3 .plt.got entries + ld.Adduint32(ld.Ctxt, got, 0) + + ld.Adduint32(ld.Ctxt, got, 0) + ld.Adduint32(ld.Ctxt, got, 0) + } +} + +func machoreloc1(r *ld.Reloc, sectoff int64) int { + var v uint32 + + rs := r.Xsym + + if rs.Type == obj.SHOSTOBJ || r.Type == obj.R_CALLARM { + if rs.Dynid < 0 { + ld.Diag("reloc %d to non-macho symbol %s type=%d", r.Type, rs.Name, rs.Type) + return -1 + } + + v = uint32(rs.Dynid) + v |= 1 << 27 // external relocation + } else { + v = uint32((rs.Sect.(*ld.Section)).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) + return -1 + } + } + + switch r.Type { + default: + return -1 + + case obj.R_ADDR: + v |= ld.MACHO_GENERIC_RELOC_VANILLA << 28 + + case obj.R_CALLARM: + v |= 1 << 24 // pc-relative bit + v |= ld.MACHO_ARM_RELOC_BR24 << 28 + } + + switch r.Siz { + default: + return -1 + + case 1: + v |= 0 << 25 + + case 2: + v |= 1 << 25 + + case 4: + v |= 2 << 25 + + case 8: + v |= 3 << 25 + } + + ld.Thearch.Lput(uint32(sectoff)) + ld.Thearch.Lput(v) + return 0 +} + +func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int { + if ld.Linkmode == ld.LinkExternal { + switch r.Type { + case obj.R_CALLARM: + r.Done = 0 + + // set up addend for eventual relocation via outer symbol. + rs := r.Sym + + r.Xadd = r.Add + if r.Xadd&0x800000 != 0 { + r.Xadd |= ^0xffffff + } + r.Xadd *= 4 + for rs.Outer != nil { + r.Xadd += ld.Symaddr(rs) - ld.Symaddr(rs.Outer) + rs = rs.Outer + } + + if rs.Type != obj.SHOSTOBJ && rs.Sect == nil { + ld.Diag("missing section for %s", rs.Name) + } + r.Xsym = rs + + // ld64 for arm seems to want the symbol table to contain offset + // into the section rather than pseudo virtual address that contains + // the section load address. + // we need to compensate that by removing the instruction's address + // from addend. + if ld.HEADTYPE == obj.Hdarwin { + r.Xadd -= ld.Symaddr(s) + int64(r.Off) + } + + *val = int64(braddoff(int32(0xff000000&uint32(r.Add)), int32(0xffffff&uint32(r.Xadd/4)))) + return 0 + } + + return -1 + } + + switch r.Type { + case obj.R_CONST: + *val = r.Add + return 0 + + case obj.R_GOTOFF: + *val = ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ld.Linklookup(ld.Ctxt, ".got", 0)) + return 0 + + // The following three arch specific relocations are only for generation of + // Linux/ARM ELF's PLT entry (3 assembler instruction) + case obj.R_PLT0: // add ip, pc, #0xXX00000 + if ld.Symaddr(ld.Linklookup(ld.Ctxt, ".got.plt", 0)) < ld.Symaddr(ld.Linklookup(ld.Ctxt, ".plt", 0)) { + ld.Diag(".got.plt should be placed after .plt section.") + } + *val = 0xe28fc600 + (0xff & (int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(ld.Linklookup(ld.Ctxt, ".plt", 0))+int64(r.Off))+r.Add)) >> 20)) + return 0 + + case obj.R_PLT1: // add ip, ip, #0xYY000 + *val = 0xe28cca00 + (0xff & (int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(ld.Linklookup(ld.Ctxt, ".plt", 0))+int64(r.Off))+r.Add+4)) >> 12)) + + return 0 + + case obj.R_PLT2: // ldr pc, [ip, #0xZZZ]! + *val = 0xe5bcf000 + (0xfff & int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(ld.Linklookup(ld.Ctxt, ".plt", 0))+int64(r.Off))+r.Add+8))) + + return 0 + + case obj.R_CALLARM: // bl XXXXXX or b YYYYYY + *val = int64(braddoff(int32(0xff000000&uint32(r.Add)), int32(0xffffff&uint32((ld.Symaddr(r.Sym)+int64((uint32(r.Add))*4)-(s.Value+int64(r.Off)))/4)))) + + return 0 + } + + return -1 +} + +func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 { + log.Fatalf("unexpected relocation variant") + return t +} + +func addpltreloc(ctxt *ld.Link, plt *ld.LSym, got *ld.LSym, sym *ld.LSym, typ int) *ld.Reloc { + r := ld.Addrel(plt) + r.Sym = got + r.Off = int32(plt.Size) + r.Siz = 4 + r.Type = int32(typ) + r.Add = int64(sym.Got) - 8 + + plt.Reachable = true + plt.Size += 4 + ld.Symgrow(ctxt, plt, plt.Size) + + return r +} + +func addpltsym(ctxt *ld.Link, s *ld.LSym) { + if s.Plt >= 0 { + return + } + + ld.Adddynsym(ctxt, s) + + if ld.Iself { + plt := ld.Linklookup(ctxt, ".plt", 0) + got := ld.Linklookup(ctxt, ".got.plt", 0) + rel := ld.Linklookup(ctxt, ".rel.plt", 0) + if plt.Size == 0 { + elfsetupplt() + } + + // .got entry + s.Got = int32(got.Size) + + // In theory, all GOT should point to the first PLT entry, + // Linux/ARM's dynamic linker will do that for us, but FreeBSD/ARM's + // dynamic linker won't, so we'd better do it ourselves. + ld.Addaddrplus(ctxt, got, plt, 0) + + // .plt entry, this depends on the .got entry + s.Plt = int32(plt.Size) + + addpltreloc(ctxt, plt, got, s, obj.R_PLT0) // add lr, pc, #0xXX00000 + addpltreloc(ctxt, plt, got, s, obj.R_PLT1) // add lr, lr, #0xYY000 + addpltreloc(ctxt, plt, got, s, obj.R_PLT2) // ldr pc, [lr, #0xZZZ]! + + // rel + ld.Addaddrplus(ctxt, rel, got, int64(s.Got)) + + ld.Adduint32(ctxt, rel, ld.ELF32_R_INFO(uint32(s.Dynid), ld.R_ARM_JUMP_SLOT)) + } else { + ld.Diag("addpltsym: unsupported binary format") + } +} + +func addgotsyminternal(ctxt *ld.Link, s *ld.LSym) { + if s.Got >= 0 { + return + } + + got := ld.Linklookup(ctxt, ".got", 0) + s.Got = int32(got.Size) + + ld.Addaddrplus(ctxt, got, s, 0) + + if ld.Iself { + } else { + ld.Diag("addgotsyminternal: unsupported binary format") + } +} + +func addgotsym(ctxt *ld.Link, s *ld.LSym) { + if s.Got >= 0 { + return + } + + ld.Adddynsym(ctxt, s) + got := ld.Linklookup(ctxt, ".got", 0) + s.Got = int32(got.Size) + ld.Adduint32(ctxt, got, 0) + + if ld.Iself { + rel := ld.Linklookup(ctxt, ".rel", 0) + ld.Addaddrplus(ctxt, rel, got, int64(s.Got)) + ld.Adduint32(ctxt, rel, ld.ELF32_R_INFO(uint32(s.Dynid), ld.R_ARM_GLOB_DAT)) + } else { + ld.Diag("addgotsym: unsupported binary format") + } +} + +func asmb() { + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime()) + } + ld.Bso.Flush() + + if ld.Iself { + ld.Asmbelfsetup() + } + + sect := ld.Segtext.Sect + ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Codeblk(int64(sect.Vaddr), int64(sect.Length)) + for sect = sect.Next; sect != nil; sect = sect.Next { + ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Datblk(int64(sect.Vaddr), int64(sect.Length)) + } + + if ld.Segrodata.Filelen > 0 { + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) + } + ld.Bso.Flush() + + ld.Cseek(int64(ld.Segrodata.Fileoff)) + ld.Datblk(int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) + } + + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f datblk\n", obj.Cputime()) + } + ld.Bso.Flush() + + ld.Cseek(int64(ld.Segdata.Fileoff)) + ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + + machlink := uint32(0) + if ld.HEADTYPE == obj.Hdarwin { + if ld.Debug['v'] != 0 { + 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)) + + ld.Segdwarf.Fileoff = uint64(ld.Cpos()) + ld.Dwarfemitdebugsections() + ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff + + machlink = uint32(ld.Domacholink()) + } + + /* output symbol table */ + ld.Symsize = 0 + + ld.Lcsize = 0 + symo := uint32(0) + if ld.Debug['s'] == 0 { + // TODO: rationalize + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime()) + } + ld.Bso.Flush() + switch ld.HEADTYPE { + default: + if ld.Iself { + symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) + symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND))) + } + + case obj.Hplan9: + 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)) + } + + ld.Cseek(int64(symo)) + switch ld.HEADTYPE { + default: + if ld.Iself { + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f elfsym\n", obj.Cputime()) + } + ld.Asmelfsym() + ld.Cflush() + ld.Cwrite(ld.Elfstrdat) + + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) + } + ld.Dwarfemitdebugsections() + + if ld.Linkmode == ld.LinkExternal { + ld.Elfemitreloc() + } + } + + case obj.Hplan9: + ld.Asmplan9sym() + ld.Cflush() + + sym := ld.Linklookup(ld.Ctxt, "pclntab", 0) + if sym != nil { + ld.Lcsize = int32(len(sym.P)) + for i := 0; int32(i) < ld.Lcsize; i++ { + ld.Cput(uint8(sym.P[i])) + } + + ld.Cflush() + } + + case obj.Hdarwin: + if ld.Linkmode == ld.LinkExternal { + ld.Machoemitreloc() + } + } + } + + ld.Ctxt.Cursym = nil + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f header\n", obj.Cputime()) + } + ld.Bso.Flush() + ld.Cseek(0) + switch ld.HEADTYPE { + default: + case obj.Hplan9: /* plan 9 */ + ld.Thearch.Lput(0x647) /* magic */ + ld.Thearch.Lput(uint32(ld.Segtext.Filelen)) /* sizes */ + ld.Thearch.Lput(uint32(ld.Segdata.Filelen)) + ld.Thearch.Lput(uint32(ld.Segdata.Length - ld.Segdata.Filelen)) + ld.Thearch.Lput(uint32(ld.Symsize)) /* nsyms */ + ld.Thearch.Lput(uint32(ld.Entryvalue())) /* va of entry */ + ld.Thearch.Lput(0) + ld.Thearch.Lput(uint32(ld.Lcsize)) + + case obj.Hlinux, + obj.Hfreebsd, + obj.Hnetbsd, + obj.Hopenbsd, + obj.Hnacl: + ld.Asmbelf(int64(symo)) + + case obj.Hdarwin: + ld.Asmbmacho() + } + + ld.Cflush() + if ld.Debug['c'] != 0 { + fmt.Printf("textsize=%d\n", ld.Segtext.Filelen) + fmt.Printf("datsize=%d\n", ld.Segdata.Filelen) + fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen) + fmt.Printf("symsize=%d\n", ld.Symsize) + fmt.Printf("lcsize=%d\n", ld.Lcsize) + fmt.Printf("total=%d\n", ld.Segtext.Filelen+ld.Segdata.Length+uint64(ld.Symsize)+uint64(ld.Lcsize)) + } +} diff --git a/src/cmd/link/internal/arm/l.go b/src/cmd/link/internal/arm/l.go new file mode 100644 index 0000000000..4973772163 --- /dev/null +++ b/src/cmd/link/internal/arm/l.go @@ -0,0 +1,79 @@ +// Inferno utils/5l/asm.c +// http://code.google.com/p/inferno-os/source/browse/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package arm + +// Writing object files. + +// Inferno utils/5l/l.h +// http://code.google.com/p/inferno-os/source/browse/utils/5l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +const ( + thechar = '5' + PtrSize = 4 + IntSize = 4 + RegSize = 4 + MaxAlign = 8 // max data alignment + FuncAlign = 4 // single-instruction alignment + MINLC = 4 +) + +/* Used by ../internal/ld/dwarf.go */ +const ( + DWARFREGSP = 13 + DWARFREGLR = 14 +) diff --git a/src/cmd/link/internal/arm/obj.go b/src/cmd/link/internal/arm/obj.go new file mode 100644 index 0000000000..14fe7a64eb --- /dev/null +++ b/src/cmd/link/internal/arm/obj.go @@ -0,0 +1,178 @@ +// Inferno utils/5l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/5l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package arm + +import ( + "cmd/internal/obj" + "cmd/link/internal/ld" + "fmt" + "log" +) + +// Reading object files. + +func Main() { + linkarchinit() + ld.Ldmain() +} + +func linkarchinit() { + ld.Thestring = "arm" + ld.Thelinkarch = &ld.Linkarm + + ld.Thearch.Thechar = thechar + ld.Thearch.Ptrsize = ld.Thelinkarch.Ptrsize + ld.Thearch.Intsize = ld.Thelinkarch.Ptrsize + ld.Thearch.Regsize = ld.Thelinkarch.Regsize + ld.Thearch.Funcalign = FuncAlign + ld.Thearch.Maxalign = MaxAlign + ld.Thearch.Minlc = MINLC + ld.Thearch.Dwarfregsp = DWARFREGSP + ld.Thearch.Dwarfreglr = DWARFREGLR + + ld.Thearch.Adddynrel = adddynrel + ld.Thearch.Archinit = archinit + ld.Thearch.Archreloc = archreloc + ld.Thearch.Archrelocvariant = archrelocvariant + ld.Thearch.Asmb = asmb + ld.Thearch.Elfreloc1 = elfreloc1 + ld.Thearch.Elfsetupplt = elfsetupplt + ld.Thearch.Gentext = gentext + ld.Thearch.Machoreloc1 = machoreloc1 + ld.Thearch.Lput = ld.Lputl + ld.Thearch.Wput = ld.Wputl + ld.Thearch.Vput = ld.Vputl + + ld.Thearch.Linuxdynld = "/lib/ld-linux.so.3" // 2 for OABI, 3 for EABI + ld.Thearch.Freebsddynld = "/usr/libexec/ld-elf.so.1" + ld.Thearch.Openbsddynld = "/usr/libexec/ld.so" + ld.Thearch.Netbsddynld = "/libexec/ld.elf_so" + ld.Thearch.Dragonflydynld = "XXX" + ld.Thearch.Solarisdynld = "XXX" +} + +func archinit() { + // getgoextlinkenabled is based on GO_EXTLINK_ENABLED when + // Go was built; see ../../make.bash. + if ld.Linkmode == ld.LinkAuto && obj.Getgoextlinkenabled() == "0" { + ld.Linkmode = ld.LinkInternal + } + + switch ld.HEADTYPE { + default: + if ld.Linkmode == ld.LinkAuto { + ld.Linkmode = ld.LinkInternal + } + if ld.Linkmode == ld.LinkExternal && obj.Getgoextlinkenabled() != "1" { + log.Fatalf("cannot use -linkmode=external with -H %s", ld.Headstr(int(ld.HEADTYPE))) + } + + case obj.Hlinux, + obj.Hfreebsd, + obj.Hnacl, + obj.Hdarwin: + break + } + + switch ld.HEADTYPE { + default: + ld.Exitf("unknown -H option: %v", ld.HEADTYPE) + + case obj.Hplan9: /* plan 9 */ + ld.HEADR = 32 + + if ld.INITTEXT == -1 { + ld.INITTEXT = 4128 + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 4096 + } + + case obj.Hlinux, /* arm elf */ + obj.Hfreebsd, + obj.Hnetbsd, + obj.Hopenbsd: + ld.Debug['d'] = 0 + // with dynamic linking + ld.Elfinit() + ld.HEADR = ld.ELFRESERVE + if ld.INITTEXT == -1 { + ld.INITTEXT = 0x10000 + int64(ld.HEADR) + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 4096 + } + + case obj.Hnacl: + ld.Elfinit() + ld.HEADR = 0x10000 + ld.Funcalign = 16 + if ld.INITTEXT == -1 { + ld.INITTEXT = 0x20000 + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 0x10000 + } + + case obj.Hdarwin: /* apple MACH */ + ld.Debug['w'] = 1 // disable DWARF generataion + ld.Machoinit() + ld.HEADR = ld.INITIAL_MACHO_HEADR + if ld.INITTEXT == -1 { + ld.INITTEXT = 4096 + int64(ld.HEADR) + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 4096 + } + } + + if ld.INITDAT != 0 && ld.INITRND != 0 { + fmt.Printf("warning: -D0x%x is ignored because of -R0x%x\n", uint64(ld.INITDAT), uint32(ld.INITRND)) + } + + // embed goarm to runtime.goarm + s := ld.Linklookup(ld.Ctxt, "runtime.goarm", 0) + + s.Type = obj.SRODATA + ld.Adduint8(ld.Ctxt, s, uint8(ld.Ctxt.Goarm)) +} diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go new file mode 100644 index 0000000000..9d76f0e0c3 --- /dev/null +++ b/src/cmd/link/internal/arm64/asm.go @@ -0,0 +1,435 @@ +// Inferno utils/5l/asm.c +// http://code.google.com/p/inferno-os/source/browse/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package arm64 + +import ( + "cmd/internal/obj" + "cmd/link/internal/ld" + "encoding/binary" + "fmt" + "log" +) + +func gentext() {} + +func adddynrela(rel *ld.LSym, s *ld.LSym, r *ld.Reloc) { + log.Fatalf("adddynrela not implemented") +} + +func adddynrel(s *ld.LSym, r *ld.Reloc) { + log.Fatalf("adddynrel not implemented") +} + +func elfreloc1(r *ld.Reloc, sectoff int64) int { + ld.Thearch.Vput(uint64(sectoff)) + + elfsym := r.Xsym.Elfsym + switch r.Type { + default: + return -1 + + case obj.R_ADDR: + switch r.Siz { + case 4: + ld.Thearch.Vput(ld.R_AARCH64_ABS32 | uint64(elfsym)<<32) + case 8: + ld.Thearch.Vput(ld.R_AARCH64_ABS64 | uint64(elfsym)<<32) + default: + return -1 + } + + case obj.R_ADDRARM64: + // two relocations: R_AARCH64_ADR_PREL_PG_HI21 and R_AARCH64_ADD_ABS_LO12_NC + ld.Thearch.Vput(ld.R_AARCH64_ADR_PREL_PG_HI21 | uint64(elfsym)<<32) + ld.Thearch.Vput(uint64(r.Xadd)) + ld.Thearch.Vput(uint64(sectoff + 4)) + ld.Thearch.Vput(ld.R_AARCH64_ADD_ABS_LO12_NC | uint64(elfsym)<<32) + + case obj.R_CALLARM64: + if r.Siz != 4 { + return -1 + } + ld.Thearch.Vput(ld.R_AARCH64_CALL26 | uint64(elfsym)<<32) + + } + ld.Thearch.Vput(uint64(r.Xadd)) + + return 0 +} + +func elfsetupplt() { + // TODO(aram) + return +} + +func machoreloc1(r *ld.Reloc, sectoff int64) int { + var v uint32 + + rs := r.Xsym + + // ld64 has a bug handling MACHO_ARM64_RELOC_UNSIGNED with !extern relocation. + // see cmd/internal/ld/data.go for details. The workarond is that don't use !extern + // UNSIGNED relocation at all. + if rs.Type == obj.SHOSTOBJ || r.Type == obj.R_CALLARM64 || r.Type == obj.R_ADDRARM64 || r.Type == obj.R_ADDR { + if rs.Dynid < 0 { + ld.Diag("reloc %d to non-macho symbol %s type=%d", r.Type, rs.Name, rs.Type) + return -1 + } + + v = uint32(rs.Dynid) + v |= 1 << 27 // external relocation + } else { + v = uint32((rs.Sect.(*ld.Section)).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) + return -1 + } + } + + switch r.Type { + default: + return -1 + + case obj.R_ADDR: + v |= ld.MACHO_ARM64_RELOC_UNSIGNED << 28 + + case obj.R_CALLARM64: + if r.Xadd != 0 { + ld.Diag("ld64 doesn't allow BR26 reloc with non-zero addend: %s+%d", rs.Name, r.Xadd) + } + + v |= 1 << 24 // pc-relative bit + v |= ld.MACHO_ARM64_RELOC_BRANCH26 << 28 + + case obj.R_ADDRARM64: + r.Siz = 4 + // Two relocation entries: MACHO_ARM64_RELOC_PAGEOFF12 MACHO_ARM64_RELOC_PAGE21 + // if r.Xadd is non-zero, add two MACHO_ARM64_RELOC_ADDEND. + if r.Xadd != 0 { + ld.Thearch.Lput(uint32(sectoff + 4)) + ld.Thearch.Lput((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(r.Xadd&0xffffff)) + } + ld.Thearch.Lput(uint32(sectoff + 4)) + ld.Thearch.Lput(v | (ld.MACHO_ARM64_RELOC_PAGEOFF12 << 28) | (2 << 25)) + if r.Xadd != 0 { + ld.Thearch.Lput(uint32(sectoff)) + ld.Thearch.Lput((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(r.Xadd&0xffffff)) + } + v |= 1 << 24 // pc-relative bit + v |= ld.MACHO_ARM64_RELOC_PAGE21 << 28 + } + + switch r.Siz { + default: + return -1 + + case 1: + v |= 0 << 25 + + case 2: + v |= 1 << 25 + + case 4: + v |= 2 << 25 + + case 8: + v |= 3 << 25 + } + + ld.Thearch.Lput(uint32(sectoff)) + ld.Thearch.Lput(v) + return 0 +} + +func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int { + if ld.Linkmode == ld.LinkExternal { + switch r.Type { + default: + return -1 + + case obj.R_ADDRARM64: + r.Done = 0 + + // set up addend for eventual relocation via outer symbol. + rs := r.Sym + r.Xadd = r.Add + for rs.Outer != nil { + r.Xadd += ld.Symaddr(rs) - ld.Symaddr(rs.Outer) + rs = rs.Outer + } + + if rs.Type != obj.SHOSTOBJ && rs.Sect == nil { + ld.Diag("missing section for %s", rs.Name) + } + r.Xsym = rs + + // the first instruction is always at the lower address, this is endian neutral; + // but note that o0 and o1 should still use the target endian. + o0 := ld.Thelinkarch.ByteOrder.Uint32(s.P[r.Off : r.Off+4]) + o1 := ld.Thelinkarch.ByteOrder.Uint32(s.P[r.Off+4 : r.Off+8]) + + // Note: ld64 currently has a bug that any non-zero addend for BR26 relocation + // will make the linking fail because it thinks the code is not PIC even though + // the BR26 relocation should be fully resolved at link time. + // That is the reason why the next if block is disabled. When the bug in ld64 + // is fixed, we can enable this block and also enable duff's device in cmd/7g. + if false && ld.HEADTYPE == obj.Hdarwin { + // Mach-O wants the addend to be encoded in the instruction + // Note that although Mach-O supports ARM64_RELOC_ADDEND, it + // can only encode 24-bit of signed addend, but the instructions + // supports 33-bit of signed addend, so we always encode the + // addend in place. + o0 |= (uint32((r.Xadd>>12)&3) << 29) | (uint32((r.Xadd>>12>>2)&0x7ffff) << 5) + o1 |= uint32(r.Xadd&0xfff) << 10 + r.Xadd = 0 + } + + // when laid out, the instruction order must always be o1, o2. + if ld.Ctxt.Arch.ByteOrder == binary.BigEndian { + *val = int64(o0)<<32 | int64(o1) + } else { + *val = int64(o1)<<32 | int64(o0) + } + + return 0 + + case obj.R_CALLARM64: + r.Done = 0 + r.Xsym = r.Sym + *val = int64(0xfc000000 & uint32(r.Add)) + r.Xadd = int64((uint32(r.Add) &^ 0xfc000000) * 4) + r.Add = 0 + return 0 + } + } + + switch r.Type { + case obj.R_CONST: + *val = r.Add + return 0 + + case obj.R_GOTOFF: + *val = ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ld.Linklookup(ld.Ctxt, ".got", 0)) + return 0 + + case obj.R_ADDRARM64: + t := ld.Symaddr(r.Sym) + r.Add - ((s.Value + int64(r.Off)) &^ 0xfff) + if t >= 1<<32 || t < -1<<32 { + ld.Diag("program too large, address relocation distance = %d", t) + } + + // the first instruction is always at the lower address, this is endian neutral; + // but note that o0 and o1 should still use the target endian. + o0 := ld.Thelinkarch.ByteOrder.Uint32(s.P[r.Off : r.Off+4]) + o1 := ld.Thelinkarch.ByteOrder.Uint32(s.P[r.Off+4 : r.Off+8]) + + o0 |= (uint32((t>>12)&3) << 29) | (uint32((t>>12>>2)&0x7ffff) << 5) + o1 |= uint32(t&0xfff) << 10 + + // when laid out, the instruction order must always be o1, o2. + if ld.Ctxt.Arch.ByteOrder == binary.BigEndian { + *val = int64(o0)<<32 | int64(o1) + } else { + *val = int64(o1)<<32 | int64(o0) + } + return 0 + + case obj.R_CALLARM64: + *val = int64((0xfc000000 & uint32(r.Add)) | uint32((ld.Symaddr(r.Sym)+r.Add*4-(s.Value+int64(r.Off)))/4)) + return 0 + } + + return -1 +} + +func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 { + log.Fatalf("unexpected relocation variant") + return -1 +} + +func asmb() { + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime()) + } + ld.Bso.Flush() + + if ld.Iself { + ld.Asmbelfsetup() + } + + sect := ld.Segtext.Sect + ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Codeblk(int64(sect.Vaddr), int64(sect.Length)) + for sect = sect.Next; sect != nil; sect = sect.Next { + ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Datblk(int64(sect.Vaddr), int64(sect.Length)) + } + + if ld.Segrodata.Filelen > 0 { + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) + } + ld.Bso.Flush() + + ld.Cseek(int64(ld.Segrodata.Fileoff)) + ld.Datblk(int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) + } + + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f datblk\n", obj.Cputime()) + } + ld.Bso.Flush() + + ld.Cseek(int64(ld.Segdata.Fileoff)) + ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + + machlink := uint32(0) + if ld.HEADTYPE == obj.Hdarwin { + if ld.Debug['v'] != 0 { + 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)) + + ld.Segdwarf.Fileoff = uint64(ld.Cpos()) + ld.Dwarfemitdebugsections() + ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff + + machlink = uint32(ld.Domacholink()) + } + + /* output symbol table */ + ld.Symsize = 0 + + ld.Lcsize = 0 + symo := uint32(0) + if ld.Debug['s'] == 0 { + // TODO: rationalize + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime()) + } + ld.Bso.Flush() + switch ld.HEADTYPE { + default: + if ld.Iself { + symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) + symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND))) + } + + case obj.Hplan9: + 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)) + } + + ld.Cseek(int64(symo)) + switch ld.HEADTYPE { + default: + if ld.Iself { + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f elfsym\n", obj.Cputime()) + } + ld.Asmelfsym() + ld.Cflush() + ld.Cwrite(ld.Elfstrdat) + + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) + } + ld.Dwarfemitdebugsections() + + if ld.Linkmode == ld.LinkExternal { + ld.Elfemitreloc() + } + } + + case obj.Hplan9: + ld.Asmplan9sym() + ld.Cflush() + + sym := ld.Linklookup(ld.Ctxt, "pclntab", 0) + if sym != nil { + ld.Lcsize = int32(len(sym.P)) + for i := 0; int32(i) < ld.Lcsize; i++ { + ld.Cput(uint8(sym.P[i])) + } + + ld.Cflush() + } + + case obj.Hdarwin: + if ld.Linkmode == ld.LinkExternal { + ld.Machoemitreloc() + } + } + } + + ld.Ctxt.Cursym = nil + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f header\n", obj.Cputime()) + } + ld.Bso.Flush() + ld.Cseek(0) + switch ld.HEADTYPE { + default: + case obj.Hplan9: /* plan 9 */ + ld.Thearch.Lput(0x647) /* magic */ + ld.Thearch.Lput(uint32(ld.Segtext.Filelen)) /* sizes */ + ld.Thearch.Lput(uint32(ld.Segdata.Filelen)) + ld.Thearch.Lput(uint32(ld.Segdata.Length - ld.Segdata.Filelen)) + ld.Thearch.Lput(uint32(ld.Symsize)) /* nsyms */ + ld.Thearch.Lput(uint32(ld.Entryvalue())) /* va of entry */ + ld.Thearch.Lput(0) + ld.Thearch.Lput(uint32(ld.Lcsize)) + + case obj.Hlinux, + obj.Hfreebsd, + obj.Hnetbsd, + obj.Hopenbsd, + obj.Hnacl: + ld.Asmbelf(int64(symo)) + + case obj.Hdarwin: + ld.Asmbmacho() + } + + ld.Cflush() + if ld.Debug['c'] != 0 { + fmt.Printf("textsize=%d\n", ld.Segtext.Filelen) + fmt.Printf("datsize=%d\n", ld.Segdata.Filelen) + fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen) + fmt.Printf("symsize=%d\n", ld.Symsize) + fmt.Printf("lcsize=%d\n", ld.Lcsize) + fmt.Printf("total=%d\n", ld.Segtext.Filelen+ld.Segdata.Length+uint64(ld.Symsize)+uint64(ld.Lcsize)) + } +} diff --git a/src/cmd/link/internal/arm64/l.go b/src/cmd/link/internal/arm64/l.go new file mode 100644 index 0000000000..8d0d57e72a --- /dev/null +++ b/src/cmd/link/internal/arm64/l.go @@ -0,0 +1,78 @@ +// Inferno utils/5l/asm.c +// http://code.google.com/p/inferno-os/source/browse/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package arm64 + +// Writing object files. + +// cmd/9l/l.h from Vita Nuova. +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +const ( + thechar = '7' + PtrSize = 8 + IntSize = 8 + RegSize = 8 + MaxAlign = 32 // max data alignment + FuncAlign = 8 + MINLC = 4 +) + +/* Used by ../internal/ld/dwarf.go */ +const ( + DWARFREGSP = 31 + DWARFREGLR = 30 +) diff --git a/src/cmd/link/internal/arm64/obj.go b/src/cmd/link/internal/arm64/obj.go new file mode 100644 index 0000000000..56f5815903 --- /dev/null +++ b/src/cmd/link/internal/arm64/obj.go @@ -0,0 +1,169 @@ +// Inferno utils/5l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/5l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package arm64 + +import ( + "cmd/internal/obj" + "cmd/link/internal/ld" + "fmt" + "log" +) + +// Reading object files. + +func Main() { + linkarchinit() + ld.Ldmain() +} + +func linkarchinit() { + ld.Thestring = obj.Getgoarch() + ld.Thelinkarch = &ld.Linkarm64 + + ld.Thearch.Thechar = thechar + ld.Thearch.Ptrsize = ld.Thelinkarch.Ptrsize + ld.Thearch.Intsize = ld.Thelinkarch.Ptrsize + ld.Thearch.Regsize = ld.Thelinkarch.Regsize + ld.Thearch.Funcalign = FuncAlign + ld.Thearch.Maxalign = MaxAlign + ld.Thearch.Minlc = MINLC + ld.Thearch.Dwarfregsp = DWARFREGSP + ld.Thearch.Dwarfreglr = DWARFREGLR + + ld.Thearch.Adddynrel = adddynrel + ld.Thearch.Archinit = archinit + ld.Thearch.Archreloc = archreloc + ld.Thearch.Archrelocvariant = archrelocvariant + ld.Thearch.Asmb = asmb + ld.Thearch.Elfreloc1 = elfreloc1 + ld.Thearch.Elfsetupplt = elfsetupplt + ld.Thearch.Gentext = gentext + ld.Thearch.Machoreloc1 = machoreloc1 + ld.Thearch.Lput = ld.Lputl + ld.Thearch.Wput = ld.Wputl + ld.Thearch.Vput = ld.Vputl + + ld.Thearch.Linuxdynld = "/lib/ld-linux-aarch64.so.1" + + ld.Thearch.Freebsddynld = "XXX" + ld.Thearch.Openbsddynld = "XXX" + ld.Thearch.Netbsddynld = "XXX" + ld.Thearch.Dragonflydynld = "XXX" + ld.Thearch.Solarisdynld = "XXX" +} + +func archinit() { + // getgoextlinkenabled is based on GO_EXTLINK_ENABLED when + // Go was built; see ../../make.bash. + if ld.Linkmode == ld.LinkAuto && obj.Getgoextlinkenabled() == "0" { + ld.Linkmode = ld.LinkInternal + } + + // Darwin/arm64 only supports external linking + if ld.HEADTYPE == obj.Hdarwin { + ld.Linkmode = ld.LinkExternal + } + + switch ld.HEADTYPE { + default: + if ld.Linkmode == ld.LinkAuto { + ld.Linkmode = ld.LinkInternal + } + if ld.Linkmode == ld.LinkExternal && obj.Getgoextlinkenabled() != "1" { + log.Fatalf("cannot use -linkmode=external with -H %s", ld.Headstr(int(ld.HEADTYPE))) + } + case obj.Hlinux, obj.Hdarwin: + break + } + + switch ld.HEADTYPE { + default: + ld.Exitf("unknown -H option: %v", ld.HEADTYPE) + + case obj.Hplan9: /* plan 9 */ + ld.HEADR = 32 + + if ld.INITTEXT == -1 { + ld.INITTEXT = 4128 + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 4096 + } + + case obj.Hlinux: /* arm64 elf */ + ld.Elfinit() + ld.HEADR = ld.ELFRESERVE + if ld.INITTEXT == -1 { + ld.INITTEXT = 0x10000 + int64(ld.HEADR) + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 0x10000 + } + + case obj.Hdarwin: /* apple MACH */ + ld.Debug['w'] = 1 // disable DWARF generation + ld.Machoinit() + ld.HEADR = ld.INITIAL_MACHO_HEADR + if ld.INITTEXT == -1 { + ld.INITTEXT = 4096 + int64(ld.HEADR) + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 4096 + } + + case obj.Hnacl: + ld.Elfinit() + ld.HEADR = 0x10000 + ld.Funcalign = 16 + if ld.INITTEXT == -1 { + ld.INITTEXT = 0x20000 + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 0x10000 + } + } + + if ld.INITDAT != 0 && ld.INITRND != 0 { + fmt.Printf("warning: -D0x%x is ignored because of -R0x%x\n", uint64(ld.INITDAT), uint32(ld.INITRND)) + } +} diff --git a/src/cmd/link/internal/ld/ar.go b/src/cmd/link/internal/ld/ar.go new file mode 100644 index 0000000000..0e59016edb --- /dev/null +++ b/src/cmd/link/internal/ld/ar.go @@ -0,0 +1,52 @@ +// Inferno utils/include/ar.h +// http://code.google.com/p/inferno-os/source/browse/utils/include/ar.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +const ( + SARMAG = 8 + SARNAME = 16 + SAR_HDR = 16 + 44 +) + +const ( + ARMAG = "!\n" + ARFMAG = "`\n" +) + +type ArHdr struct { + name string + date string + uid string + gid string + mode string + size string + fmag string +} diff --git a/src/cmd/link/internal/ld/arch.go b/src/cmd/link/internal/ld/arch.go new file mode 100644 index 0000000000..1b8e1b19ba --- /dev/null +++ b/src/cmd/link/internal/ld/arch.go @@ -0,0 +1,70 @@ +// 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 "encoding/binary" + +var Linkarm = LinkArch{ + ByteOrder: binary.LittleEndian, + Name: "arm", + Thechar: '5', + Minlc: 4, + Ptrsize: 4, + Regsize: 4, +} + +var Linkarm64 = LinkArch{ + ByteOrder: binary.LittleEndian, + Name: "arm64", + Thechar: '7', + Minlc: 4, + Ptrsize: 8, + Regsize: 8, +} + +var Linkamd64 = LinkArch{ + ByteOrder: binary.LittleEndian, + Name: "amd64", + Thechar: '6', + Minlc: 1, + Ptrsize: 8, + Regsize: 8, +} + +var Linkamd64p32 = LinkArch{ + ByteOrder: binary.LittleEndian, + Name: "amd64p32", + Thechar: '6', + Minlc: 1, + Ptrsize: 4, + Regsize: 8, +} + +var Link386 = LinkArch{ + ByteOrder: binary.LittleEndian, + Name: "386", + Thechar: '8', + Minlc: 1, + Ptrsize: 4, + Regsize: 4, +} + +var Linkppc64 = LinkArch{ + ByteOrder: binary.BigEndian, + Name: "ppc64", + Thechar: '9', + Minlc: 4, + Ptrsize: 8, + Regsize: 8, +} + +var Linkppc64le = LinkArch{ + ByteOrder: binary.LittleEndian, + Name: "ppc64le", + Thechar: '9', + Minlc: 4, + Ptrsize: 8, + Regsize: 8, +} diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go new file mode 100644 index 0000000000..f9aacf0e19 --- /dev/null +++ b/src/cmd/link/internal/ld/data.go @@ -0,0 +1,1721 @@ +// Derived from Inferno utils/6l/obj.c and utils/6l/span.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/span.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "cmd/internal/gcprog" + "cmd/internal/obj" + "fmt" + "log" + "os" + "strings" +) + +func Symgrow(ctxt *Link, s *LSym, siz int64) { + if int64(int(siz)) != siz { + log.Fatalf("symgrow size %d too long", siz) + } + if int64(len(s.P)) >= siz { + return + } + for cap(s.P) < int(siz) { + s.P = append(s.P[:len(s.P)], 0) + } + s.P = s.P[:siz] +} + +func Addrel(s *LSym) *Reloc { + s.R = append(s.R, Reloc{}) + return &s.R[len(s.R)-1] +} + +func setuintxx(ctxt *Link, s *LSym, off int64, v uint64, wid int64) int64 { + if s.Type == 0 { + s.Type = obj.SDATA + } + s.Reachable = true + if s.Size < off+wid { + s.Size = off + wid + Symgrow(ctxt, s, s.Size) + } + + switch wid { + case 1: + s.P[off] = uint8(v) + case 2: + ctxt.Arch.ByteOrder.PutUint16(s.P[off:], uint16(v)) + case 4: + ctxt.Arch.ByteOrder.PutUint32(s.P[off:], uint32(v)) + case 8: + ctxt.Arch.ByteOrder.PutUint64(s.P[off:], uint64(v)) + } + + return off + wid +} + +func adduintxx(ctxt *Link, s *LSym, v uint64, wid int) int64 { + off := s.Size + setuintxx(ctxt, s, off, v, int64(wid)) + return off +} + +func Adduint8(ctxt *Link, s *LSym, v uint8) int64 { + return adduintxx(ctxt, s, uint64(v), 1) +} + +func Adduint16(ctxt *Link, s *LSym, v uint16) int64 { + return adduintxx(ctxt, s, uint64(v), 2) +} + +func Adduint32(ctxt *Link, s *LSym, v uint32) int64 { + return adduintxx(ctxt, s, uint64(v), 4) +} + +func Adduint64(ctxt *Link, s *LSym, v uint64) int64 { + return adduintxx(ctxt, s, v, 8) +} + +func adduint(ctxt *Link, s *LSym, v uint64) int64 { + return adduintxx(ctxt, s, v, Thearch.Intsize) +} + +func setuint8(ctxt *Link, s *LSym, r int64, v uint8) int64 { + return setuintxx(ctxt, s, r, uint64(v), 1) +} + +func setuint32(ctxt *Link, s *LSym, r int64, v uint32) int64 { + return setuintxx(ctxt, s, r, uint64(v), 4) +} + +func Addaddrplus(ctxt *Link, s *LSym, t *LSym, add int64) int64 { + if s.Type == 0 { + s.Type = obj.SDATA + } + s.Reachable = true + i := s.Size + s.Size += int64(ctxt.Arch.Ptrsize) + Symgrow(ctxt, s, s.Size) + r := Addrel(s) + r.Sym = t + r.Off = int32(i) + r.Siz = uint8(ctxt.Arch.Ptrsize) + r.Type = obj.R_ADDR + r.Add = add + return i + int64(r.Siz) +} + +func Addpcrelplus(ctxt *Link, s *LSym, t *LSym, add int64) int64 { + if s.Type == 0 { + s.Type = obj.SDATA + } + s.Reachable = true + i := s.Size + s.Size += 4 + Symgrow(ctxt, s, s.Size) + r := Addrel(s) + r.Sym = t + r.Off = int32(i) + r.Add = add + r.Type = obj.R_PCREL + r.Siz = 4 + return i + int64(r.Siz) +} + +func Addaddr(ctxt *Link, s *LSym, t *LSym) int64 { + return Addaddrplus(ctxt, s, t, 0) +} + +func setaddrplus(ctxt *Link, s *LSym, off int64, t *LSym, add int64) int64 { + if s.Type == 0 { + s.Type = obj.SDATA + } + s.Reachable = true + if off+int64(ctxt.Arch.Ptrsize) > s.Size { + s.Size = off + int64(ctxt.Arch.Ptrsize) + Symgrow(ctxt, s, s.Size) + } + + r := Addrel(s) + r.Sym = t + r.Off = int32(off) + r.Siz = uint8(ctxt.Arch.Ptrsize) + r.Type = obj.R_ADDR + r.Add = add + return off + int64(r.Siz) +} + +func setaddr(ctxt *Link, s *LSym, off int64, t *LSym) int64 { + return setaddrplus(ctxt, s, off, t, 0) +} + +func addsize(ctxt *Link, s *LSym, t *LSym) int64 { + if s.Type == 0 { + s.Type = obj.SDATA + } + s.Reachable = true + i := s.Size + s.Size += int64(ctxt.Arch.Ptrsize) + Symgrow(ctxt, s, s.Size) + r := Addrel(s) + r.Sym = t + r.Off = int32(i) + r.Siz = uint8(ctxt.Arch.Ptrsize) + r.Type = obj.R_SIZE + return i + int64(r.Siz) +} + +func addaddrplus4(ctxt *Link, s *LSym, t *LSym, add int64) int64 { + if s.Type == 0 { + s.Type = obj.SDATA + } + s.Reachable = true + i := s.Size + s.Size += 4 + Symgrow(ctxt, s, s.Size) + r := Addrel(s) + r.Sym = t + r.Off = int32(i) + r.Siz = 4 + r.Type = obj.R_ADDR + r.Add = add + return i + int64(r.Siz) +} + +/* + * divide-and-conquer list-link + * sort of LSym* structures. + * Used for the data block. + */ +func datcmp(s1 *LSym, s2 *LSym) int { + if s1.Type != s2.Type { + return int(s1.Type) - int(s2.Type) + } + + // For ppc64, we want to interleave the .got and .toc sections + // from input files. Both are type SELFGOT, so in that case + // fall through to the name comparison (conveniently, .got + // sorts before .toc). + if s1.Type != obj.SELFGOT && s1.Size != s2.Size { + if s1.Size < s2.Size { + return -1 + } + return +1 + } + + return stringsCompare(s1.Name, s2.Name) +} + +func listnextp(s *LSym) **LSym { + return &s.Next +} + +func listsubp(s *LSym) **LSym { + return &s.Sub +} + +func listsort(l *LSym, cmp func(*LSym, *LSym) int, nextp func(*LSym) **LSym) *LSym { + if l == nil || *nextp(l) == nil { + return l + } + + l1 := l + l2 := l + for { + l2 = *nextp(l2) + if l2 == nil { + break + } + l2 = *nextp(l2) + if l2 == nil { + break + } + l1 = *nextp(l1) + } + + l2 = *nextp(l1) + *nextp(l1) = nil + l1 = listsort(l, cmp, nextp) + l2 = listsort(l2, cmp, nextp) + + /* set up lead element */ + if cmp(l1, l2) < 0 { + l = l1 + l1 = *nextp(l1) + } else { + l = l2 + l2 = *nextp(l2) + } + + le := l + + for { + if l1 == nil { + for l2 != nil { + *nextp(le) = l2 + le = l2 + l2 = *nextp(l2) + } + + *nextp(le) = nil + break + } + + if l2 == nil { + for l1 != nil { + *nextp(le) = l1 + le = l1 + l1 = *nextp(l1) + } + + break + } + + if cmp(l1, l2) < 0 { + *nextp(le) = l1 + le = l1 + l1 = *nextp(l1) + } else { + *nextp(le) = l2 + le = l2 + l2 = *nextp(l2) + } + } + + *nextp(le) = nil + return l +} + +func relocsym(s *LSym) { + var r *Reloc + var rs *LSym + var i16 int16 + var off int32 + var siz int32 + var fl int32 + var o int64 + + Ctxt.Cursym = s + for ri := int32(0); ri < int32(len(s.R)); ri++ { + r = &s.R[ri] + r.Done = 1 + off = r.Off + siz = int32(r.Siz) + if off < 0 || off+siz > int32(len(s.P)) { + Diag("%s: invalid relocation %d+%d not in [%d,%d)", s.Name, off, siz, 0, len(s.P)) + continue + } + + if r.Sym != nil && (r.Sym.Type&(obj.SMASK|obj.SHIDDEN) == 0 || r.Sym.Type&obj.SMASK == obj.SXREF) { + // When putting the runtime but not main into a shared library + // these symbols are undefined and that's OK. + if Buildmode == BuildmodeShared && (r.Sym.Name == "main.main" || r.Sym.Name == "main.init") { + r.Sym.Type = obj.SDYNIMPORT + } else { + Diag("%s: not defined", r.Sym.Name) + continue + } + } + + if r.Type >= 256 { + continue + } + if r.Siz == 0 { // informational relocation - no work to do + continue + } + + // We need to be able to reference dynimport symbols when linking against + // shared libraries, and Solaris needs it always + if HEADTYPE != obj.Hsolaris && r.Sym != nil && r.Sym.Type == obj.SDYNIMPORT && !DynlinkingGo() { + Diag("unhandled relocation for %s (type %d rtype %d)", r.Sym.Name, r.Sym.Type, r.Type) + } + if r.Sym != nil && r.Sym.Type != obj.STLSBSS && !r.Sym.Reachable { + Diag("unreachable sym in relocation: %s %s", s.Name, r.Sym.Name) + } + + // Android emulates runtime.tlsg as a regular variable. + if r.Type == obj.R_TLS && goos == "android" { + r.Type = obj.R_ADDR + } + + switch r.Type { + default: + o = 0 + if Thearch.Archreloc(r, s, &o) < 0 { + Diag("unknown reloc %d", r.Type) + } + + case obj.R_TLS: + if Linkmode == LinkExternal && Iself && HEADTYPE != obj.Hopenbsd { + r.Done = 0 + r.Sym = Ctxt.Tlsg + r.Xsym = Ctxt.Tlsg + r.Xadd = r.Add + o = r.Add + break + } + if Linkmode == LinkInternal && Iself && Thearch.Thechar == '5' { + // On ELF ARM, the thread pointer is 8 bytes before + // the start of the thread-local data block, so add 8 + // to the actual TLS offset (r->sym->value). + // This 8 seems to be a fundamental constant of + // ELF on ARM (or maybe Glibc on ARM); it is not + // related to the fact that our own TLS storage happens + // to take up 8 bytes. + o = 8 + r.Sym.Value + + break + } + + r.Done = 0 + o = 0 + if Thearch.Thechar != '6' { + o = r.Add + } + + case obj.R_TLS_LE: + if Linkmode == LinkExternal && Iself && HEADTYPE != obj.Hopenbsd { + r.Done = 0 + r.Sym = Ctxt.Tlsg + r.Xsym = Ctxt.Tlsg + r.Xadd = r.Add + o = 0 + if Thearch.Thechar != '6' { + o = r.Add + } + break + } + + if Iself || Ctxt.Headtype == obj.Hplan9 || Ctxt.Headtype == obj.Hdarwin { + o = int64(Ctxt.Tlsoffset) + r.Add + } else if Ctxt.Headtype == obj.Hwindows { + o = r.Add + } else { + log.Fatalf("unexpected R_TLS_LE relocation for %s", Headstr(Ctxt.Headtype)) + } + + case obj.R_TLS_IE: + if Linkmode == LinkExternal && Iself && HEADTYPE != obj.Hopenbsd { + r.Done = 0 + r.Sym = Ctxt.Tlsg + r.Xsym = Ctxt.Tlsg + r.Xadd = r.Add + o = 0 + if Thearch.Thechar != '6' { + o = r.Add + } + break + } + log.Fatalf("cannot handle R_TLS_IE when linking internally") + + case obj.R_ADDR: + if Linkmode == LinkExternal && r.Sym.Type != obj.SCONST { + r.Done = 0 + + // set up addend for eventual relocation via outer symbol. + rs = r.Sym + + r.Xadd = r.Add + for rs.Outer != nil { + r.Xadd += Symaddr(rs) - Symaddr(rs.Outer) + rs = rs.Outer + } + + if rs.Type != obj.SHOSTOBJ && rs.Type != obj.SDYNIMPORT && rs.Sect == nil { + Diag("missing section for %s", rs.Name) + } + r.Xsym = rs + + o = r.Xadd + if Iself { + if Thearch.Thechar == '6' { + o = 0 + } + } else if HEADTYPE == obj.Hdarwin { + // ld64 for arm64 has a bug where if the address pointed to by o exists in the + // symbol table (dynid >= 0), or is inside a symbol that exists in the symbol + // table, then it will add o twice into the relocated value. + // The workaround is that on arm64 don't ever add symaddr to o and always use + // extern relocation by requiring rs->dynid >= 0. + if rs.Type != obj.SHOSTOBJ { + if Thearch.Thechar == '7' && rs.Dynid < 0 { + Diag("R_ADDR reloc to %s+%d is not supported on darwin/arm64", rs.Name, o) + } + if Thearch.Thechar != '7' { + o += Symaddr(rs) + } + } + } else if HEADTYPE == obj.Hwindows { + // nothing to do + } else { + Diag("unhandled pcrel relocation for %s", headstring) + } + + break + } + + o = Symaddr(r.Sym) + r.Add + + // On amd64, 4-byte offsets will be sign-extended, so it is impossible to + // access more than 2GB of static data; fail at link time is better than + // fail at runtime. See http://golang.org/issue/7980. + // Instead of special casing only amd64, we treat this as an error on all + // 64-bit architectures so as to be future-proof. + if int32(o) < 0 && Thearch.Ptrsize > 4 && siz == 4 { + Diag("non-pc-relative relocation address is too big: %#x (%#x + %#x)", uint64(o), Symaddr(r.Sym), r.Add) + errorexit() + } + + // r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call. + case obj.R_CALL, obj.R_GOTPCREL, obj.R_PCREL: + if Linkmode == LinkExternal && r.Sym != nil && r.Sym.Type != obj.SCONST && (r.Sym.Sect != Ctxt.Cursym.Sect || r.Type == obj.R_GOTPCREL) { + r.Done = 0 + + // set up addend for eventual relocation via outer symbol. + rs = r.Sym + + r.Xadd = r.Add + for rs.Outer != nil { + r.Xadd += Symaddr(rs) - Symaddr(rs.Outer) + rs = rs.Outer + } + + r.Xadd -= int64(r.Siz) // relative to address after the relocated chunk + if rs.Type != obj.SHOSTOBJ && rs.Type != obj.SDYNIMPORT && rs.Sect == nil { + Diag("missing section for %s", rs.Name) + } + r.Xsym = rs + + o = r.Xadd + if Iself { + if Thearch.Thechar == '6' { + o = 0 + } + } 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(r.Off) // relative to section offset, not symbol + } else { + o += int64(r.Siz) + } + } else if HEADTYPE == obj.Hwindows && Thearch.Thechar == '6' { // only amd64 needs PCREL + // PE/COFF's PC32 relocation uses the address after the relocated + // bytes as the base. Compensate by skewing the addend. + 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) + } else { + Diag("unhandled pcrel relocation for %s", headstring) + } + + break + } + + o = 0 + if r.Sym != nil { + o += Symaddr(r.Sym) + } + + // NOTE: The (int32) cast on the next line works around a bug in Plan 9's 8c + // compiler. The expression s->value + r->off + r->siz is int32 + int32 + + // uchar, and Plan 9 8c incorrectly treats the expression as type uint32 + // instead of int32, causing incorrect values when sign extended for adding + // to o. The bug only occurs on Plan 9, because this C program is compiled by + // the standard host compiler (gcc on most other systems). + o += r.Add - (s.Value + int64(r.Off) + int64(int32(r.Siz))) + + case obj.R_SIZE: + o = r.Sym.Size + r.Add + } + + if r.Variant != RV_NONE { + o = Thearch.Archrelocvariant(r, s, o) + } + + if false { + nam := "" + if r.Sym != nil { + nam = r.Sym.Name + } + fmt.Printf("relocate %s %#x (%#x+%#x, size %d) => %s %#x +%#x [type %d/%d, %x]\n", s.Name, s.Value+int64(off), s.Value, r.Off, r.Siz, nam, Symaddr(r.Sym), r.Add, r.Type, r.Variant, o) + } + switch siz { + default: + Ctxt.Cursym = s + Diag("bad reloc size %#x for %s", uint32(siz), r.Sym.Name) + fallthrough + + // TODO(rsc): Remove. + case 1: + s.P[off] = byte(int8(o)) + + case 2: + if o != int64(int16(o)) { + Diag("relocation address is too big: %#x", o) + } + i16 = int16(o) + Ctxt.Arch.ByteOrder.PutUint16(s.P[off:], uint16(i16)) + + case 4: + if r.Type == obj.R_PCREL || r.Type == obj.R_CALL { + if o != int64(int32(o)) { + Diag("pc-relative relocation address is too big: %#x", o) + } + } else { + if o != int64(int32(o)) && o != int64(uint32(o)) { + Diag("non-pc-relative relocation address is too big: %#x", uint64(o)) + } + } + + fl = int32(o) + Ctxt.Arch.ByteOrder.PutUint32(s.P[off:], uint32(fl)) + + case 8: + Ctxt.Arch.ByteOrder.PutUint64(s.P[off:], uint64(o)) + } + } +} + +func reloc() { + if Debug['v'] != 0 { + fmt.Fprintf(&Bso, "%5.2f reloc\n", obj.Cputime()) + } + Bso.Flush() + + for s := Ctxt.Textp; s != nil; s = s.Next { + relocsym(s) + } + for s := datap; s != nil; s = s.Next { + relocsym(s) + } +} + +func dynrelocsym(s *LSym) { + if HEADTYPE == obj.Hwindows && Linkmode != LinkExternal { + rel := Linklookup(Ctxt, ".rel", 0) + if s == rel { + return + } + var r *Reloc + var targ *LSym + for ri := 0; ri < len(s.R); ri++ { + r = &s.R[ri] + targ = r.Sym + if targ == nil { + continue + } + if !targ.Reachable { + Diag("internal inconsistency: dynamic symbol %s is not reachable.", targ.Name) + } + if r.Sym.Plt == -2 && r.Sym.Got != -2 { // make dynimport JMP table for PE object files. + targ.Plt = int32(rel.Size) + r.Sym = rel + r.Add = int64(targ.Plt) + + // jmp *addr + if Thearch.Thechar == '8' { + Adduint8(Ctxt, rel, 0xff) + Adduint8(Ctxt, rel, 0x25) + Addaddr(Ctxt, rel, targ) + Adduint8(Ctxt, rel, 0x90) + Adduint8(Ctxt, rel, 0x90) + } else { + Adduint8(Ctxt, rel, 0xff) + Adduint8(Ctxt, rel, 0x24) + Adduint8(Ctxt, rel, 0x25) + addaddrplus4(Ctxt, rel, targ, 0) + Adduint8(Ctxt, rel, 0x90) + } + } else if r.Sym.Plt >= 0 { + r.Sym = rel + r.Add = int64(targ.Plt) + } + } + + return + } + + var r *Reloc + for ri := 0; ri < len(s.R); ri++ { + r = &s.R[ri] + if r.Sym != nil && r.Sym.Type == obj.SDYNIMPORT || r.Type >= 256 { + if r.Sym != nil && !r.Sym.Reachable { + Diag("internal inconsistency: dynamic symbol %s is not reachable.", r.Sym.Name) + } + Thearch.Adddynrel(s, r) + } + } +} + +func dynreloc() { + // -d suppresses dynamic loader format, so we may as well not + // compute these sections or mark their symbols as reachable. + if Debug['d'] != 0 && HEADTYPE != obj.Hwindows { + return + } + if Debug['v'] != 0 { + fmt.Fprintf(&Bso, "%5.2f reloc\n", obj.Cputime()) + } + Bso.Flush() + + for s := Ctxt.Textp; s != nil; s = s.Next { + dynrelocsym(s) + } + for s := datap; s != nil; s = s.Next { + dynrelocsym(s) + } + if Iself { + elfdynhash() + } +} + +func blk(start *LSym, addr int64, size int64) { + var sym *LSym + + for sym = start; sym != nil; sym = sym.Next { + if sym.Type&obj.SSUB == 0 && sym.Value >= addr { + break + } + } + + eaddr := addr + size + var ep []byte + var p []byte + for ; sym != nil; sym = sym.Next { + if sym.Type&obj.SSUB != 0 { + continue + } + if sym.Value >= eaddr { + break + } + Ctxt.Cursym = sym + if sym.Value < addr { + Diag("phase error: addr=%#x but sym=%#x type=%d", int64(addr), int64(sym.Value), sym.Type) + errorexit() + } + + for ; addr < sym.Value; addr++ { + Cput(0) + } + p = sym.P + ep = p[len(sym.P):] + for -cap(p) < -cap(ep) { + Cput(uint8(p[0])) + p = p[1:] + } + addr += int64(len(sym.P)) + for ; addr < sym.Value+sym.Size; addr++ { + Cput(0) + } + if addr != sym.Value+sym.Size { + Diag("phase error: addr=%#x value+size=%#x", int64(addr), int64(sym.Value)+sym.Size) + errorexit() + } + + if sym.Value+sym.Size >= eaddr { + break + } + } + + for ; addr < eaddr; addr++ { + Cput(0) + } + Cflush() +} + +func Codeblk(addr int64, size int64) { + if Debug['a'] != 0 { + fmt.Fprintf(&Bso, "codeblk [%#x,%#x) at offset %#x\n", addr, addr+size, Cpos()) + } + + blk(Ctxt.Textp, addr, size) + + /* again for printing */ + if Debug['a'] == 0 { + return + } + + var sym *LSym + for sym = Ctxt.Textp; sym != nil; sym = sym.Next { + if !sym.Reachable { + continue + } + if sym.Value >= addr { + break + } + } + + eaddr := addr + size + var n int64 + var q []byte + for ; sym != nil; sym = sym.Next { + if !sym.Reachable { + continue + } + if sym.Value >= eaddr { + break + } + + if addr < sym.Value { + fmt.Fprintf(&Bso, "%-20s %.8x|", "_", uint64(int64(addr))) + for ; addr < sym.Value; addr++ { + fmt.Fprintf(&Bso, " %.2x", 0) + } + fmt.Fprintf(&Bso, "\n") + } + + fmt.Fprintf(&Bso, "%.6x\t%-20s\n", uint64(int64(addr)), sym.Name) + n = sym.Size + q = sym.P + + for n >= 16 { + fmt.Fprintf(&Bso, "%.6x\t%-20.16I\n", uint64(addr), q) + addr += 16 + q = q[16:] + n -= 16 + } + + if n > 0 { + fmt.Fprintf(&Bso, "%.6x\t%-20.*I\n", uint64(addr), int(n), q) + } + addr += n + } + + if addr < eaddr { + fmt.Fprintf(&Bso, "%-20s %.8x|", "_", uint64(int64(addr))) + for ; addr < eaddr; addr++ { + fmt.Fprintf(&Bso, " %.2x", 0) + } + } + + Bso.Flush() +} + +func Datblk(addr int64, size int64) { + if Debug['a'] != 0 { + fmt.Fprintf(&Bso, "datblk [%#x,%#x) at offset %#x\n", addr, addr+size, Cpos()) + } + + blk(datap, addr, size) + + /* again for printing */ + if Debug['a'] == 0 { + return + } + + var sym *LSym + for sym = datap; sym != nil; sym = sym.Next { + if sym.Value >= addr { + break + } + } + + eaddr := addr + size + var ep []byte + var i int64 + var p []byte + var r *Reloc + var rsname string + var typ string + for ; sym != nil; sym = sym.Next { + if sym.Value >= eaddr { + break + } + if addr < sym.Value { + fmt.Fprintf(&Bso, "\t%.8x| 00 ...\n", uint64(addr)) + addr = sym.Value + } + + fmt.Fprintf(&Bso, "%s\n\t%.8x|", sym.Name, uint(addr)) + p = sym.P + ep = p[len(sym.P):] + for -cap(p) < -cap(ep) { + if -cap(p) > -cap(sym.P) && int(-cap(p)+cap(sym.P))%16 == 0 { + fmt.Fprintf(&Bso, "\n\t%.8x|", uint(addr+int64(-cap(p)+cap(sym.P)))) + } + fmt.Fprintf(&Bso, " %.2x", p[0]) + p = p[1:] + } + + addr += int64(len(sym.P)) + for ; addr < sym.Value+sym.Size; addr++ { + fmt.Fprintf(&Bso, " %.2x", 0) + } + fmt.Fprintf(&Bso, "\n") + + if Linkmode == LinkExternal { + for i = 0; i < int64(len(sym.R)); i++ { + r = &sym.R[i] + rsname = "" + if r.Sym != nil { + rsname = r.Sym.Name + } + typ = "?" + switch r.Type { + case obj.R_ADDR: + typ = "addr" + + case obj.R_PCREL: + typ = "pcrel" + + case obj.R_CALL: + typ = "call" + } + + fmt.Fprintf(&Bso, "\treloc %.8x/%d %s %s+%#x [%#x]\n", uint(sym.Value+int64(r.Off)), r.Siz, typ, rsname, int64(r.Add), int64(r.Sym.Value+r.Add)) + } + } + } + + if addr < eaddr { + fmt.Fprintf(&Bso, "\t%.8x| 00 ...\n", uint(addr)) + } + fmt.Fprintf(&Bso, "\t%.8x|\n", uint(eaddr)) +} + +func strnput(s string, n int) { + for ; n > 0 && s != ""; s = s[1:] { + Cput(uint8(s[0])) + n-- + } + + for n > 0 { + Cput(0) + n-- + } +} + +var addstrdata_name string + +func addstrdata1(arg string) { + if strings.HasPrefix(arg, "VALUE:") { + addstrdata(addstrdata_name, arg[6:]) + } else { + addstrdata_name = arg + } +} + +func addstrdata(name string, value string) { + p := fmt.Sprintf("%s.str", name) + sp := Linklookup(Ctxt, p, 0) + + Addstring(sp, value) + sp.Type = obj.SRODATA + + s := Linklookup(Ctxt, name, 0) + s.Size = 0 + s.Dupok = 1 + reachable := s.Reachable + Addaddr(Ctxt, s, sp) + adduintxx(Ctxt, s, uint64(len(value)), Thearch.Ptrsize) + + // addstring, addaddr, etc., mark the symbols as reachable. + // In this case that is not necessarily true, so stick to what + // we know before entering this function. + s.Reachable = reachable + + sp.Reachable = reachable +} + +func Addstring(s *LSym, str string) int64 { + if s.Type == 0 { + s.Type = obj.SNOPTRDATA + } + s.Reachable = true + r := int32(s.Size) + n := len(str) + 1 + if s.Name == ".shstrtab" { + elfsetstring(str, int(r)) + } + Symgrow(Ctxt, s, int64(r)+int64(n)) + copy(s.P[r:], str) + s.P[int(r)+len(str)] = 0 + s.Size += int64(n) + 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) + sp.Type = obj.SINITARR + sp.Size = 0 + sp.Dupok = 1 + Addaddr(Ctxt, sp, s) +} + +func dosymtype() { + for s := Ctxt.Allsym; s != nil; s = s.Allsym { + if len(s.P) > 0 { + if s.Type == obj.SBSS { + s.Type = obj.SDATA + } + if s.Type == obj.SNOPTRBSS { + s.Type = obj.SNOPTRDATA + } + } + // Create a new entry in the .init_array section that points to the + // library initializer function. + switch Buildmode { + case BuildmodeCArchive, BuildmodeCShared: + if s.Name == INITENTRY { + addinitarrdata(s) + } + } + } +} + +func symalign(s *LSym) int32 { + if s.Align != 0 { + return s.Align + } + + align := int32(Thearch.Maxalign) + for int64(align) > s.Size && align > 1 { + align >>= 1 + } + if align < s.Align { + align = s.Align + } + return align +} + +func aligndatsize(datsize int64, s *LSym) int64 { + return Rnd(datsize, int64(symalign(s))) +} + +// maxalign returns the maximum required alignment for +// the list of symbols s; the list stops when s->type exceeds type. +func maxalign(s *LSym, type_ int) int32 { + var align int32 + + max := int32(0) + for ; s != nil && int(s.Type) <= type_; s = s.Next { + align = symalign(s) + if max < align { + max = align + } + } + + return max +} + +const debugGCProg = false + +type GCProg struct { + sym *LSym + w gcprog.Writer +} + +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) + } +} + +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") + } +} + +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 + } + + 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) { + datsize := *datsizep + if s.Size < 0 { + Diag("negative size (datsize = %d, s->size = %d)", datsize, s.Size) + } + if datsize+s.Size < datsize { + Diag("symbol too large (datsize = %d, s->size = %d)", datsize, s.Size) + } + *datsizep = datsize + s.Size +} + +func dodata() { + if Debug['v'] != 0 { + fmt.Fprintf(&Bso, "%5.2f dodata\n", obj.Cputime()) + } + Bso.Flush() + + var last *LSym + datap = nil + + for s := Ctxt.Allsym; s != nil; s = s.Allsym { + if !s.Reachable || s.Special != 0 { + continue + } + if obj.STEXT < s.Type && s.Type < obj.SXREF { + if s.Onlist != 0 { + log.Fatalf("symbol %s listed multiple times", s.Name) + } + s.Onlist = 1 + if last == nil { + datap = s + } else { + last.Next = s + } + s.Next = nil + last = s + } + } + + for s := datap; s != nil; s = s.Next { + if int64(len(s.P)) > s.Size { + Diag("%s: initialize bounds (%d < %d)", s.Name, int64(s.Size), len(s.P)) + } + } + + /* + * now that we have the datap list, but before we start + * to assign addresses, record all the necessary + * dynamic relocations. these will grow the relocation + * symbol, which is itself data. + * + * on darwin, we need the symbol table numbers for dynreloc. + */ + if HEADTYPE == obj.Hdarwin { + machosymorder() + } + dynreloc() + + /* some symbols may no longer belong in datap (Mach-O) */ + var l **LSym + var s *LSym + for l = &datap; ; { + s = *l + if s == nil { + break + } + + if s.Type <= obj.STEXT || obj.SXREF <= s.Type { + *l = s.Next + } else { + l = &s.Next + } + } + + *l = nil + + datap = listsort(datap, datcmp, listnextp) + + if Iself { + // Make .rela and .rela.plt contiguous, the ELF ABI requires this + // and Solaris actually cares. + var relplt *LSym + for l = &datap; *l != nil; l = &(*l).Next { + if (*l).Name == ".rel.plt" || (*l).Name == ".rela.plt" { + relplt = (*l) + *l = (*l).Next + break + } + } + if relplt != nil { + for s = datap; s != nil; s = s.Next { + if s.Name == ".rel" || s.Name == ".rela" { + relplt.Next = s.Next + s.Next = relplt + } + } + } + } + + /* + * allocate sections. list is sorted by type, + * so we can just walk it for each piece we want to emit. + * segdata is processed before segtext, because we need + * to see all symbols in the .data and .bss sections in order + * to generate garbage collection information. + */ + + /* begin segdata */ + + /* skip symbols belonging to segtext */ + s = datap + + for ; s != nil && s.Type < obj.SELFSECT; s = s.Next { + } + + /* writable ELF sections */ + datsize := int64(0) + + var sect *Section + for ; s != nil && s.Type < obj.SELFGOT; s = s.Next { + sect = addsection(&Segdata, s.Name, 06) + sect.Align = symalign(s) + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + s.Sect = sect + s.Type = obj.SDATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + growdatsize(&datsize, s) + sect.Length = uint64(datsize) - sect.Vaddr + } + + /* .got (and .toc on ppc64) */ + if s.Type == obj.SELFGOT { + sect := addsection(&Segdata, ".got", 06) + sect.Align = maxalign(s, obj.SELFGOT) + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + var toc *LSym + for ; s != nil && s.Type == obj.SELFGOT; s = s.Next { + datsize = aligndatsize(datsize, s) + s.Sect = sect + s.Type = obj.SDATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + + // Resolve .TOC. symbol for this object file (ppc64) + toc = Linkrlookup(Ctxt, ".TOC.", int(s.Version)) + + if toc != nil { + toc.Sect = sect + toc.Outer = s + toc.Sub = s.Sub + s.Sub = toc + + toc.Value = 0x8000 + } + + growdatsize(&datsize, s) + } + + sect.Length = uint64(datsize) - sect.Vaddr + } + + /* pointer-free data */ + sect = addsection(&Segdata, ".noptrdata", 06) + + sect.Align = maxalign(s, obj.SINITARR-1) + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + Linklookup(Ctxt, "runtime.noptrdata", 0).Sect = sect + Linklookup(Ctxt, "runtime.enoptrdata", 0).Sect = sect + for ; s != nil && s.Type < obj.SINITARR; s = s.Next { + datsize = aligndatsize(datsize, s) + s.Sect = sect + s.Type = obj.SDATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + growdatsize(&datsize, s) + } + + sect.Length = uint64(datsize) - sect.Vaddr + + hasinitarr := Linkshared + + /* shared library initializer */ + switch Buildmode { + case BuildmodeCArchive, BuildmodeCShared, BuildmodeShared: + hasinitarr = true + } + + if hasinitarr { + sect := addsection(&Segdata, ".init_array", 06) + sect.Align = maxalign(s, obj.SINITARR) + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + for ; s != nil && s.Type == obj.SINITARR; s = s.Next { + datsize = aligndatsize(datsize, s) + s.Sect = sect + s.Value = int64(uint64(datsize) - sect.Vaddr) + growdatsize(&datsize, s) + } + + sect.Length = uint64(datsize) - sect.Vaddr + } + + /* 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 + 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 + Diag("unexpected symbol type %d", s.Type) + } + + s.Sect = sect + s.Type = obj.SDATA + datsize = aligndatsize(datsize, s) + s.Value = int64(uint64(datsize) - sect.Vaddr) + gc.AddSym(s) + growdatsize(&datsize, s) + } + sect.Length = uint64(datsize) - sect.Vaddr + 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 + 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) + gc.AddSym(s) + growdatsize(&datsize, s) + } + sect.Length = uint64(datsize) - sect.Vaddr + gc.End(int64(sect.Length)) + + /* pointer-free bss */ + sect = addsection(&Segdata, ".noptrbss", 06) + + sect.Align = maxalign(s, obj.SNOPTRBSS) + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + Linklookup(Ctxt, "runtime.noptrbss", 0).Sect = sect + Linklookup(Ctxt, "runtime.enoptrbss", 0).Sect = sect + for ; s != nil && s.Type == obj.SNOPTRBSS; s = s.Next { + datsize = aligndatsize(datsize, s) + s.Sect = sect + s.Value = int64(uint64(datsize) - sect.Vaddr) + growdatsize(&datsize, s) + } + + sect.Length = uint64(datsize) - sect.Vaddr + Linklookup(Ctxt, "runtime.end", 0).Sect = sect + + // 6g uses 4-byte relocation offsets, so the entire segment must fit in 32 bits. + if datsize != int64(uint32(datsize)) { + Diag("data or bss segment too large") + } + + if Iself && Linkmode == LinkExternal && s != nil && s.Type == obj.STLSBSS && HEADTYPE != obj.Hopenbsd { + sect := addsection(&Segdata, ".tbss", 06) + sect.Align = int32(Thearch.Ptrsize) + sect.Vaddr = 0 + datsize = 0 + for ; s != nil && s.Type == obj.STLSBSS; s = s.Next { + datsize = aligndatsize(datsize, s) + s.Sect = sect + s.Value = int64(uint64(datsize) - sect.Vaddr) + growdatsize(&datsize, s) + } + + sect.Length = uint64(datsize) + } else { + // Might be internal linking but still using cgo. + // In that case, the only possible STLSBSS symbol is runtime.tlsg. + // Give it offset 0, because it's the only thing here. + if s != nil && s.Type == obj.STLSBSS && s.Name == "runtime.tlsg" { + s.Value = 0 + s = s.Next + } + } + + if s != nil { + Ctxt.Cursym = nil + Diag("unexpected symbol type %d for %s", s.Type, s.Name) + } + + /* + * We finished data, begin read-only data. + * Not all systems support a separate read-only non-executable data section. + * ELF systems do. + * OS X and Plan 9 do not. + * Windows PE may, but if so we have not implemented it. + * And if we're using external linking mode, the point is moot, + * since it's not our decision; that code expects the sections in + * segtext. + */ + var segro *Segment + if Iself && Linkmode == LinkInternal { + segro = &Segrodata + } else { + segro = &Segtext + } + + s = datap + + datsize = 0 + + /* read-only executable ELF, Mach-O sections */ + for ; s != nil && s.Type < obj.STYPE; s = s.Next { + sect = addsection(&Segtext, s.Name, 04) + sect.Align = symalign(s) + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + s.Sect = sect + s.Type = obj.SRODATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + growdatsize(&datsize, s) + sect.Length = uint64(datsize) - sect.Vaddr + } + + /* read-only data */ + sect = addsection(segro, ".rodata", 04) + + sect.Align = maxalign(s, obj.STYPELINK-1) + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = 0 + Linklookup(Ctxt, "runtime.rodata", 0).Sect = sect + Linklookup(Ctxt, "runtime.erodata", 0).Sect = sect + for ; s != nil && s.Type < obj.STYPELINK; s = s.Next { + datsize = aligndatsize(datsize, s) + s.Sect = sect + s.Type = obj.SRODATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + growdatsize(&datsize, s) + } + + sect.Length = uint64(datsize) - sect.Vaddr + + /* typelink */ + sect = addsection(segro, ".typelink", 04) + + sect.Align = maxalign(s, obj.STYPELINK) + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + Linklookup(Ctxt, "runtime.typelink", 0).Sect = sect + Linklookup(Ctxt, "runtime.etypelink", 0).Sect = sect + for ; s != nil && s.Type == obj.STYPELINK; s = s.Next { + datsize = aligndatsize(datsize, s) + s.Sect = sect + s.Type = obj.SRODATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + growdatsize(&datsize, s) + } + + sect.Length = uint64(datsize) - sect.Vaddr + + /* gosymtab */ + sect = addsection(segro, ".gosymtab", 04) + + sect.Align = maxalign(s, obj.SPCLNTAB-1) + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + Linklookup(Ctxt, "runtime.symtab", 0).Sect = sect + Linklookup(Ctxt, "runtime.esymtab", 0).Sect = sect + for ; s != nil && s.Type < obj.SPCLNTAB; s = s.Next { + datsize = aligndatsize(datsize, s) + s.Sect = sect + s.Type = obj.SRODATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + growdatsize(&datsize, s) + } + + sect.Length = uint64(datsize) - sect.Vaddr + + /* gopclntab */ + sect = addsection(segro, ".gopclntab", 04) + + sect.Align = maxalign(s, obj.SELFROSECT-1) + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + Linklookup(Ctxt, "runtime.pclntab", 0).Sect = sect + Linklookup(Ctxt, "runtime.epclntab", 0).Sect = sect + for ; s != nil && s.Type < obj.SELFROSECT; s = s.Next { + datsize = aligndatsize(datsize, s) + s.Sect = sect + s.Type = obj.SRODATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + growdatsize(&datsize, s) + } + + sect.Length = uint64(datsize) - sect.Vaddr + + /* read-only ELF, Mach-O sections */ + for ; s != nil && s.Type < obj.SELFSECT; s = s.Next { + sect = addsection(segro, s.Name, 04) + sect.Align = symalign(s) + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + s.Sect = sect + s.Type = obj.SRODATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + growdatsize(&datsize, s) + sect.Length = uint64(datsize) - sect.Vaddr + } + + // 6g uses 4-byte relocation offsets, so the entire segment must fit in 32 bits. + if datsize != int64(uint32(datsize)) { + Diag("read-only data segment too large") + } + + /* number the sections */ + n := int32(1) + + for sect := Segtext.Sect; sect != nil; sect = sect.Next { + sect.Extnum = int16(n) + n++ + } + for sect := Segrodata.Sect; sect != nil; sect = sect.Next { + sect.Extnum = int16(n) + n++ + } + for sect := Segdata.Sect; sect != nil; sect = sect.Next { + sect.Extnum = int16(n) + n++ + } +} + +// assign addresses to text +func textaddress() { + var sub *LSym + + addsection(&Segtext, ".text", 05) + + // Assign PCs in text segment. + // Could parallelize, by assigning to text + // and then letting threads copy down, but probably not worth it. + sect := Segtext.Sect + + sect.Align = int32(Funcalign) + Linklookup(Ctxt, "runtime.text", 0).Sect = sect + Linklookup(Ctxt, "runtime.etext", 0).Sect = sect + va := uint64(INITTEXT) + sect.Vaddr = va + for sym := Ctxt.Textp; sym != nil; sym = sym.Next { + sym.Sect = sect + if sym.Type&obj.SSUB != 0 { + continue + } + if sym.Align != 0 { + va = uint64(Rnd(int64(va), int64(sym.Align))) + } else { + va = uint64(Rnd(int64(va), int64(Funcalign))) + } + sym.Value = 0 + for sub = sym; sub != nil; sub = sub.Sub { + sub.Value += int64(va) + } + if sym.Size == 0 && sym.Sub != nil { + Ctxt.Cursym = sym + } + if sym.Size < MINFUNC { + va += MINFUNC // spacing required for findfunctab + } else { + va += uint64(sym.Size) + } + } + + sect.Length = va - sect.Vaddr +} + +// assign addresses +func address() { + va := uint64(INITTEXT) + Segtext.Rwx = 05 + Segtext.Vaddr = va + Segtext.Fileoff = uint64(HEADR) + for s := Segtext.Sect; s != nil; s = s.Next { + va = uint64(Rnd(int64(va), int64(s.Align))) + s.Vaddr = va + va += s.Length + } + + Segtext.Length = va - uint64(INITTEXT) + Segtext.Filelen = Segtext.Length + if HEADTYPE == obj.Hnacl { + va += 32 // room for the "halt sled" + } + + if Segrodata.Sect != nil { + // align to page boundary so as not to mix + // rodata and executable text. + va = uint64(Rnd(int64(va), int64(INITRND))) + + Segrodata.Rwx = 04 + Segrodata.Vaddr = va + Segrodata.Fileoff = va - Segtext.Vaddr + Segtext.Fileoff + Segrodata.Filelen = 0 + for s := Segrodata.Sect; s != nil; s = s.Next { + va = uint64(Rnd(int64(va), int64(s.Align))) + s.Vaddr = va + va += s.Length + } + + Segrodata.Length = va - Segrodata.Vaddr + Segrodata.Filelen = Segrodata.Length + } + + va = uint64(Rnd(int64(va), int64(INITRND))) + Segdata.Rwx = 06 + Segdata.Vaddr = va + Segdata.Fileoff = va - Segtext.Vaddr + Segtext.Fileoff + Segdata.Filelen = 0 + if HEADTYPE == obj.Hwindows { + Segdata.Fileoff = Segtext.Fileoff + uint64(Rnd(int64(Segtext.Length), PEFILEALIGN)) + } + if HEADTYPE == obj.Hplan9 { + Segdata.Fileoff = Segtext.Fileoff + Segtext.Filelen + } + var data *Section + var noptr *Section + var bss *Section + var noptrbss *Section + var vlen int64 + for s := Segdata.Sect; s != nil; s = s.Next { + vlen = int64(s.Length) + if s.Next != nil { + vlen = int64(s.Next.Vaddr - s.Vaddr) + } + s.Vaddr = va + va += uint64(vlen) + Segdata.Length = va - Segdata.Vaddr + if s.Name == ".data" { + data = s + } + if s.Name == ".noptrdata" { + noptr = s + } + if s.Name == ".bss" { + bss = s + } + if s.Name == ".noptrbss" { + noptrbss = s + } + } + + Segdata.Filelen = bss.Vaddr - Segdata.Vaddr + + text := Segtext.Sect + var rodata *Section + if Segrodata.Sect != nil { + rodata = Segrodata.Sect + } else { + rodata = text.Next + } + typelink := rodata.Next + symtab := typelink.Next + pclntab := symtab.Next + + var sub *LSym + for sym := datap; sym != nil; sym = sym.Next { + Ctxt.Cursym = sym + if sym.Sect != nil { + sym.Value += int64((sym.Sect.(*Section)).Vaddr) + } + for sub = sym.Sub; sub != nil; sub = sub.Sub { + sub.Value += sym.Value + } + } + + 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)) + xdefine("runtime.erodata", obj.SRODATA, int64(rodata.Vaddr+rodata.Length)) + xdefine("runtime.typelink", obj.SRODATA, int64(typelink.Vaddr)) + xdefine("runtime.etypelink", obj.SRODATA, int64(typelink.Vaddr+typelink.Length)) + + sym := Linklookup(Ctxt, "runtime.gcdata", 0) + sym.Local = true + xdefine("runtime.egcdata", obj.SRODATA, Symaddr(sym)+sym.Size) + Linklookup(Ctxt, "runtime.egcdata", 0).Sect = sym.Sect + + sym = Linklookup(Ctxt, "runtime.gcbss", 0) + sym.Local = true + xdefine("runtime.egcbss", obj.SRODATA, Symaddr(sym)+sym.Size) + Linklookup(Ctxt, "runtime.egcbss", 0).Sect = sym.Sect + + xdefine("runtime.symtab", obj.SRODATA, int64(symtab.Vaddr)) + xdefine("runtime.esymtab", obj.SRODATA, int64(symtab.Vaddr+symtab.Length)) + xdefine("runtime.pclntab", obj.SRODATA, int64(pclntab.Vaddr)) + xdefine("runtime.epclntab", obj.SRODATA, int64(pclntab.Vaddr+pclntab.Length)) + xdefine("runtime.noptrdata", obj.SNOPTRDATA, int64(noptr.Vaddr)) + xdefine("runtime.enoptrdata", obj.SNOPTRDATA, int64(noptr.Vaddr+noptr.Length)) + xdefine("runtime.bss", obj.SBSS, int64(bss.Vaddr)) + xdefine("runtime.ebss", obj.SBSS, int64(bss.Vaddr+bss.Length)) + xdefine("runtime.data", obj.SDATA, int64(data.Vaddr)) + xdefine("runtime.edata", obj.SDATA, int64(data.Vaddr+data.Length)) + xdefine("runtime.noptrbss", obj.SNOPTRBSS, int64(noptrbss.Vaddr)) + xdefine("runtime.enoptrbss", obj.SNOPTRBSS, int64(noptrbss.Vaddr+noptrbss.Length)) + xdefine("runtime.end", obj.SBSS, int64(Segdata.Vaddr+Segdata.Length)) +} diff --git a/src/cmd/link/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go new file mode 100644 index 0000000000..fcc664dde7 --- /dev/null +++ b/src/cmd/link/internal/ld/decodesym.go @@ -0,0 +1,192 @@ +// Copyright 2012 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 "cmd/internal/obj" + +// Decoding the type.* symbols. This has to be in sync with +// ../../runtime/type.go, or more specifically, with what +// ../gc/reflect.c stuffs in these. + +func decode_reloc(s *LSym, off int32) *Reloc { + for i := 0; i < len(s.R); i++ { + if s.R[i].Off == off { + return &s.R[i:][0] + } + } + return nil +} + +func decode_reloc_sym(s *LSym, off int32) *LSym { + r := decode_reloc(s, off) + if r == nil { + return nil + } + return r.Sym +} + +func decode_inuxi(p []byte, sz int) uint64 { + switch sz { + case 2: + return uint64(Ctxt.Arch.ByteOrder.Uint16(p)) + case 4: + return uint64(Ctxt.Arch.ByteOrder.Uint32(p)) + case 8: + return Ctxt.Arch.ByteOrder.Uint64(p) + default: + Exitf("dwarf: decode inuxi %d", sz) + panic("unreachable") + } +} + +// commonsize returns the size of the common prefix for all type +// structures (runtime._type). +func commonsize() int { + return 8*Thearch.Ptrsize + 8 +} + +// Type.commonType.kind +func decodetype_kind(s *LSym) uint8 { + return uint8(s.P[2*Thearch.Ptrsize+7] & obj.KindMask) // 0x13 / 0x1f +} + +// Type.commonType.kind +func decodetype_noptr(s *LSym) uint8 { + return uint8(s.P[2*Thearch.Ptrsize+7] & obj.KindNoPointers) // 0x13 / 0x1f +} + +// Type.commonType.kind +func decodetype_usegcprog(s *LSym) uint8 { + return uint8(s.P[2*Thearch.Ptrsize+7] & obj.KindGCProg) // 0x13 / 0x1f +} + +// Type.commonType.size +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 { + // The gcprog for "type.$name" is calle "type..gcprog.$name". + x := "type..gcprog." + s.Name[5:] + return Linklookup(Ctxt, x, 0) + } + return decode_reloc_sym(s, 2*int32(Thearch.Ptrsize)+8+1*int32(Thearch.Ptrsize)) +} + +func decodetype_gcprog_shlib(s *LSym) uint64 { + return decode_inuxi(s.P[2*int32(Thearch.Ptrsize)+8+1*int32(Thearch.Ptrsize):], Thearch.Ptrsize) +} + +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 + } + mask := decode_reloc_sym(s, 2*int32(Thearch.Ptrsize)+8+1*int32(Thearch.Ptrsize)) + return mask.P +} + +// Type.ArrayType.elem and Type.SliceType.Elem +func decodetype_arrayelem(s *LSym) *LSym { + return decode_reloc_sym(s, int32(commonsize())) // 0x1c / 0x30 +} + +func decodetype_arraylen(s *LSym) int64 { + return int64(decode_inuxi(s.P[commonsize()+2*Thearch.Ptrsize:], Thearch.Ptrsize)) +} + +// Type.PtrType.elem +func decodetype_ptrelem(s *LSym) *LSym { + return decode_reloc_sym(s, int32(commonsize())) // 0x1c / 0x30 +} + +// Type.MapType.key, elem +func decodetype_mapkey(s *LSym) *LSym { + return decode_reloc_sym(s, int32(commonsize())) // 0x1c / 0x30 +} + +func decodetype_mapvalue(s *LSym) *LSym { + return decode_reloc_sym(s, int32(commonsize())+int32(Thearch.Ptrsize)) // 0x20 / 0x38 +} + +// Type.ChanType.elem +func decodetype_chanelem(s *LSym) *LSym { + return decode_reloc_sym(s, int32(commonsize())) // 0x1c / 0x30 +} + +// Type.FuncType.dotdotdot +func decodetype_funcdotdotdot(s *LSym) int { + return int(s.P[commonsize()]) +} + +// Type.FuncType.in.length +func decodetype_funcincount(s *LSym) int { + return int(decode_inuxi(s.P[commonsize()+2*Thearch.Ptrsize:], Thearch.Intsize)) +} + +func decodetype_funcoutcount(s *LSym) int { + return int(decode_inuxi(s.P[commonsize()+3*Thearch.Ptrsize+2*Thearch.Intsize:], Thearch.Intsize)) +} + +func decodetype_funcintype(s *LSym, i int) *LSym { + r := decode_reloc(s, int32(commonsize())+int32(Thearch.Ptrsize)) + if r == nil { + return nil + } + return decode_reloc_sym(r.Sym, int32(r.Add+int64(int32(i)*int32(Thearch.Ptrsize)))) +} + +func decodetype_funcouttype(s *LSym, i int) *LSym { + r := decode_reloc(s, int32(commonsize())+2*int32(Thearch.Ptrsize)+2*int32(Thearch.Intsize)) + if r == nil { + return nil + } + return decode_reloc_sym(r.Sym, int32(r.Add+int64(int32(i)*int32(Thearch.Ptrsize)))) +} + +// Type.StructType.fields.Slice::length +func decodetype_structfieldcount(s *LSym) int { + return int(decode_inuxi(s.P[commonsize()+Thearch.Ptrsize:], Thearch.Intsize)) +} + +func structfieldsize() int { + return 5 * Thearch.Ptrsize +} + +// Type.StructType.fields[]-> name, typ and offset. +func decodetype_structfieldname(s *LSym, i int) string { + // go.string."foo" 0x28 / 0x40 + s = decode_reloc_sym(s, int32(commonsize())+int32(Thearch.Ptrsize)+2*int32(Thearch.Intsize)+int32(i)*int32(structfieldsize())) + + if s == nil { // embedded structs have a nil name. + return "" + } + r := decode_reloc(s, 0) // s has a pointer to the string data at offset 0 + if r == nil { // shouldn't happen. + return "" + } + return cstring(r.Sym.P[r.Add:]) +} + +func decodetype_structfieldtype(s *LSym, i int) *LSym { + return decode_reloc_sym(s, int32(commonsize())+int32(Thearch.Ptrsize)+2*int32(Thearch.Intsize)+int32(i)*int32(structfieldsize())+2*int32(Thearch.Ptrsize)) +} + +func decodetype_structfieldoffs(s *LSym, i int) int64 { + return int64(decode_inuxi(s.P[commonsize()+Thearch.Ptrsize+2*Thearch.Intsize+i*structfieldsize()+4*Thearch.Ptrsize:], Thearch.Intsize)) +} + +// InterfaceType.methods.length +func decodetype_ifacemethodcount(s *LSym) int64 { + return int64(decode_inuxi(s.P[commonsize()+Thearch.Ptrsize:], Thearch.Intsize)) +} diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go new file mode 100644 index 0000000000..b8fb2e6b55 --- /dev/null +++ b/src/cmd/link/internal/ld/dwarf.go @@ -0,0 +1,2621 @@ +// 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. + +// TODO/NICETOHAVE: +// - eliminate DW_CLS_ if not used +// - package info in compilation units +// - assign global variables and types to their packages +// - gdb uses c syntax, meaning clumsy quoting is needed for go identifiers. eg +// ptype struct '[]uint8' and qualifiers need to be quoted away +// - lexical scoping is lost, so gdb gets confused as to which 'main.i' you mean. +// - file:line info for variables +// - make strings a typedef so prettyprinters can see the underlying string type + +package ld + +import ( + "cmd/internal/obj" + "fmt" + "os" + "strings" +) + +/* + * Offsets and sizes of the debug_* sections in the cout file. + */ +var abbrevo int64 + +var abbrevsize int64 + +var abbrevsym *LSym + +var abbrevsympos int64 + +var lineo int64 + +var linesize int64 + +var linesym *LSym + +var linesympos int64 + +var infoo int64 // also the base for DWDie->offs and reference attributes. + +var infosize int64 + +var infosym *LSym + +var infosympos int64 + +var frameo int64 + +var framesize int64 + +var framesym *LSym + +var framesympos int64 + +var pubnameso int64 + +var pubnamessize int64 + +var pubtypeso int64 + +var pubtypessize int64 + +var arangeso int64 + +var arangessize int64 + +var gdbscripto int64 + +var gdbscriptsize int64 + +var infosec *LSym + +var inforeloco int64 + +var inforelocsize int64 + +var arangessec *LSym + +var arangesreloco int64 + +var arangesrelocsize int64 + +var linesec *LSym + +var linereloco int64 + +var linerelocsize int64 + +var framesec *LSym + +var framereloco int64 + +var framerelocsize int64 + +var gdbscript string + +/* + * Basic I/O + */ +func addrput(addr int64) { + switch Thearch.Ptrsize { + case 4: + Thearch.Lput(uint32(addr)) + + case 8: + Thearch.Vput(uint64(addr)) + } +} + +func uleb128enc(v uint64, dst []byte) int { + var c uint8 + + length := uint8(0) + for { + c = uint8(v & 0x7f) + v >>= 7 + if v != 0 { + c |= 0x80 + } + if dst != nil { + dst[0] = byte(c) + dst = dst[1:] + } + length++ + if c&0x80 == 0 { + break + } + } + + return int(length) +} + +func sleb128enc(v int64, dst []byte) int { + var c uint8 + var s uint8 + + length := uint8(0) + for { + c = uint8(v & 0x7f) + s = uint8(v & 0x40) + v >>= 7 + if (v != -1 || s == 0) && (v != 0 || s != 0) { + c |= 0x80 + } + if dst != nil { + dst[0] = byte(c) + dst = dst[1:] + } + length++ + if c&0x80 == 0 { + break + } + } + + return int(length) +} + +var encbuf [10]byte + +func uleb128put(v int64) { + n := uleb128enc(uint64(v), encbuf[:]) + Cwrite(encbuf[:n]) +} + +func sleb128put(v int64) { + n := sleb128enc(v, encbuf[:]) + Cwrite(encbuf[:n]) +} + +/* + * Defining Abbrevs. This is hardcoded, and there will be + * only a handful of them. The DWARF spec places no restriction on + * the ordering of attributes in the Abbrevs and DIEs, and we will + * always write them out in the order of declaration in the abbrev. + */ +type DWAttrForm struct { + attr uint16 + form uint8 +} + +// Go-specific type attributes. +const ( + DW_AT_go_kind = 0x2900 + DW_AT_go_key = 0x2901 + DW_AT_go_elem = 0x2902 + + DW_AT_internal_location = 253 // params and locals; not emitted +) + +// Index into the abbrevs table below. +// Keep in sync with ispubname() and ispubtype() below. +// ispubtype considers >= NULLTYPE public +const ( + DW_ABRV_NULL = iota + DW_ABRV_COMPUNIT + DW_ABRV_FUNCTION + DW_ABRV_VARIABLE + DW_ABRV_AUTO + DW_ABRV_PARAM + DW_ABRV_STRUCTFIELD + DW_ABRV_FUNCTYPEPARAM + DW_ABRV_DOTDOTDOT + DW_ABRV_ARRAYRANGE + DW_ABRV_NULLTYPE + DW_ABRV_BASETYPE + DW_ABRV_ARRAYTYPE + DW_ABRV_CHANTYPE + DW_ABRV_FUNCTYPE + DW_ABRV_IFACETYPE + DW_ABRV_MAPTYPE + DW_ABRV_PTRTYPE + DW_ABRV_BARE_PTRTYPE // only for void*, no DW_AT_type attr to please gdb 6. + DW_ABRV_SLICETYPE + DW_ABRV_STRINGTYPE + DW_ABRV_STRUCTTYPE + DW_ABRV_TYPEDECL + DW_NABRV +) + +type DWAbbrev struct { + tag uint8 + children uint8 + attr []DWAttrForm +} + +var abbrevs = [DW_NABRV]DWAbbrev{ + /* The mandatory DW_ABRV_NULL entry. */ + {0, 0, []DWAttrForm{}}, + + /* COMPUNIT */ + { + DW_TAG_compile_unit, + DW_CHILDREN_yes, + []DWAttrForm{ + {DW_AT_name, DW_FORM_string}, + {DW_AT_language, DW_FORM_data1}, + {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}, + }, + }, + + /* FUNCTION */ + { + DW_TAG_subprogram, + DW_CHILDREN_yes, + []DWAttrForm{ + {DW_AT_name, DW_FORM_string}, + {DW_AT_low_pc, DW_FORM_addr}, + {DW_AT_high_pc, DW_FORM_addr}, + {DW_AT_external, DW_FORM_flag}, + }, + }, + + /* VARIABLE */ + { + DW_TAG_variable, + DW_CHILDREN_no, + []DWAttrForm{ + {DW_AT_name, DW_FORM_string}, + {DW_AT_location, DW_FORM_block1}, + {DW_AT_type, DW_FORM_ref_addr}, + {DW_AT_external, DW_FORM_flag}, + }, + }, + + /* AUTO */ + { + DW_TAG_variable, + DW_CHILDREN_no, + []DWAttrForm{ + {DW_AT_name, DW_FORM_string}, + {DW_AT_location, DW_FORM_block1}, + {DW_AT_type, DW_FORM_ref_addr}, + }, + }, + + /* PARAM */ + { + DW_TAG_formal_parameter, + DW_CHILDREN_no, + []DWAttrForm{ + {DW_AT_name, DW_FORM_string}, + {DW_AT_location, DW_FORM_block1}, + {DW_AT_type, DW_FORM_ref_addr}, + }, + }, + + /* STRUCTFIELD */ + { + DW_TAG_member, + DW_CHILDREN_no, + []DWAttrForm{ + {DW_AT_name, DW_FORM_string}, + {DW_AT_data_member_location, DW_FORM_block1}, + {DW_AT_type, DW_FORM_ref_addr}, + }, + }, + + /* FUNCTYPEPARAM */ + { + DW_TAG_formal_parameter, + DW_CHILDREN_no, + + // No name! + []DWAttrForm{ + {DW_AT_type, DW_FORM_ref_addr}, + }, + }, + + /* DOTDOTDOT */ + { + DW_TAG_unspecified_parameters, + DW_CHILDREN_no, + []DWAttrForm{}, + }, + + /* ARRAYRANGE */ + { + DW_TAG_subrange_type, + DW_CHILDREN_no, + + // No name! + []DWAttrForm{ + {DW_AT_type, DW_FORM_ref_addr}, + {DW_AT_count, DW_FORM_udata}, + }, + }, + + // Below here are the types considered public by ispubtype + /* NULLTYPE */ + { + DW_TAG_unspecified_type, + DW_CHILDREN_no, + []DWAttrForm{ + {DW_AT_name, DW_FORM_string}, + }, + }, + + /* BASETYPE */ + { + DW_TAG_base_type, + DW_CHILDREN_no, + []DWAttrForm{ + {DW_AT_name, DW_FORM_string}, + {DW_AT_encoding, DW_FORM_data1}, + {DW_AT_byte_size, DW_FORM_data1}, + {DW_AT_go_kind, DW_FORM_data1}, + }, + }, + + /* ARRAYTYPE */ + // child is subrange with upper bound + { + DW_TAG_array_type, + DW_CHILDREN_yes, + []DWAttrForm{ + {DW_AT_name, DW_FORM_string}, + {DW_AT_type, DW_FORM_ref_addr}, + {DW_AT_byte_size, DW_FORM_udata}, + {DW_AT_go_kind, DW_FORM_data1}, + }, + }, + + /* CHANTYPE */ + { + DW_TAG_typedef, + DW_CHILDREN_no, + []DWAttrForm{ + {DW_AT_name, DW_FORM_string}, + {DW_AT_type, DW_FORM_ref_addr}, + {DW_AT_go_kind, DW_FORM_data1}, + {DW_AT_go_elem, DW_FORM_ref_addr}, + }, + }, + + /* FUNCTYPE */ + { + DW_TAG_subroutine_type, + DW_CHILDREN_yes, + []DWAttrForm{ + {DW_AT_name, DW_FORM_string}, + // {DW_AT_type, DW_FORM_ref_addr}, + {DW_AT_go_kind, DW_FORM_data1}, + }, + }, + + /* IFACETYPE */ + { + DW_TAG_typedef, + DW_CHILDREN_yes, + []DWAttrForm{ + {DW_AT_name, DW_FORM_string}, + {DW_AT_type, DW_FORM_ref_addr}, + {DW_AT_go_kind, DW_FORM_data1}, + }, + }, + + /* MAPTYPE */ + { + DW_TAG_typedef, + DW_CHILDREN_no, + []DWAttrForm{ + {DW_AT_name, DW_FORM_string}, + {DW_AT_type, DW_FORM_ref_addr}, + {DW_AT_go_kind, DW_FORM_data1}, + {DW_AT_go_key, DW_FORM_ref_addr}, + {DW_AT_go_elem, DW_FORM_ref_addr}, + }, + }, + + /* PTRTYPE */ + { + DW_TAG_pointer_type, + DW_CHILDREN_no, + []DWAttrForm{ + {DW_AT_name, DW_FORM_string}, + {DW_AT_type, DW_FORM_ref_addr}, + {DW_AT_go_kind, DW_FORM_data1}, + }, + }, + + /* BARE_PTRTYPE */ + { + DW_TAG_pointer_type, + DW_CHILDREN_no, + []DWAttrForm{ + {DW_AT_name, DW_FORM_string}, + }, + }, + + /* SLICETYPE */ + { + DW_TAG_structure_type, + DW_CHILDREN_yes, + []DWAttrForm{ + {DW_AT_name, DW_FORM_string}, + {DW_AT_byte_size, DW_FORM_udata}, + {DW_AT_go_kind, DW_FORM_data1}, + {DW_AT_go_elem, DW_FORM_ref_addr}, + }, + }, + + /* STRINGTYPE */ + { + DW_TAG_structure_type, + DW_CHILDREN_yes, + []DWAttrForm{ + {DW_AT_name, DW_FORM_string}, + {DW_AT_byte_size, DW_FORM_udata}, + {DW_AT_go_kind, DW_FORM_data1}, + }, + }, + + /* STRUCTTYPE */ + { + DW_TAG_structure_type, + DW_CHILDREN_yes, + []DWAttrForm{ + {DW_AT_name, DW_FORM_string}, + {DW_AT_byte_size, DW_FORM_udata}, + {DW_AT_go_kind, DW_FORM_data1}, + }, + }, + + /* TYPEDECL */ + { + DW_TAG_typedef, + DW_CHILDREN_no, + []DWAttrForm{ + {DW_AT_name, DW_FORM_string}, + {DW_AT_type, DW_FORM_ref_addr}, + }, + }, +} + +func writeabbrev() { + abbrevo = Cpos() + for i := 1; i < DW_NABRV; i++ { + // See section 7.5.3 + uleb128put(int64(i)) + + uleb128put(int64(abbrevs[i].tag)) + Cput(abbrevs[i].children) + for _, f := range abbrevs[i].attr { + uleb128put(int64(f.attr)) + uleb128put(int64(f.form)) + } + uleb128put(0) + uleb128put(0) + } + + Cput(0) + abbrevsize = Cpos() - abbrevo +} + +/* + * Debugging Information Entries and their attributes. + */ +const ( + HASHSIZE = 107 +) + +func dwarfhashstr(s string) uint32 { + h := uint32(0) + for s != "" { + h = h + h + h + uint32(s[0]) + s = s[1:] + } + return h % HASHSIZE +} + +// For DW_CLS_string and _block, value should contain the length, and +// data the data, for _reference, value is 0 and data is a DWDie* to +// the referenced instance, for all others, value is the whole thing +// and data is null. + +type DWAttr struct { + link *DWAttr + atr uint16 // DW_AT_ + cls uint8 // DW_CLS_ + value int64 + data interface{} +} + +type DWDie struct { + abbrev int + link *DWDie + child *DWDie + attr *DWAttr + // offset into .debug_info section, i.e relative to + // infoo. only valid after call to putdie() + offs int64 + hash []*DWDie // optional index of children by name, enabled by mkindex() + hlink *DWDie // bucket chain in parent's index +} + +/* + * Root DIEs for compilation units, types and global variables. + */ +var dwroot DWDie + +var dwtypes DWDie + +var dwglobals DWDie + +func newattr(die *DWDie, attr uint16, cls int, value int64, data interface{}) *DWAttr { + a := new(DWAttr) + a.link = die.attr + die.attr = a + a.atr = attr + a.cls = uint8(cls) + a.value = value + a.data = data + return a +} + +// Each DIE (except the root ones) has at least 1 attribute: its +// name. getattr moves the desired one to the front so +// frequently searched ones are found faster. +func getattr(die *DWDie, attr uint16) *DWAttr { + if die.attr.atr == attr { + return die.attr + } + + a := die.attr + b := a.link + for b != nil { + if b.atr == attr { + a.link = b.link + b.link = die.attr + die.attr = b + return b + } + + a = b + b = b.link + } + + return nil +} + +// Every DIE has at least a DW_AT_name attribute (but it will only be +// written out if it is listed in the abbrev). If its parent is +// keeping an index, the new DIE will be inserted there. +func newdie(parent *DWDie, abbrev int, name string) *DWDie { + die := new(DWDie) + die.abbrev = abbrev + die.link = parent.child + parent.child = die + + newattr(die, DW_AT_name, DW_CLS_STRING, int64(len(name)), name) + + if parent.hash != nil { + h := int(dwarfhashstr(name)) + die.hlink = parent.hash[h] + parent.hash[h] = die + } + + return die +} + +func mkindex(die *DWDie) { + die.hash = make([]*DWDie, HASHSIZE) +} + +func walktypedef(die *DWDie) *DWDie { + // Resolve typedef if present. + if die.abbrev == DW_ABRV_TYPEDECL { + for attr := die.attr; attr != nil; attr = attr.link { + if attr.atr == DW_AT_type && attr.cls == DW_CLS_REFERENCE && attr.data != nil { + return attr.data.(*DWDie) + } + } + } + + return die +} + +// Find child by AT_name using hashtable if available or linear scan +// if not. +func find(die *DWDie, name string) *DWDie { + var a *DWDie + var b *DWDie + var die2 *DWDie + var h int + +top: + if die.hash == nil { + for a = die.child; a != nil; a = a.link { + if name == getattr(a, DW_AT_name).data { + return a + } + } + goto notfound + } + + h = int(dwarfhashstr(name)) + a = die.hash[h] + + if a == nil { + goto notfound + } + + if name == getattr(a, DW_AT_name).data { + return a + } + + // Move found ones to head of the list. + b = a.hlink + + for b != nil { + if name == getattr(b, DW_AT_name).data { + a.hlink = b.hlink + b.hlink = die.hash[h] + die.hash[h] = b + return b + } + + a = b + b = b.hlink + } + +notfound: + die2 = walktypedef(die) + if die2 != die { + die = die2 + goto top + } + + return nil +} + +func mustFind(die *DWDie, name string) *DWDie { + r := find(die, name) + if r == nil { + Exitf("dwarf find: %s %p has no %s", getattr(die, DW_AT_name).data, die, name) + } + return r +} + +func adddwarfrel(sec *LSym, sym *LSym, offsetbase int64, siz int, addend int64) { + r := Addrel(sec) + r.Sym = sym + r.Xsym = sym + r.Off = int32(Cpos() - offsetbase) + r.Siz = uint8(siz) + r.Type = obj.R_ADDR + r.Add = addend + r.Xadd = addend + if Iself && Thearch.Thechar == '6' { + addend = 0 + } + if HEADTYPE == obj.Hdarwin { + addend += sym.Value + } + switch siz { + case 4: + Thearch.Lput(uint32(addend)) + + case 8: + Thearch.Vput(uint64(addend)) + + default: + Diag("bad size in adddwarfrel") + } +} + +func newrefattr(die *DWDie, attr uint16, ref *DWDie) *DWAttr { + if ref == nil { + return nil + } + return newattr(die, attr, DW_CLS_REFERENCE, 0, ref) +} + +var fwdcount int + +func putattr(abbrev int, form int, cls int, value int64, data interface{}) { + switch form { + case DW_FORM_addr: // address + if Linkmode == LinkExternal { + value -= (data.(*LSym)).Value + adddwarfrel(infosec, data.(*LSym), infoo, Thearch.Ptrsize, value) + break + } + + addrput(value) + + case DW_FORM_block1: // block + if cls == DW_CLS_ADDRESS { + Cput(uint8(1 + Thearch.Ptrsize)) + Cput(DW_OP_addr) + if Linkmode == LinkExternal { + value -= (data.(*LSym)).Value + adddwarfrel(infosec, data.(*LSym), infoo, Thearch.Ptrsize, value) + break + } + + addrput(value) + break + } + + value &= 0xff + Cput(uint8(value)) + p := data.([]byte) + for i := 0; int64(i) < value; i++ { + Cput(uint8(p[i])) + } + + case DW_FORM_block2: // block + value &= 0xffff + + Thearch.Wput(uint16(value)) + p := data.([]byte) + for i := 0; int64(i) < value; i++ { + Cput(uint8(p[i])) + } + + case DW_FORM_block4: // block + value &= 0xffffffff + + Thearch.Lput(uint32(value)) + p := data.([]byte) + for i := 0; int64(i) < value; i++ { + Cput(uint8(p[i])) + } + + case DW_FORM_block: // block + uleb128put(value) + + p := data.([]byte) + for i := 0; int64(i) < value; i++ { + Cput(uint8(p[i])) + } + + case DW_FORM_data1: // constant + Cput(uint8(value)) + + case DW_FORM_data2: // constant + Thearch.Wput(uint16(value)) + + case DW_FORM_data4: // constant, {line,loclist,mac,rangelist}ptr + if Linkmode == LinkExternal && cls == DW_CLS_PTR { + adddwarfrel(infosec, linesym, infoo, 4, value) + break + } + + Thearch.Lput(uint32(value)) + + case DW_FORM_data8: // constant, {line,loclist,mac,rangelist}ptr + Thearch.Vput(uint64(value)) + + case DW_FORM_sdata: // constant + sleb128put(value) + + case DW_FORM_udata: // constant + uleb128put(value) + + case DW_FORM_string: // string + strnput(data.(string), int(value+1)) + + case DW_FORM_flag: // flag + if value != 0 { + Cput(1) + } else { + Cput(0) + } + + // In DWARF 2 (which is what we claim to generate), + // the ref_addr is the same size as a normal address. + // In DWARF 3 it is always 32 bits, unless emitting a large + // (> 4 GB of debug info aka "64-bit") unit, which we don't implement. + case DW_FORM_ref_addr: // reference to a DIE in the .info section + if data == nil { + Diag("dwarf: null reference in %d", abbrev) + if Thearch.Ptrsize == 8 { + Thearch.Vput(0) // invalid dwarf, gdb will complain. + } else { + Thearch.Lput(0) // invalid dwarf, gdb will complain. + } + } else { + off := (data.(*DWDie)).offs + if off == 0 { + fwdcount++ + } + if Linkmode == LinkExternal { + adddwarfrel(infosec, infosym, infoo, Thearch.Ptrsize, off) + break + } + + addrput(off) + } + + case DW_FORM_ref1, // reference within the compilation unit + DW_FORM_ref2, // reference + DW_FORM_ref4, // reference + DW_FORM_ref8, // reference + DW_FORM_ref_udata, // reference + + DW_FORM_strp, // string + DW_FORM_indirect: // (see Section 7.5.3) + fallthrough + default: + Exitf("dwarf: unsupported attribute form %d / class %d", form, cls) + } +} + +// Note that we can (and do) add arbitrary attributes to a DIE, but +// only the ones actually listed in the Abbrev will be written out. +func putattrs(abbrev int, attr *DWAttr) { +Outer: + for _, f := range abbrevs[abbrev].attr { + for ap := attr; ap != nil; ap = ap.link { + if ap.atr == f.attr { + putattr(abbrev, int(f.form), int(ap.cls), ap.value, ap.data) + continue Outer + } + } + + putattr(abbrev, int(f.form), 0, 0, nil) + } +} + +func putdies(die *DWDie) { + for ; die != nil; die = die.link { + putdie(die) + } +} + +func putdie(die *DWDie) { + die.offs = Cpos() - infoo + uleb128put(int64(die.abbrev)) + putattrs(die.abbrev, die.attr) + if abbrevs[die.abbrev].children != 0 { + putdies(die.child) + Cput(0) + } +} + +func reverselist(list **DWDie) { + curr := *list + var prev *DWDie + for curr != nil { + var next *DWDie = curr.link + curr.link = prev + prev = curr + curr = next + } + + *list = prev +} + +func reversetree(list **DWDie) { + reverselist(list) + for die := *list; die != nil; die = die.link { + if abbrevs[die.abbrev].children != 0 { + reversetree(&die.child) + } + } +} + +func newmemberoffsetattr(die *DWDie, offs int32) { + var block [20]byte + + i := 0 + block[i] = DW_OP_plus_uconst + i++ + i += uleb128enc(uint64(offs), block[i:]) + newattr(die, DW_AT_data_member_location, DW_CLS_BLOCK, int64(i), block[:i]) +} + +// GDB doesn't like DW_FORM_addr for DW_AT_location, so emit a +// location expression that evals to a const. +func newabslocexprattr(die *DWDie, addr int64, sym *LSym) { + newattr(die, DW_AT_location, DW_CLS_ADDRESS, addr, sym) + // below +} + +// Lookup predefined types +func lookup_or_diag(n string) *LSym { + s := Linkrlookup(Ctxt, n, 0) + if s == nil || s.Size == 0 { + Exitf("dwarf: missing type: %s", n) + } + + return s +} + +func dotypedef(parent *DWDie, name string, def *DWDie) { + // Only emit typedefs for real names. + if strings.HasPrefix(name, "map[") { + return + } + if strings.HasPrefix(name, "struct {") { + return + } + if strings.HasPrefix(name, "chan ") { + return + } + if name[0] == '[' || name[0] == '*' { + return + } + if def == nil { + Diag("dwarf: bad def in dotypedef") + } + + // The typedef entry must be created after the def, + // so that future lookups will find the typedef instead + // of the real definition. This hooks the typedef into any + // circular definition loops, so that gdb can understand them. + die := newdie(parent, DW_ABRV_TYPEDECL, name) + + newrefattr(die, DW_AT_type, def) +} + +// Define gotype, for composite ones recurse into constituents. +func defgotype(gotype *LSym) *DWDie { + if gotype == nil { + return mustFind(&dwtypes, "") + } + + if !strings.HasPrefix(gotype.Name, "type.") { + Diag("dwarf: type name doesn't start with \".type\": %s", gotype.Name) + return mustFind(&dwtypes, "") + } + + name := gotype.Name[5:] // could also decode from Type.string + + die := find(&dwtypes, name) + + if die != nil { + return die + } + + if false && Debug['v'] > 2 { + fmt.Printf("new type: %v\n", gotype) + } + + kind := decodetype_kind(gotype) + bytesize := decodetype_size(gotype) + + switch kind { + case obj.KindBool: + die = newdie(&dwtypes, DW_ABRV_BASETYPE, name) + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_boolean, 0) + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) + + case obj.KindInt, + obj.KindInt8, + obj.KindInt16, + obj.KindInt32, + obj.KindInt64: + die = newdie(&dwtypes, DW_ABRV_BASETYPE, name) + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_signed, 0) + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) + + case obj.KindUint, + obj.KindUint8, + obj.KindUint16, + obj.KindUint32, + obj.KindUint64, + obj.KindUintptr: + die = newdie(&dwtypes, DW_ABRV_BASETYPE, name) + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_unsigned, 0) + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) + + case obj.KindFloat32, + obj.KindFloat64: + die = newdie(&dwtypes, DW_ABRV_BASETYPE, name) + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_float, 0) + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) + + case obj.KindComplex64, + obj.KindComplex128: + die = newdie(&dwtypes, DW_ABRV_BASETYPE, name) + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_complex_float, 0) + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) + + case obj.KindArray: + die = newdie(&dwtypes, DW_ABRV_ARRAYTYPE, name) + dotypedef(&dwtypes, name, die) + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) + s := decodetype_arrayelem(gotype) + newrefattr(die, DW_AT_type, defgotype(s)) + fld := newdie(die, DW_ABRV_ARRAYRANGE, "range") + + // use actual length not upper bound; correct for 0-length arrays. + newattr(fld, DW_AT_count, DW_CLS_CONSTANT, decodetype_arraylen(gotype), 0) + + newrefattr(fld, DW_AT_type, mustFind(&dwtypes, "uintptr")) + + case obj.KindChan: + die = newdie(&dwtypes, DW_ABRV_CHANTYPE, name) + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) + s := decodetype_chanelem(gotype) + newrefattr(die, DW_AT_go_elem, defgotype(s)) + + case obj.KindFunc: + die = newdie(&dwtypes, DW_ABRV_FUNCTYPE, name) + dotypedef(&dwtypes, name, die) + newrefattr(die, DW_AT_type, mustFind(&dwtypes, "void")) + nfields := decodetype_funcincount(gotype) + var fld *DWDie + var s *LSym + for i := 0; i < nfields; i++ { + s = decodetype_funcintype(gotype, i) + fld = newdie(die, DW_ABRV_FUNCTYPEPARAM, s.Name[5:]) + newrefattr(fld, DW_AT_type, defgotype(s)) + } + + if decodetype_funcdotdotdot(gotype) != 0 { + newdie(die, DW_ABRV_DOTDOTDOT, "...") + } + nfields = decodetype_funcoutcount(gotype) + for i := 0; i < nfields; i++ { + s = decodetype_funcouttype(gotype, i) + fld = newdie(die, DW_ABRV_FUNCTYPEPARAM, s.Name[5:]) + newrefattr(fld, DW_AT_type, defptrto(defgotype(s))) + } + + case obj.KindInterface: + die = newdie(&dwtypes, DW_ABRV_IFACETYPE, name) + dotypedef(&dwtypes, name, die) + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) + nfields := int(decodetype_ifacemethodcount(gotype)) + var s *LSym + if nfields == 0 { + s = lookup_or_diag("type.runtime.eface") + } else { + s = lookup_or_diag("type.runtime.iface") + } + newrefattr(die, DW_AT_type, defgotype(s)) + + case obj.KindMap: + die = newdie(&dwtypes, DW_ABRV_MAPTYPE, name) + s := decodetype_mapkey(gotype) + newrefattr(die, DW_AT_go_key, defgotype(s)) + s = decodetype_mapvalue(gotype) + newrefattr(die, DW_AT_go_elem, defgotype(s)) + + case obj.KindPtr: + die = newdie(&dwtypes, DW_ABRV_PTRTYPE, name) + dotypedef(&dwtypes, name, die) + s := decodetype_ptrelem(gotype) + newrefattr(die, DW_AT_type, defgotype(s)) + + case obj.KindSlice: + die = newdie(&dwtypes, DW_ABRV_SLICETYPE, name) + dotypedef(&dwtypes, name, die) + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) + s := decodetype_arrayelem(gotype) + newrefattr(die, DW_AT_go_elem, defgotype(s)) + + case obj.KindString: + die = newdie(&dwtypes, DW_ABRV_STRINGTYPE, name) + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) + + case obj.KindStruct: + die = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, name) + dotypedef(&dwtypes, name, die) + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) + nfields := decodetype_structfieldcount(gotype) + var f string + var fld *DWDie + var s *LSym + for i := 0; i < nfields; i++ { + f = decodetype_structfieldname(gotype, i) + s = decodetype_structfieldtype(gotype, i) + if f == "" { + f = s.Name[5:] // skip "type." + } + fld = newdie(die, DW_ABRV_STRUCTFIELD, f) + newrefattr(fld, DW_AT_type, defgotype(s)) + newmemberoffsetattr(fld, int32(decodetype_structfieldoffs(gotype, i))) + } + + case obj.KindUnsafePointer: + die = newdie(&dwtypes, DW_ABRV_BARE_PTRTYPE, name) + + default: + Diag("dwarf: definition of unknown kind %d: %s", kind, gotype.Name) + die = newdie(&dwtypes, DW_ABRV_TYPEDECL, name) + newrefattr(die, DW_AT_type, mustFind(&dwtypes, "")) + } + + newattr(die, DW_AT_go_kind, DW_CLS_CONSTANT, int64(kind), 0) + + return die +} + +// Find or construct *T given T. +func defptrto(dwtype *DWDie) *DWDie { + ptrname := fmt.Sprintf("*%s", getattr(dwtype, DW_AT_name).data) + die := find(&dwtypes, ptrname) + if die == nil { + die = newdie(&dwtypes, DW_ABRV_PTRTYPE, ptrname) + newrefattr(die, DW_AT_type, dwtype) + } + + return die +} + +// Copies src's children into dst. Copies attributes by value. +// DWAttr.data is copied as pointer only. If except is one of +// the top-level children, it will not be copied. +func copychildrenexcept(dst *DWDie, src *DWDie, except *DWDie) { + var c *DWDie + var a *DWAttr + + for src = src.child; src != nil; src = src.link { + if src == except { + continue + } + c = newdie(dst, src.abbrev, getattr(src, DW_AT_name).data.(string)) + for a = src.attr; a != nil; a = a.link { + newattr(c, a.atr, int(a.cls), a.value, a.data) + } + copychildrenexcept(c, src, nil) + } + + reverselist(&dst.child) +} + +func copychildren(dst *DWDie, src *DWDie) { + copychildrenexcept(dst, src, nil) +} + +// Search children (assumed to have DW_TAG_member) for the one named +// field and set its DW_AT_type to dwtype +func substitutetype(structdie *DWDie, field string, dwtype *DWDie) { + child := mustFind(structdie, field) + if child == nil { + return + } + + a := getattr(child, DW_AT_type) + if a != nil { + a.data = dwtype + } else { + newrefattr(child, DW_AT_type, dwtype) + } +} + +func synthesizestringtypes(die *DWDie) { + prototype := walktypedef(defgotype(lookup_or_diag("type.runtime._string"))) + if prototype == nil { + return + } + + for ; die != nil; die = die.link { + if die.abbrev != DW_ABRV_STRINGTYPE { + continue + } + copychildren(die, prototype) + } +} + +func synthesizeslicetypes(die *DWDie) { + prototype := walktypedef(defgotype(lookup_or_diag("type.runtime.slice"))) + if prototype == nil { + return + } + + var elem *DWDie + for ; die != nil; die = die.link { + if die.abbrev != DW_ABRV_SLICETYPE { + continue + } + copychildren(die, prototype) + elem = getattr(die, DW_AT_go_elem).data.(*DWDie) + substitutetype(die, "array", defptrto(elem)) + } +} + +func mkinternaltypename(base string, arg1 string, arg2 string) string { + var buf string + + if arg2 == "" { + buf = fmt.Sprintf("%s<%s>", base, arg1) + } else { + buf = fmt.Sprintf("%s<%s,%s>", base, arg1, arg2) + } + n := buf + return n +} + +// synthesizemaptypes is way too closely married to runtime/hashmap.c +const ( + MaxKeySize = 128 + MaxValSize = 128 + BucketSize = 8 +) + +func synthesizemaptypes(die *DWDie) { + hash := walktypedef(defgotype(lookup_or_diag("type.runtime.hmap"))) + bucket := walktypedef(defgotype(lookup_or_diag("type.runtime.bmap"))) + + if hash == nil { + return + } + + var a *DWAttr + var dwh *DWDie + var dwhb *DWDie + var dwhk *DWDie + var dwhv *DWDie + var fld *DWDie + var indirect_key int + var indirect_val int + var keysize int + var keytype *DWDie + var t *DWDie + var valsize int + var valtype *DWDie + for ; die != nil; die = die.link { + if die.abbrev != DW_ABRV_MAPTYPE { + continue + } + + keytype = walktypedef(getattr(die, DW_AT_go_key).data.(*DWDie)) + valtype = walktypedef(getattr(die, DW_AT_go_elem).data.(*DWDie)) + + // compute size info like hashmap.c does. + a = getattr(keytype, DW_AT_byte_size) + + if a != nil { + keysize = int(a.value) + } else { + keysize = Thearch.Ptrsize + } + a = getattr(valtype, DW_AT_byte_size) + if a != nil { + valsize = int(a.value) + } else { + valsize = Thearch.Ptrsize + } + indirect_key = 0 + indirect_val = 0 + if keysize > MaxKeySize { + keysize = Thearch.Ptrsize + indirect_key = 1 + } + + if valsize > MaxValSize { + valsize = Thearch.Ptrsize + indirect_val = 1 + } + + // Construct type to represent an array of BucketSize keys + dwhk = newdie(&dwtypes, DW_ABRV_ARRAYTYPE, mkinternaltypename("[]key", getattr(keytype, DW_AT_name).data.(string), "")) + + newattr(dwhk, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize*int64(keysize), 0) + t = keytype + if indirect_key != 0 { + t = defptrto(keytype) + } + newrefattr(dwhk, DW_AT_type, t) + fld = newdie(dwhk, DW_ABRV_ARRAYRANGE, "size") + newattr(fld, DW_AT_count, DW_CLS_CONSTANT, BucketSize, 0) + newrefattr(fld, DW_AT_type, mustFind(&dwtypes, "uintptr")) + + // Construct type to represent an array of BucketSize values + dwhv = newdie(&dwtypes, DW_ABRV_ARRAYTYPE, mkinternaltypename("[]val", getattr(valtype, DW_AT_name).data.(string), "")) + + newattr(dwhv, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize*int64(valsize), 0) + t = valtype + if indirect_val != 0 { + t = defptrto(valtype) + } + newrefattr(dwhv, DW_AT_type, t) + fld = newdie(dwhv, DW_ABRV_ARRAYRANGE, "size") + newattr(fld, DW_AT_count, DW_CLS_CONSTANT, BucketSize, 0) + newrefattr(fld, DW_AT_type, mustFind(&dwtypes, "uintptr")) + + // Construct bucket + dwhb = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, mkinternaltypename("bucket", getattr(keytype, DW_AT_name).data.(string), getattr(valtype, DW_AT_name).data.(string))) + + // Copy over all fields except the field "data" from the generic bucket. + // "data" will be replaced with keys/values below. + copychildrenexcept(dwhb, bucket, find(bucket, "data")) + + fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "keys") + newrefattr(fld, DW_AT_type, dwhk) + newmemberoffsetattr(fld, BucketSize) + fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "values") + newrefattr(fld, DW_AT_type, dwhv) + newmemberoffsetattr(fld, BucketSize+BucketSize*int32(keysize)) + fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "overflow") + newrefattr(fld, DW_AT_type, defptrto(dwhb)) + newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))) + if Thearch.Regsize > Thearch.Ptrsize { + fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "pad") + newrefattr(fld, DW_AT_type, mustFind(&dwtypes, "uintptr")) + newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))+int32(Thearch.Ptrsize)) + } + + newattr(dwhb, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize+BucketSize*int64(keysize)+BucketSize*int64(valsize)+int64(Thearch.Regsize), 0) + + // Construct hash + dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, mkinternaltypename("hash", getattr(keytype, DW_AT_name).data.(string), getattr(valtype, DW_AT_name).data.(string))) + + copychildren(dwh, hash) + substitutetype(dwh, "buckets", defptrto(dwhb)) + substitutetype(dwh, "oldbuckets", defptrto(dwhb)) + newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, getattr(hash, DW_AT_byte_size).value, nil) + + // make map type a pointer to hash + newrefattr(die, DW_AT_type, defptrto(dwh)) + } +} + +func synthesizechantypes(die *DWDie) { + sudog := walktypedef(defgotype(lookup_or_diag("type.runtime.sudog"))) + waitq := walktypedef(defgotype(lookup_or_diag("type.runtime.waitq"))) + hchan := walktypedef(defgotype(lookup_or_diag("type.runtime.hchan"))) + if sudog == nil || waitq == nil || hchan == nil { + return + } + + sudogsize := int(getattr(sudog, DW_AT_byte_size).value) + + var a *DWAttr + var dwh *DWDie + var dws *DWDie + var dww *DWDie + var elemsize int + var elemtype *DWDie + for ; die != nil; die = die.link { + if die.abbrev != DW_ABRV_CHANTYPE { + continue + } + elemtype = getattr(die, DW_AT_go_elem).data.(*DWDie) + a = getattr(elemtype, DW_AT_byte_size) + if a != nil { + elemsize = int(a.value) + } else { + elemsize = Thearch.Ptrsize + } + + // sudog + dws = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, mkinternaltypename("sudog", getattr(elemtype, DW_AT_name).data.(string), "")) + + copychildren(dws, sudog) + substitutetype(dws, "elem", elemtype) + if elemsize > 8 { + elemsize -= 8 + } else { + elemsize = 0 + } + newattr(dws, DW_AT_byte_size, DW_CLS_CONSTANT, int64(sudogsize)+int64(elemsize), nil) + + // waitq + dww = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, mkinternaltypename("waitq", getattr(elemtype, DW_AT_name).data.(string), "")) + + copychildren(dww, waitq) + substitutetype(dww, "first", defptrto(dws)) + substitutetype(dww, "last", defptrto(dws)) + newattr(dww, DW_AT_byte_size, DW_CLS_CONSTANT, getattr(waitq, DW_AT_byte_size).value, nil) + + // hchan + dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, mkinternaltypename("hchan", getattr(elemtype, DW_AT_name).data.(string), "")) + + copychildren(dwh, hchan) + substitutetype(dwh, "recvq", dww) + substitutetype(dwh, "sendq", dww) + newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, getattr(hchan, DW_AT_byte_size).value, nil) + + newrefattr(die, DW_AT_type, defptrto(dwh)) + } +} + +// For use with pass.c::genasmsym +func defdwsymb(sym *LSym, s string, t int, v int64, size int64, ver int, gotype *LSym) { + if strings.HasPrefix(s, "go.string.") { + return + } + + if strings.HasPrefix(s, "type.") && s != "type.*" && !strings.HasPrefix(s, "type..") { + defgotype(sym) + return + } + + var dv *DWDie + + var dt *DWDie + switch t { + default: + return + + case 'd', 'b', 'D', 'B': + dv = newdie(&dwglobals, DW_ABRV_VARIABLE, s) + newabslocexprattr(dv, v, sym) + if ver == 0 { + newattr(dv, DW_AT_external, DW_CLS_FLAG, 1, 0) + } + fallthrough + + case 'a', 'p': + dt = defgotype(gotype) + } + + if dv != nil { + newrefattr(dv, DW_AT_type, dt) + } +} + +func movetomodule(parent *DWDie) { + die := dwroot.child.child + for die.link != nil { + die = die.link + } + die.link = parent.child +} + +// If the pcln table contains runtime/runtime.go, use that to set gdbscript path. +func finddebugruntimepath(s *LSym) { + if gdbscript != "" { + return + } + + var f *LSym + var p string + for i := 0; i < s.Pcln.Nfile; i++ { + f = s.Pcln.File[i] + _ = p + if i := strings.Index(f.Name, "runtime/runtime.go"); i >= 0 { + gdbscript = f.Name[:i] + "runtime/runtime-gdb.py" + break + } + } +} + +/* + * Generate short opcodes when possible, long ones when necessary. + * See section 6.2.5 + */ +const ( + LINE_BASE = -1 + LINE_RANGE = 4 + OPCODE_BASE = 10 +) + +func putpclcdelta(delta_pc int64, delta_lc int64) { + if LINE_BASE <= delta_lc && delta_lc < LINE_BASE+LINE_RANGE { + var opcode int64 = OPCODE_BASE + (delta_lc - LINE_BASE) + (LINE_RANGE * delta_pc) + if OPCODE_BASE <= opcode && opcode < 256 { + Cput(uint8(opcode)) + return + } + } + + if delta_pc != 0 { + Cput(DW_LNS_advance_pc) + sleb128put(delta_pc) + } + + Cput(DW_LNS_advance_line) + sleb128put(delta_lc) + Cput(DW_LNS_copy) +} + +func newcfaoffsetattr(die *DWDie, offs int32) { + var block [20]byte + + i := 0 + + block[i] = DW_OP_call_frame_cfa + i++ + if offs != 0 { + block[i] = DW_OP_consts + i++ + i += sleb128enc(int64(offs), block[i:]) + block[i] = DW_OP_plus + i++ + } + + newattr(die, DW_AT_location, DW_CLS_BLOCK, int64(i), block[:i]) +} + +func mkvarname(name string, da int) string { + buf := fmt.Sprintf("%s#%d", name, da) + n := buf + return n +} + +/* + * Walk prog table, emit line program and build DIE tree. + */ + +// flush previous compilation unit. +func flushunit(dwinfo *DWDie, pc int64, pcsym *LSym, unitstart int64, header_length int32) { + if dwinfo != nil && pc != 0 { + newattr(dwinfo, DW_AT_high_pc, DW_CLS_ADDRESS, pc+1, pcsym) + } + + if unitstart >= 0 { + Cput(0) // start extended opcode + uleb128put(1) + Cput(DW_LNE_end_sequence) + + here := Cpos() + Cseek(unitstart) + Thearch.Lput(uint32(here - unitstart - 4)) // unit_length + Thearch.Wput(2) // dwarf version + Thearch.Lput(uint32(header_length)) // header length starting here + Cseek(here) + } +} + +func getCompilationDir() string { + if dir, err := os.Getwd(); err == nil { + return dir + } + return "/" +} + +func writelines() { + if linesec == nil { + linesec = Linklookup(Ctxt, ".dwarfline", 0) + } + linesec.R = linesec.R[:0] + + unitstart := int64(-1) + headerend := int64(-1) + epc := int64(0) + var epcs *LSym + lineo = Cpos() + var dwinfo *DWDie + + flushunit(dwinfo, epc, epcs, unitstart, int32(headerend-unitstart-10)) + unitstart = Cpos() + + lang := DW_LANG_Go + + s := Ctxt.Textp + + dwinfo = newdie(&dwroot, DW_ABRV_COMPUNIT, "go") + 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 + Thearch.Lput(0) // unit_length (*), will be filled in by flushunit. + Thearch.Wput(2) // dwarf version (appendix F) + Thearch.Lput(0) // header_length (*), filled in by flushunit. + + // cpos == unitstart + 4 + 2 + 4 + Cput(1) // minimum_instruction_length + Cput(1) // default_is_stmt + Cput(LINE_BASE & 0xFF) // line_base + Cput(LINE_RANGE) // line_range + Cput(OPCODE_BASE) // opcode_base + Cput(0) // standard_opcode_lengths[1] + Cput(1) // standard_opcode_lengths[2] + Cput(1) // standard_opcode_lengths[3] + Cput(1) // standard_opcode_lengths[4] + Cput(1) // standard_opcode_lengths[5] + Cput(0) // standard_opcode_lengths[6] + Cput(0) // standard_opcode_lengths[7] + Cput(0) // standard_opcode_lengths[8] + Cput(1) // standard_opcode_lengths[9] + Cput(0) // include_directories (empty) + + files := make([]*LSym, Ctxt.Nhistfile) + + for f := Ctxt.Filesyms; f != nil; f = f.Next { + files[f.Value-1] = f + } + + for i := 0; int32(i) < Ctxt.Nhistfile; i++ { + strnput(files[i].Name, len(files[i].Name)+4) + } + + // 4 zeros: the string termination + 3 fields. + Cput(0) + // terminate file_names. + headerend = Cpos() + + Cput(0) // start extended opcode + uleb128put(1 + int64(Thearch.Ptrsize)) + Cput(DW_LNE_set_address) + + pc := s.Value + line := 1 + file := 1 + if Linkmode == LinkExternal { + adddwarfrel(linesec, s, lineo, Thearch.Ptrsize, 0) + } else { + addrput(pc) + } + + var a *Auto + var da int + var dt int + var dwfunc *DWDie + var dws **DWDie + var dwvar *DWDie + var n string + var nn string + var offs int64 + var pcfile Pciter + var pcline Pciter + var varhash [HASHSIZE]*DWDie + for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { + s = Ctxt.Cursym + + dwfunc = newdie(dwinfo, DW_ABRV_FUNCTION, s.Name) + newattr(dwfunc, DW_AT_low_pc, DW_CLS_ADDRESS, s.Value, s) + epc = s.Value + s.Size + epcs = s + newattr(dwfunc, DW_AT_high_pc, DW_CLS_ADDRESS, epc, s) + if s.Version == 0 { + newattr(dwfunc, DW_AT_external, DW_CLS_FLAG, 1, 0) + } + + if s.Pcln == nil { + continue + } + + finddebugruntimepath(s) + + pciterinit(Ctxt, &pcfile, &s.Pcln.Pcfile) + pciterinit(Ctxt, &pcline, &s.Pcln.Pcline) + epc = pc + for pcfile.done == 0 && pcline.done == 0 { + if epc-s.Value >= int64(pcfile.nextpc) { + pciternext(&pcfile) + continue + } + + if epc-s.Value >= int64(pcline.nextpc) { + pciternext(&pcline) + continue + } + + if int32(file) != pcfile.value { + Cput(DW_LNS_set_file) + uleb128put(int64(pcfile.value)) + file = int(pcfile.value) + } + + putpclcdelta(s.Value+int64(pcline.pc)-pc, int64(pcline.value)-int64(line)) + + pc = s.Value + int64(pcline.pc) + line = int(pcline.value) + if pcfile.nextpc < pcline.nextpc { + epc = int64(pcfile.nextpc) + } else { + epc = int64(pcline.nextpc) + } + epc += s.Value + } + + da = 0 + dwfunc.hash = varhash[:] // enable indexing of children by name + varhash = [HASHSIZE]*DWDie{} + for a = s.Autom; a != nil; a = a.Link { + switch a.Name { + case obj.A_AUTO: + dt = DW_ABRV_AUTO + 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 + } + + if strings.Contains(a.Asym.Name, ".autotmp_") { + continue + } + if find(dwfunc, a.Asym.Name) != nil { + n = mkvarname(a.Asym.Name, da) + } else { + n = a.Asym.Name + } + + // Drop the package prefix from locals and arguments. + _ = nn + if i := strings.LastIndex(n, "."); i >= 0 { + n = n[i+1:] + } + + dwvar = newdie(dwfunc, dt, n) + newcfaoffsetattr(dwvar, int32(offs)) + newrefattr(dwvar, DW_AT_type, defgotype(a.Gotype)) + + // push dwvar down dwfunc->child to preserve order + newattr(dwvar, DW_AT_internal_location, DW_CLS_CONSTANT, offs, nil) + + dwfunc.child = dwvar.link // take dwvar out from the top of the list + for dws = &dwfunc.child; *dws != nil; dws = &(*dws).link { + if offs > getattr(*dws, DW_AT_internal_location).value { + break + } + } + dwvar.link = *dws + *dws = dwvar + + da++ + } + + dwfunc.hash = nil + } + + flushunit(dwinfo, epc, epcs, unitstart, int32(headerend-unitstart-10)) + linesize = Cpos() - lineo +} + +/* + * Emit .debug_frame + */ +const ( + CIERESERVE = 16 + DATAALIGNMENTFACTOR = -4 +) + +func putpccfadelta(deltapc int64, cfa int64) { + Cput(DW_CFA_def_cfa_offset_sf) + sleb128put(cfa / DATAALIGNMENTFACTOR) + + if deltapc < 0x40 { + Cput(uint8(DW_CFA_advance_loc + deltapc)) + } else if deltapc < 0x100 { + Cput(DW_CFA_advance_loc1) + Cput(uint8(deltapc)) + } else if deltapc < 0x10000 { + Cput(DW_CFA_advance_loc2) + Thearch.Wput(uint16(deltapc)) + } else { + Cput(DW_CFA_advance_loc4) + Thearch.Lput(uint32(deltapc)) + } +} + +func writeframes() { + if framesec == nil { + framesec = Linklookup(Ctxt, ".dwarfframe", 0) + } + framesec.R = framesec.R[:0] + 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(int64(Thearch.Dwarfreglr)) // return_address_register + + Cput(DW_CFA_def_cfa) + + uleb128put(int64(Thearch.Dwarfregsp)) // register SP (**ABI-dependent, defined in l.h) + if haslinkregister() { + uleb128put(int64(0)) // offset + } else { + uleb128put(int64(Thearch.Ptrsize)) // offset + } + + 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() + + if pad < 0 { + Exitf("dwarf: CIERESERVE too small by %d bytes.", -pad) + } + + strnput("", int(pad)) + + var fdeo int64 + var fdesize int64 + var nextpc uint32 + var pcsp Pciter + var s *LSym + for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { + s = Ctxt.Cursym + if s.Pcln == nil { + continue + } + + fdeo = Cpos() + + // Emit a FDE, Section 6.4.1, starting wit a placeholder. + Thearch.Lput(0) // length, must be multiple of thearch.ptrsize + Thearch.Lput(0) // Pointer to the CIE above, at offset 0 + addrput(0) // initial location + addrput(0) // address range + + for pciterinit(Ctxt, &pcsp, &s.Pcln.Pcsp); pcsp.done == 0; pciternext(&pcsp) { + nextpc = pcsp.nextpc + + // pciterinit goes up to the end of the function, + // but DWARF expects us to stop just before the end. + if int64(nextpc) == s.Size { + nextpc-- + if nextpc < pcsp.pc { + continue + } + } + + 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. + pad = Rnd(fdesize, int64(Thearch.Ptrsize)) - fdesize + strnput("", int(pad)) + fdesize += pad + + // Emit the FDE header for real, Section 6.4.1. + Cseek(fdeo) + + Thearch.Lput(uint32(fdesize)) + if Linkmode == LinkExternal { + adddwarfrel(framesec, framesym, frameo, 4, 0) + adddwarfrel(framesec, s, frameo, Thearch.Ptrsize, 0) + } else { + Thearch.Lput(0) + addrput(s.Value) + } + + addrput(s.Size) + Cseek(fdeo + 4 + fdesize) + } + + Cflush() + framesize = Cpos() - frameo +} + +/* + * Walk DWarfDebugInfoEntries, and emit .debug_info + */ +const ( + COMPUNITHEADERSIZE = 4 + 2 + 4 + 1 +) + +func writeinfo() { + fwdcount = 0 + if infosec == nil { + infosec = Linklookup(Ctxt, ".dwarfinfo", 0) + } + infosec.R = infosec.R[:0] + + if arangessec == nil { + arangessec = Linklookup(Ctxt, ".dwarfaranges", 0) + } + arangessec.R = arangessec.R[:0] + + var here int64 + var unitstart int64 + for compunit := dwroot.child; compunit != nil; compunit = compunit.link { + unitstart = Cpos() + + // Write .debug_info Compilation Unit Header (sec 7.5.1) + // Fields marked with (*) must be changed for 64-bit dwarf + // This must match COMPUNITHEADERSIZE above. + Thearch.Lput(0) // unit_length (*), will be filled in later. + Thearch.Wput(2) // dwarf version (appendix F) + + // debug_abbrev_offset (*) + if Linkmode == LinkExternal { + adddwarfrel(infosec, abbrevsym, infoo, 4, 0) + } else { + Thearch.Lput(0) + } + + Cput(uint8(Thearch.Ptrsize)) // address_size + + putdie(compunit) + + here = Cpos() + Cseek(unitstart) + Thearch.Lput(uint32(here - unitstart - 4)) // exclude the length field. + Cseek(here) + } + + Cflush() +} + +/* + * Emit .debug_pubnames/_types. _info must have been written before, + * because we need die->offs and infoo/infosize; + */ +func ispubname(die *DWDie) bool { + switch die.abbrev { + case DW_ABRV_FUNCTION, DW_ABRV_VARIABLE: + a := getattr(die, DW_AT_external) + return a != nil && a.value != 0 + } + + return false +} + +func ispubtype(die *DWDie) bool { + return die.abbrev >= DW_ABRV_NULLTYPE +} + +func writepub(ispub func(*DWDie) bool) int64 { + var die *DWDie + var dwa *DWAttr + var unitstart int64 + var unitend int64 + var here int64 + + sectionstart := Cpos() + + for compunit := dwroot.child; compunit != nil; compunit = compunit.link { + unitstart = compunit.offs - COMPUNITHEADERSIZE + if compunit.link != nil { + unitend = compunit.link.offs - COMPUNITHEADERSIZE + } else { + unitend = infoo + infosize + } + + // Write .debug_pubnames/types Header (sec 6.1.1) + Thearch.Lput(0) // unit_length (*), will be filled in later. + Thearch.Wput(2) // dwarf version (appendix F) + Thearch.Lput(uint32(unitstart)) // debug_info_offset (of the Comp unit Header) + Thearch.Lput(uint32(unitend - unitstart)) // debug_info_length + + for die = compunit.child; die != nil; die = die.link { + if !ispub(die) { + continue + } + Thearch.Lput(uint32(die.offs - unitstart)) + dwa = getattr(die, DW_AT_name) + strnput(dwa.data.(string), int(dwa.value+1)) + } + + Thearch.Lput(0) + + here = Cpos() + Cseek(sectionstart) + Thearch.Lput(uint32(here - sectionstart - 4)) // exclude the length field. + Cseek(here) + } + + return sectionstart +} + +/* + * emit .debug_aranges. _info must have been written before, + * because we need die->offs of dw_globals. + */ +func writearanges() int64 { + var b *DWAttr + var e *DWAttr + var value int64 + + sectionstart := Cpos() + headersize := int(Rnd(4+2+4+1+1, int64(Thearch.Ptrsize))) // don't count unit_length field itself + + for compunit := dwroot.child; compunit != nil; compunit = compunit.link { + b = getattr(compunit, DW_AT_low_pc) + if b == nil { + continue + } + e = getattr(compunit, DW_AT_high_pc) + if e == nil { + continue + } + + // Write .debug_aranges Header + entry (sec 6.1.2) + Thearch.Lput(uint32(headersize) + 4*uint32(Thearch.Ptrsize) - 4) // unit_length (*) + Thearch.Wput(2) // dwarf version (appendix F) + + value = compunit.offs - COMPUNITHEADERSIZE // debug_info_offset + if Linkmode == LinkExternal { + adddwarfrel(arangessec, infosym, sectionstart, 4, value) + } else { + Thearch.Lput(uint32(value)) + } + + Cput(uint8(Thearch.Ptrsize)) // address_size + Cput(0) // segment_size + strnput("", headersize-(4+2+4+1+1)) // align to thearch.ptrsize + + if Linkmode == LinkExternal { + adddwarfrel(arangessec, b.data.(*LSym), sectionstart, Thearch.Ptrsize, b.value-(b.data.(*LSym)).Value) + } else { + addrput(b.value) + } + + addrput(e.value - b.value) + addrput(0) + addrput(0) + } + + Cflush() + return sectionstart +} + +func writegdbscript() int64 { + sectionstart := Cpos() + + if gdbscript != "" { + Cput(1) // magic 1 byte? + strnput(gdbscript, len(gdbscript)+1) + Cflush() + } + + return sectionstart +} + +func align(size int64) { + if HEADTYPE == obj.Hwindows { // Only Windows PE need section align. + strnput("", int(Rnd(size, PEFILEALIGN)-size)) + } +} + +func writedwarfreloc(s *LSym) int64 { + var i int + var r *Reloc + + start := Cpos() + for ri := 0; ri < len(s.R); ri++ { + r = &s.R[ri] + if Iself { + i = Thearch.Elfreloc1(r, int64(r.Off)) + } else if HEADTYPE == obj.Hdarwin { + i = Thearch.Machoreloc1(r, int64(r.Off)) + } else { + i = -1 + } + if i < 0 { + Diag("unsupported obj reloc %d/%d to %s", r.Type, r.Siz, r.Sym.Name) + } + } + + 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 + * the per-compilation unit part of the DIE tree, while simultaneously + * emitting the debug_line section. When the final tree contains + * forward references, it will write the debug_info section in 2 + * passes. + * + */ +func Dwarfemitdebugsections() { + if Debug['w'] != 0 { // disable dwarf + 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. + newattr(&dwtypes, DW_AT_name, DW_CLS_STRING, int64(len("dwtypes")), "dwtypes") + + mkindex(&dwroot) + mkindex(&dwtypes) + mkindex(&dwglobals) + + // Some types that must exist to define other ones. + newdie(&dwtypes, DW_ABRV_NULLTYPE, "") + + newdie(&dwtypes, DW_ABRV_NULLTYPE, "void") + newdie(&dwtypes, DW_ABRV_BARE_PTRTYPE, "unsafe.Pointer") + + die := newdie(&dwtypes, DW_ABRV_BASETYPE, "uintptr") // needed for array size + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_unsigned, 0) + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, int64(Thearch.Ptrsize), 0) + newattr(die, DW_AT_go_kind, DW_CLS_CONSTANT, obj.KindUintptr, 0) + + // Needed by the prettyprinter code for interface inspection. + defgotype(lookup_or_diag("type.runtime._type")) + + defgotype(lookup_or_diag("type.runtime.interfacetype")) + defgotype(lookup_or_diag("type.runtime.itab")) + + genasmsym(defdwsymb) + + writeabbrev() + align(abbrevsize) + writelines() + align(linesize) + writeframes() + align(framesize) + + synthesizestringtypes(dwtypes.child) + synthesizeslicetypes(dwtypes.child) + synthesizemaptypes(dwtypes.child) + synthesizechantypes(dwtypes.child) + + reversetree(&dwroot.child) + reversetree(&dwtypes.child) + reversetree(&dwglobals.child) + + movetomodule(&dwtypes) + movetomodule(&dwglobals) + + infoo = Cpos() + writeinfo() + infoe := Cpos() + pubnameso = infoe + pubtypeso = infoe + arangeso = infoe + gdbscripto = infoe + + if fwdcount > 0 { + if Debug['v'] != 0 { + fmt.Fprintf(&Bso, "%5.2f dwarf pass 2.\n", obj.Cputime()) + } + Cseek(infoo) + writeinfo() + if fwdcount > 0 { + Exitf("dwarf: unresolved references after first dwarf info pass") + } + + if infoe != Cpos() { + Exitf("dwarf: inconsistent second dwarf info pass") + } + } + + infosize = infoe - infoo + align(infosize) + + pubnameso = writepub(ispubname) + pubnamessize = Cpos() - pubnameso + align(pubnamessize) + + pubtypeso = writepub(ispubtype) + pubtypessize = Cpos() - pubtypeso + align(pubtypessize) + + arangeso = writearanges() + arangessize = Cpos() - arangeso + align(arangessize) + + gdbscripto = writegdbscript() + gdbscriptsize = Cpos() - gdbscripto + align(gdbscriptsize) + + 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) + + arangesreloco = writedwarfreloc(arangessec) + arangesrelocsize = Cpos() - arangesreloco + align(arangesrelocsize) + + linereloco = writedwarfreloc(linesec) + linerelocsize = Cpos() - linereloco + align(linerelocsize) + + framereloco = writedwarfreloc(framesec) + framerelocsize = Cpos() - framereloco + align(framerelocsize) +} + +/* + * Elf. + */ +const ( + ElfStrDebugAbbrev = iota + ElfStrDebugAranges + ElfStrDebugFrame + ElfStrDebugInfo + ElfStrDebugLine + ElfStrDebugLoc + ElfStrDebugMacinfo + ElfStrDebugPubNames + ElfStrDebugPubTypes + ElfStrDebugRanges + ElfStrDebugStr + ElfStrGDBScripts + ElfStrRelDebugInfo + ElfStrRelDebugAranges + ElfStrRelDebugLine + ElfStrRelDebugFrame + NElfStrDbg +) + +var elfstrdbg [NElfStrDbg]int64 + +func dwarfaddshstrings(shstrtab *LSym) { + if Debug['w'] != 0 { // disable dwarf + return + } + + elfstrdbg[ElfStrDebugAbbrev] = Addstring(shstrtab, ".debug_abbrev") + elfstrdbg[ElfStrDebugAranges] = Addstring(shstrtab, ".debug_aranges") + elfstrdbg[ElfStrDebugFrame] = Addstring(shstrtab, ".debug_frame") + elfstrdbg[ElfStrDebugInfo] = Addstring(shstrtab, ".debug_info") + elfstrdbg[ElfStrDebugLine] = Addstring(shstrtab, ".debug_line") + elfstrdbg[ElfStrDebugLoc] = Addstring(shstrtab, ".debug_loc") + elfstrdbg[ElfStrDebugMacinfo] = Addstring(shstrtab, ".debug_macinfo") + elfstrdbg[ElfStrDebugPubNames] = Addstring(shstrtab, ".debug_pubnames") + elfstrdbg[ElfStrDebugPubTypes] = Addstring(shstrtab, ".debug_pubtypes") + elfstrdbg[ElfStrDebugRanges] = Addstring(shstrtab, ".debug_ranges") + elfstrdbg[ElfStrDebugStr] = Addstring(shstrtab, ".debug_str") + elfstrdbg[ElfStrGDBScripts] = Addstring(shstrtab, ".debug_gdb_scripts") + if Linkmode == LinkExternal { + switch Thearch.Thechar { + case '6', '7', '9': + elfstrdbg[ElfStrRelDebugInfo] = Addstring(shstrtab, ".rela.debug_info") + elfstrdbg[ElfStrRelDebugAranges] = Addstring(shstrtab, ".rela.debug_aranges") + elfstrdbg[ElfStrRelDebugLine] = Addstring(shstrtab, ".rela.debug_line") + elfstrdbg[ElfStrRelDebugFrame] = Addstring(shstrtab, ".rela.debug_frame") + default: + elfstrdbg[ElfStrRelDebugInfo] = Addstring(shstrtab, ".rel.debug_info") + elfstrdbg[ElfStrRelDebugAranges] = Addstring(shstrtab, ".rel.debug_aranges") + 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 + } +} + +// Add section symbols for DWARF debug info. This is called before +// dwarfaddelfheaders. +func dwarfaddelfsectionsyms() { + if infosym != nil { + infosympos = Cpos() + putelfsectionsym(infosym, 0) + } + + if abbrevsym != nil { + abbrevsympos = Cpos() + putelfsectionsym(abbrevsym, 0) + } + + if linesym != nil { + linesympos = Cpos() + putelfsectionsym(linesym, 0) + } + + if framesym != nil { + framesympos = Cpos() + putelfsectionsym(framesym, 0) + } +} + +func dwarfaddelfrelocheader(elfstr int, shdata *ElfShdr, off int64, size int64) { + sh := newElfShdr(elfstrdbg[elfstr]) + switch Thearch.Thechar { + case '6', '7', '9': + sh.type_ = SHT_RELA + default: + sh.type_ = SHT_REL + } + + sh.entsize = uint64(Thearch.Ptrsize) * 2 + if sh.type_ == SHT_RELA { + sh.entsize += uint64(Thearch.Ptrsize) + } + sh.link = uint32(elfshname(".symtab").shnum) + sh.info = uint32(shdata.shnum) + sh.off = uint64(off) + sh.size = uint64(size) + sh.addralign = uint64(Thearch.Ptrsize) +} + +func dwarfaddelfheaders() { + if Debug['w'] != 0 { // disable dwarf + return + } + + sh := newElfShdr(elfstrdbg[ElfStrDebugAbbrev]) + sh.type_ = SHT_PROGBITS + sh.off = uint64(abbrevo) + sh.size = uint64(abbrevsize) + sh.addralign = 1 + if abbrevsympos > 0 { + putelfsymshndx(abbrevsympos, sh.shnum) + } + + sh = newElfShdr(elfstrdbg[ElfStrDebugLine]) + sh.type_ = SHT_PROGBITS + sh.off = uint64(lineo) + sh.size = uint64(linesize) + sh.addralign = 1 + if linesympos > 0 { + putelfsymshndx(linesympos, sh.shnum) + } + shline := sh + + sh = newElfShdr(elfstrdbg[ElfStrDebugFrame]) + sh.type_ = SHT_PROGBITS + sh.off = uint64(frameo) + sh.size = uint64(framesize) + sh.addralign = 1 + if framesympos > 0 { + putelfsymshndx(framesympos, sh.shnum) + } + shframe := sh + + sh = newElfShdr(elfstrdbg[ElfStrDebugInfo]) + sh.type_ = SHT_PROGBITS + sh.off = uint64(infoo) + sh.size = uint64(infosize) + sh.addralign = 1 + if infosympos > 0 { + putelfsymshndx(infosympos, sh.shnum) + } + shinfo := sh + + if pubnamessize > 0 { + sh := newElfShdr(elfstrdbg[ElfStrDebugPubNames]) + sh.type_ = SHT_PROGBITS + sh.off = uint64(pubnameso) + sh.size = uint64(pubnamessize) + sh.addralign = 1 + } + + if pubtypessize > 0 { + sh := newElfShdr(elfstrdbg[ElfStrDebugPubTypes]) + sh.type_ = SHT_PROGBITS + sh.off = uint64(pubtypeso) + sh.size = uint64(pubtypessize) + sh.addralign = 1 + } + + var sharanges *ElfShdr + if arangessize != 0 { + sh := newElfShdr(elfstrdbg[ElfStrDebugAranges]) + sh.type_ = SHT_PROGBITS + sh.off = uint64(arangeso) + sh.size = uint64(arangessize) + sh.addralign = 1 + sharanges = sh + } + + if gdbscriptsize != 0 { + sh := newElfShdr(elfstrdbg[ElfStrGDBScripts]) + sh.type_ = SHT_PROGBITS + sh.off = uint64(gdbscripto) + sh.size = uint64(gdbscriptsize) + sh.addralign = 1 + } + + if inforelocsize != 0 { + dwarfaddelfrelocheader(ElfStrRelDebugInfo, shinfo, inforeloco, inforelocsize) + } + + if arangesrelocsize != 0 { + dwarfaddelfrelocheader(ElfStrRelDebugAranges, sharanges, arangesreloco, arangesrelocsize) + } + + if linerelocsize != 0 { + dwarfaddelfrelocheader(ElfStrRelDebugLine, shline, linereloco, linerelocsize) + } + + if framerelocsize != 0 { + dwarfaddelfrelocheader(ElfStrRelDebugFrame, shframe, framereloco, framerelocsize) + } +} + +/* + * Macho + */ +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 := Rnd(int64(Segdwarf.Fileoff), 0x1000) + addr := Segdata.Vaddr + Segdata.Length + + nsect := 4 + if pubnamessize > 0 { + nsect++ + } + if pubtypessize > 0 { + nsect++ + } + if arangessize > 0 { + nsect++ + } + if gdbscriptsize > 0 { + nsect++ + } + + 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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) + if gdbscriptsize > 0 { + msect := newMachoSect(ms, "__debug_gdb_scripts", "__DWARF") + msect.off = uint32(gdbscripto) + msect.size = uint64(gdbscriptsize) + msect.addr = addr + addr += msect.size + msect.flag = 0x02000000 + } +} + +/* + * Windows PE + */ +func dwarfaddpeheaders() { + if Debug['w'] != 0 { // disable dwarf + return + } + + newPEDWARFSection(".debug_abbrev", abbrevsize) + newPEDWARFSection(".debug_line", linesize) + newPEDWARFSection(".debug_frame", framesize) + newPEDWARFSection(".debug_info", infosize) + newPEDWARFSection(".debug_pubnames", pubnamessize) + newPEDWARFSection(".debug_pubtypes", pubtypessize) + newPEDWARFSection(".debug_aranges", arangessize) + newPEDWARFSection(".debug_gdb_scripts", gdbscriptsize) +} diff --git a/src/cmd/link/internal/ld/dwarf_defs.go b/src/cmd/link/internal/ld/dwarf_defs.go new file mode 100644 index 0000000000..61389d9218 --- /dev/null +++ b/src/cmd/link/internal/ld/dwarf_defs.go @@ -0,0 +1,516 @@ +// 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. + +package ld + +// TODO/NICETOHAVE: +// - eliminate DW_CLS_ if not used +// - package info in compilation units +// - assign global variables and types to their packages +// - gdb uses c syntax, meaning clumsy quoting is needed for go identifiers. eg +// ptype struct '[]uint8' and qualifiers need to be quoted away +// - lexical scoping is lost, so gdb gets confused as to which 'obj.i' you mean. +// - file:line info for variables +// - make strings a typedef so prettyprinters can see the underlying string type +// +// 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. + +/* + * Emit debug_abbrevs, debug_info and debug_line sections to current + * offset in cout. + */ + +/* + * Add the dwarf section names to the ELF + * s[ection]h[eader]str[ing]tab. Prerequisite for + * dwarfaddelfheaders(). + */ + +/* + * Add section headers pointing to the sections emitted in + * dwarfemitdebugsections. + */ +// 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. + +// Cut, pasted, tr-and-awk'ed from tables in +// http://dwarfstd.org/doc/Dwarf3.pdf + +// Table 18 +const ( + DW_TAG_array_type = 0x01 + DW_TAG_class_type = 0x02 + DW_TAG_entry_point = 0x03 + DW_TAG_enumeration_type = 0x04 + DW_TAG_formal_parameter = 0x05 + DW_TAG_imported_declaration = 0x08 + DW_TAG_label = 0x0a + DW_TAG_lexical_block = 0x0b + DW_TAG_member = 0x0d + DW_TAG_pointer_type = 0x0f + DW_TAG_reference_type = 0x10 + DW_TAG_compile_unit = 0x11 + DW_TAG_string_type = 0x12 + DW_TAG_structure_type = 0x13 + DW_TAG_subroutine_type = 0x15 + DW_TAG_typedef = 0x16 + DW_TAG_union_type = 0x17 + DW_TAG_unspecified_parameters = 0x18 + DW_TAG_variant = 0x19 + DW_TAG_common_block = 0x1a + DW_TAG_common_inclusion = 0x1b + DW_TAG_inheritance = 0x1c + DW_TAG_inlined_subroutine = 0x1d + DW_TAG_module = 0x1e + DW_TAG_ptr_to_member_type = 0x1f + DW_TAG_set_type = 0x20 + DW_TAG_subrange_type = 0x21 + DW_TAG_with_stmt = 0x22 + DW_TAG_access_declaration = 0x23 + DW_TAG_base_type = 0x24 + DW_TAG_catch_block = 0x25 + DW_TAG_const_type = 0x26 + DW_TAG_constant = 0x27 + DW_TAG_enumerator = 0x28 + DW_TAG_file_type = 0x29 + DW_TAG_friend = 0x2a + DW_TAG_namelist = 0x2b + DW_TAG_namelist_item = 0x2c + DW_TAG_packed_type = 0x2d + DW_TAG_subprogram = 0x2e + DW_TAG_template_type_parameter = 0x2f + DW_TAG_template_value_parameter = 0x30 + DW_TAG_thrown_type = 0x31 + DW_TAG_try_block = 0x32 + DW_TAG_variant_part = 0x33 + DW_TAG_variable = 0x34 + DW_TAG_volatile_type = 0x35 + // Dwarf3 + DW_TAG_dwarf_procedure = 0x36 + DW_TAG_restrict_type = 0x37 + DW_TAG_interface_type = 0x38 + DW_TAG_namespace = 0x39 + DW_TAG_imported_module = 0x3a + DW_TAG_unspecified_type = 0x3b + DW_TAG_partial_unit = 0x3c + DW_TAG_imported_unit = 0x3d + DW_TAG_condition = 0x3f + DW_TAG_shared_type = 0x40 + // Dwarf4 + DW_TAG_type_unit = 0x41 + DW_TAG_rvalue_reference_type = 0x42 + DW_TAG_template_alias = 0x43 + + // User defined + DW_TAG_lo_user = 0x4080 + DW_TAG_hi_user = 0xffff +) + +// Table 19 +const ( + DW_CHILDREN_no = 0x00 + DW_CHILDREN_yes = 0x01 +) + +// Not from the spec, but logicaly belongs here +const ( + DW_CLS_ADDRESS = 0x01 + iota + DW_CLS_BLOCK + DW_CLS_CONSTANT + DW_CLS_FLAG + DW_CLS_PTR // lineptr, loclistptr, macptr, rangelistptr + DW_CLS_REFERENCE + DW_CLS_ADDRLOC + DW_CLS_STRING +) + +// Table 20 +const ( + DW_AT_sibling = 0x01 // reference + DW_AT_location = 0x02 // block, loclistptr + DW_AT_name = 0x03 // string + DW_AT_ordering = 0x09 // constant + DW_AT_byte_size = 0x0b // block, constant, reference + DW_AT_bit_offset = 0x0c // block, constant, reference + DW_AT_bit_size = 0x0d // block, constant, reference + DW_AT_stmt_list = 0x10 // lineptr + DW_AT_low_pc = 0x11 // address + DW_AT_high_pc = 0x12 // address + DW_AT_language = 0x13 // constant + DW_AT_discr = 0x15 // reference + DW_AT_discr_value = 0x16 // constant + DW_AT_visibility = 0x17 // constant + DW_AT_import = 0x18 // reference + DW_AT_string_length = 0x19 // block, loclistptr + DW_AT_common_reference = 0x1a // reference + DW_AT_comp_dir = 0x1b // string + DW_AT_const_value = 0x1c // block, constant, string + DW_AT_containing_type = 0x1d // reference + DW_AT_default_value = 0x1e // reference + DW_AT_inline = 0x20 // constant + DW_AT_is_optional = 0x21 // flag + DW_AT_lower_bound = 0x22 // block, constant, reference + DW_AT_producer = 0x25 // string + DW_AT_prototyped = 0x27 // flag + DW_AT_return_addr = 0x2a // block, loclistptr + DW_AT_start_scope = 0x2c // constant + DW_AT_bit_stride = 0x2e // constant + DW_AT_upper_bound = 0x2f // block, constant, reference + DW_AT_abstract_origin = 0x31 // reference + DW_AT_accessibility = 0x32 // constant + DW_AT_address_class = 0x33 // constant + DW_AT_artificial = 0x34 // flag + DW_AT_base_types = 0x35 // reference + DW_AT_calling_convention = 0x36 // constant + DW_AT_count = 0x37 // block, constant, reference + DW_AT_data_member_location = 0x38 // block, constant, loclistptr + DW_AT_decl_column = 0x39 // constant + DW_AT_decl_file = 0x3a // constant + DW_AT_decl_line = 0x3b // constant + DW_AT_declaration = 0x3c // flag + DW_AT_discr_list = 0x3d // block + DW_AT_encoding = 0x3e // constant + DW_AT_external = 0x3f // flag + DW_AT_frame_base = 0x40 // block, loclistptr + DW_AT_friend = 0x41 // reference + DW_AT_identifier_case = 0x42 // constant + DW_AT_macro_info = 0x43 // macptr + DW_AT_namelist_item = 0x44 // block + DW_AT_priority = 0x45 // reference + DW_AT_segment = 0x46 // block, loclistptr + DW_AT_specification = 0x47 // reference + DW_AT_static_link = 0x48 // block, loclistptr + DW_AT_type = 0x49 // reference + DW_AT_use_location = 0x4a // block, loclistptr + DW_AT_variable_parameter = 0x4b // flag + DW_AT_virtuality = 0x4c // constant + DW_AT_vtable_elem_location = 0x4d // block, loclistptr + // Dwarf3 + DW_AT_allocated = 0x4e // block, constant, reference + DW_AT_associated = 0x4f // block, constant, reference + DW_AT_data_location = 0x50 // block + DW_AT_byte_stride = 0x51 // block, constant, reference + DW_AT_entry_pc = 0x52 // address + DW_AT_use_UTF8 = 0x53 // flag + DW_AT_extension = 0x54 // reference + DW_AT_ranges = 0x55 // rangelistptr + DW_AT_trampoline = 0x56 // address, flag, reference, string + DW_AT_call_column = 0x57 // constant + DW_AT_call_file = 0x58 // constant + DW_AT_call_line = 0x59 // constant + DW_AT_description = 0x5a // string + DW_AT_binary_scale = 0x5b // constant + DW_AT_decimal_scale = 0x5c // constant + DW_AT_small = 0x5d // reference + DW_AT_decimal_sign = 0x5e // constant + DW_AT_digit_count = 0x5f // constant + DW_AT_picture_string = 0x60 // string + DW_AT_mutable = 0x61 // flag + DW_AT_threads_scaled = 0x62 // flag + DW_AT_explicit = 0x63 // flag + DW_AT_object_pointer = 0x64 // reference + DW_AT_endianity = 0x65 // constant + DW_AT_elemental = 0x66 // flag + DW_AT_pure = 0x67 // flag + DW_AT_recursive = 0x68 // flag + + DW_AT_lo_user = 0x2000 // --- + DW_AT_hi_user = 0x3fff // --- +) + +// Table 21 +const ( + DW_FORM_addr = 0x01 // address + DW_FORM_block2 = 0x03 // block + DW_FORM_block4 = 0x04 // block + DW_FORM_data2 = 0x05 // constant + DW_FORM_data4 = 0x06 // constant, lineptr, loclistptr, macptr, rangelistptr + DW_FORM_data8 = 0x07 // constant, lineptr, loclistptr, macptr, rangelistptr + DW_FORM_string = 0x08 // string + DW_FORM_block = 0x09 // block + DW_FORM_block1 = 0x0a // block + DW_FORM_data1 = 0x0b // constant + DW_FORM_flag = 0x0c // flag + DW_FORM_sdata = 0x0d // constant + DW_FORM_strp = 0x0e // string + DW_FORM_udata = 0x0f // constant + DW_FORM_ref_addr = 0x10 // reference + DW_FORM_ref1 = 0x11 // reference + DW_FORM_ref2 = 0x12 // reference + DW_FORM_ref4 = 0x13 // reference + DW_FORM_ref8 = 0x14 // reference + DW_FORM_ref_udata = 0x15 // reference + DW_FORM_indirect = 0x16 // (see Section 7.5.3) +) + +// Table 24 (#operands, notes) +const ( + DW_OP_addr = 0x03 // 1 constant address (size target specific) + DW_OP_deref = 0x06 // 0 + DW_OP_const1u = 0x08 // 1 1-byte constant + DW_OP_const1s = 0x09 // 1 1-byte constant + DW_OP_const2u = 0x0a // 1 2-byte constant + DW_OP_const2s = 0x0b // 1 2-byte constant + DW_OP_const4u = 0x0c // 1 4-byte constant + DW_OP_const4s = 0x0d // 1 4-byte constant + DW_OP_const8u = 0x0e // 1 8-byte constant + DW_OP_const8s = 0x0f // 1 8-byte constant + DW_OP_constu = 0x10 // 1 ULEB128 constant + DW_OP_consts = 0x11 // 1 SLEB128 constant + DW_OP_dup = 0x12 // 0 + DW_OP_drop = 0x13 // 0 + DW_OP_over = 0x14 // 0 + DW_OP_pick = 0x15 // 1 1-byte stack index + DW_OP_swap = 0x16 // 0 + DW_OP_rot = 0x17 // 0 + DW_OP_xderef = 0x18 // 0 + DW_OP_abs = 0x19 // 0 + DW_OP_and = 0x1a // 0 + DW_OP_div = 0x1b // 0 + DW_OP_minus = 0x1c // 0 + DW_OP_mod = 0x1d // 0 + DW_OP_mul = 0x1e // 0 + DW_OP_neg = 0x1f // 0 + DW_OP_not = 0x20 // 0 + DW_OP_or = 0x21 // 0 + DW_OP_plus = 0x22 // 0 + DW_OP_plus_uconst = 0x23 // 1 ULEB128 addend + DW_OP_shl = 0x24 // 0 + DW_OP_shr = 0x25 // 0 + DW_OP_shra = 0x26 // 0 + DW_OP_xor = 0x27 // 0 + DW_OP_skip = 0x2f // 1 signed 2-byte constant + DW_OP_bra = 0x28 // 1 signed 2-byte constant + DW_OP_eq = 0x29 // 0 + DW_OP_ge = 0x2a // 0 + DW_OP_gt = 0x2b // 0 + DW_OP_le = 0x2c // 0 + DW_OP_lt = 0x2d // 0 + DW_OP_ne = 0x2e // 0 + DW_OP_lit0 = 0x30 // 0 ... + DW_OP_lit31 = 0x4f // 0 literals 0..31 = (DW_OP_lit0 + literal) + DW_OP_reg0 = 0x50 // 0 .. + DW_OP_reg31 = 0x6f // 0 reg 0..31 = (DW_OP_reg0 + regnum) + DW_OP_breg0 = 0x70 // 1 ... + DW_OP_breg31 = 0x8f // 1 SLEB128 offset base register 0..31 = (DW_OP_breg0 + regnum) + DW_OP_regx = 0x90 // 1 ULEB128 register + DW_OP_fbreg = 0x91 // 1 SLEB128 offset + DW_OP_bregx = 0x92 // 2 ULEB128 register followed by SLEB128 offset + DW_OP_piece = 0x93 // 1 ULEB128 size of piece addressed + DW_OP_deref_size = 0x94 // 1 1-byte size of data retrieved + DW_OP_xderef_size = 0x95 // 1 1-byte size of data retrieved + DW_OP_nop = 0x96 // 0 + DW_OP_push_object_address = 0x97 // 0 + DW_OP_call2 = 0x98 // 1 2-byte offset of DIE + DW_OP_call4 = 0x99 // 1 4-byte offset of DIE + DW_OP_call_ref = 0x9a // 1 4- or 8-byte offset of DIE + DW_OP_form_tls_address = 0x9b // 0 + DW_OP_call_frame_cfa = 0x9c // 0 + DW_OP_bit_piece = 0x9d // 2 + DW_OP_lo_user = 0xe0 + DW_OP_hi_user = 0xff +) + +// Table 25 +const ( + DW_ATE_address = 0x01 + DW_ATE_boolean = 0x02 + DW_ATE_complex_float = 0x03 + DW_ATE_float = 0x04 + DW_ATE_signed = 0x05 + DW_ATE_signed_char = 0x06 + DW_ATE_unsigned = 0x07 + DW_ATE_unsigned_char = 0x08 + DW_ATE_imaginary_float = 0x09 + DW_ATE_packed_decimal = 0x0a + DW_ATE_numeric_string = 0x0b + DW_ATE_edited = 0x0c + DW_ATE_signed_fixed = 0x0d + DW_ATE_unsigned_fixed = 0x0e + DW_ATE_decimal_float = 0x0f + DW_ATE_lo_user = 0x80 + DW_ATE_hi_user = 0xff +) + +// Table 26 +const ( + DW_DS_unsigned = 0x01 + DW_DS_leading_overpunch = 0x02 + DW_DS_trailing_overpunch = 0x03 + DW_DS_leading_separate = 0x04 + DW_DS_trailing_separate = 0x05 +) + +// Table 27 +const ( + DW_END_default = 0x00 + DW_END_big = 0x01 + DW_END_little = 0x02 + DW_END_lo_user = 0x40 + DW_END_hi_user = 0xff +) + +// Table 28 +const ( + DW_ACCESS_public = 0x01 + DW_ACCESS_protected = 0x02 + DW_ACCESS_private = 0x03 +) + +// Table 29 +const ( + DW_VIS_local = 0x01 + DW_VIS_exported = 0x02 + DW_VIS_qualified = 0x03 +) + +// Table 30 +const ( + DW_VIRTUALITY_none = 0x00 + DW_VIRTUALITY_virtual = 0x01 + DW_VIRTUALITY_pure_virtual = 0x02 +) + +// Table 31 +const ( + DW_LANG_C89 = 0x0001 + DW_LANG_C = 0x0002 + DW_LANG_Ada83 = 0x0003 + DW_LANG_C_plus_plus = 0x0004 + DW_LANG_Cobol74 = 0x0005 + DW_LANG_Cobol85 = 0x0006 + DW_LANG_Fortran77 = 0x0007 + DW_LANG_Fortran90 = 0x0008 + DW_LANG_Pascal83 = 0x0009 + DW_LANG_Modula2 = 0x000a + // Dwarf3 + DW_LANG_Java = 0x000b + DW_LANG_C99 = 0x000c + DW_LANG_Ada95 = 0x000d + DW_LANG_Fortran95 = 0x000e + DW_LANG_PLI = 0x000f + DW_LANG_ObjC = 0x0010 + DW_LANG_ObjC_plus_plus = 0x0011 + DW_LANG_UPC = 0x0012 + DW_LANG_D = 0x0013 + // Dwarf4 + DW_LANG_Python = 0x0014 + // Dwarf5 + DW_LANG_Go = 0x0016 + + DW_LANG_lo_user = 0x8000 + DW_LANG_hi_user = 0xffff +) + +// Table 32 +const ( + DW_ID_case_sensitive = 0x00 + DW_ID_up_case = 0x01 + DW_ID_down_case = 0x02 + DW_ID_case_insensitive = 0x03 +) + +// Table 33 +const ( + DW_CC_normal = 0x01 + DW_CC_program = 0x02 + DW_CC_nocall = 0x03 + DW_CC_lo_user = 0x40 + DW_CC_hi_user = 0xff +) + +// Table 34 +const ( + DW_INL_not_inlined = 0x00 + DW_INL_inlined = 0x01 + DW_INL_declared_not_inlined = 0x02 + DW_INL_declared_inlined = 0x03 +) + +// Table 35 +const ( + DW_ORD_row_major = 0x00 + DW_ORD_col_major = 0x01 +) + +// Table 36 +const ( + DW_DSC_label = 0x00 + DW_DSC_range = 0x01 +) + +// Table 37 +const ( + DW_LNS_copy = 0x01 + DW_LNS_advance_pc = 0x02 + DW_LNS_advance_line = 0x03 + DW_LNS_set_file = 0x04 + DW_LNS_set_column = 0x05 + DW_LNS_negate_stmt = 0x06 + DW_LNS_set_basic_block = 0x07 + DW_LNS_const_add_pc = 0x08 + DW_LNS_fixed_advance_pc = 0x09 + // Dwarf3 + DW_LNS_set_prologue_end = 0x0a + DW_LNS_set_epilogue_begin = 0x0b + DW_LNS_set_isa = 0x0c +) + +// Table 38 +const ( + DW_LNE_end_sequence = 0x01 + DW_LNE_set_address = 0x02 + DW_LNE_define_file = 0x03 + DW_LNE_lo_user = 0x80 + DW_LNE_hi_user = 0xff +) + +// Table 39 +const ( + DW_MACINFO_define = 0x01 + DW_MACINFO_undef = 0x02 + DW_MACINFO_start_file = 0x03 + DW_MACINFO_end_file = 0x04 + DW_MACINFO_vendor_ext = 0xff +) + +// Table 40. +const ( + // operand,... + DW_CFA_nop = 0x00 + DW_CFA_set_loc = 0x01 // address + DW_CFA_advance_loc1 = 0x02 // 1-byte delta + DW_CFA_advance_loc2 = 0x03 // 2-byte delta + DW_CFA_advance_loc4 = 0x04 // 4-byte delta + DW_CFA_offset_extended = 0x05 // ULEB128 register, ULEB128 offset + DW_CFA_restore_extended = 0x06 // ULEB128 register + DW_CFA_undefined = 0x07 // ULEB128 register + DW_CFA_same_value = 0x08 // ULEB128 register + DW_CFA_register = 0x09 // ULEB128 register, ULEB128 register + DW_CFA_remember_state = 0x0a + DW_CFA_restore_state = 0x0b + + DW_CFA_def_cfa = 0x0c // ULEB128 register, ULEB128 offset + DW_CFA_def_cfa_register = 0x0d // ULEB128 register + DW_CFA_def_cfa_offset = 0x0e // ULEB128 offset + DW_CFA_def_cfa_expression = 0x0f // BLOCK + DW_CFA_expression = 0x10 // ULEB128 register, BLOCK + DW_CFA_offset_extended_sf = 0x11 // ULEB128 register, SLEB128 offset + DW_CFA_def_cfa_sf = 0x12 // ULEB128 register, SLEB128 offset + DW_CFA_def_cfa_offset_sf = 0x13 // SLEB128 offset + DW_CFA_val_offset = 0x14 // ULEB128, ULEB128 + DW_CFA_val_offset_sf = 0x15 // ULEB128, SLEB128 + DW_CFA_val_expression = 0x16 // ULEB128, BLOCK + + DW_CFA_lo_user = 0x1c + DW_CFA_hi_user = 0x3f + + // Opcodes that take an addend operand. + DW_CFA_advance_loc = 0x1 << 6 // +delta + DW_CFA_offset = 0x2 << 6 // +register (ULEB128 offset) + DW_CFA_restore = 0x3 << 6 // +register +) diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go new file mode 100644 index 0000000000..b73a75b59b --- /dev/null +++ b/src/cmd/link/internal/ld/elf.go @@ -0,0 +1,2493 @@ +// Copyright 2009 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 ( + "cmd/internal/obj" + "encoding/binary" + "fmt" +) + +/* + * Derived from: + * $FreeBSD: src/sys/sys/elf32.h,v 1.8.14.1 2005/12/30 22:13:58 marcel Exp $ + * $FreeBSD: src/sys/sys/elf64.h,v 1.10.14.1 2005/12/30 22:13:58 marcel Exp $ + * $FreeBSD: src/sys/sys/elf_common.h,v 1.15.8.1 2005/12/30 22:13:58 marcel Exp $ + * $FreeBSD: src/sys/alpha/include/elf.h,v 1.14 2003/09/25 01:10:22 peter Exp $ + * $FreeBSD: src/sys/amd64/include/elf.h,v 1.18 2004/08/03 08:21:48 dfr Exp $ + * $FreeBSD: src/sys/arm/include/elf.h,v 1.5.2.1 2006/06/30 21:42:52 cognet Exp $ + * $FreeBSD: src/sys/i386/include/elf.h,v 1.16 2004/08/02 19:12:17 dfr Exp $ + * $FreeBSD: src/sys/powerpc/include/elf.h,v 1.7 2004/11/02 09:47:01 ssouhlal Exp $ + * $FreeBSD: src/sys/sparc64/include/elf.h,v 1.12 2003/09/25 01:10:26 peter Exp $ + * + * Copyright (c) 1996-1998 John D. Polstra. All rights reserved. + * Copyright (c) 2001 David E. O'Brien + * Portions Copyright 2009 The Go 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +/* + * ELF definitions that are independent of architecture or word size. + */ + +/* + * Note header. The ".note" section contains an array of notes. Each + * begins with this header, aligned to a word boundary. Immediately + * following the note header is n_namesz bytes of name, padded to the + * next word boundary. Then comes n_descsz bytes of descriptor, again + * padded to a word boundary. The values of n_namesz and n_descsz do + * not include the padding. + */ +type Elf_Note struct { + n_namesz uint32 + n_descsz uint32 + n_type uint32 +} + +const ( + EI_MAG0 = 0 + EI_MAG1 = 1 + EI_MAG2 = 2 + EI_MAG3 = 3 + EI_CLASS = 4 + EI_DATA = 5 + EI_VERSION = 6 + EI_OSABI = 7 + EI_ABIVERSION = 8 + OLD_EI_BRAND = 8 + EI_PAD = 9 + EI_NIDENT = 16 + ELFMAG0 = 0x7f + ELFMAG1 = 'E' + ELFMAG2 = 'L' + ELFMAG3 = 'F' + SELFMAG = 4 + EV_NONE = 0 + EV_CURRENT = 1 + ELFCLASSNONE = 0 + ELFCLASS32 = 1 + ELFCLASS64 = 2 + ELFDATANONE = 0 + ELFDATA2LSB = 1 + ELFDATA2MSB = 2 + ELFOSABI_NONE = 0 + ELFOSABI_HPUX = 1 + ELFOSABI_NETBSD = 2 + ELFOSABI_LINUX = 3 + ELFOSABI_HURD = 4 + ELFOSABI_86OPEN = 5 + ELFOSABI_SOLARIS = 6 + ELFOSABI_AIX = 7 + ELFOSABI_IRIX = 8 + ELFOSABI_FREEBSD = 9 + ELFOSABI_TRU64 = 10 + ELFOSABI_MODESTO = 11 + ELFOSABI_OPENBSD = 12 + ELFOSABI_OPENVMS = 13 + ELFOSABI_NSK = 14 + ELFOSABI_ARM = 97 + ELFOSABI_STANDALONE = 255 + ELFOSABI_SYSV = ELFOSABI_NONE + ELFOSABI_MONTEREY = ELFOSABI_AIX + ET_NONE = 0 + ET_REL = 1 + ET_EXEC = 2 + ET_DYN = 3 + ET_CORE = 4 + ET_LOOS = 0xfe00 + ET_HIOS = 0xfeff + ET_LOPROC = 0xff00 + ET_HIPROC = 0xffff + EM_NONE = 0 + EM_M32 = 1 + EM_SPARC = 2 + EM_386 = 3 + EM_68K = 4 + EM_88K = 5 + EM_860 = 7 + EM_MIPS = 8 + EM_S370 = 9 + EM_MIPS_RS3_LE = 10 + EM_PARISC = 15 + EM_VPP500 = 17 + EM_SPARC32PLUS = 18 + EM_960 = 19 + EM_PPC = 20 + EM_PPC64 = 21 + EM_S390 = 22 + EM_V800 = 36 + EM_FR20 = 37 + EM_RH32 = 38 + EM_RCE = 39 + EM_ARM = 40 + EM_SH = 42 + EM_SPARCV9 = 43 + EM_TRICORE = 44 + EM_ARC = 45 + EM_H8_300 = 46 + EM_H8_300H = 47 + EM_H8S = 48 + EM_H8_500 = 49 + EM_IA_64 = 50 + EM_MIPS_X = 51 + EM_COLDFIRE = 52 + EM_68HC12 = 53 + EM_MMA = 54 + EM_PCP = 55 + EM_NCPU = 56 + EM_NDR1 = 57 + EM_STARCORE = 58 + EM_ME16 = 59 + EM_ST100 = 60 + EM_TINYJ = 61 + EM_X86_64 = 62 + EM_AARCH64 = 183 + EM_486 = 6 + EM_MIPS_RS4_BE = 10 + EM_ALPHA_STD = 41 + EM_ALPHA = 0x9026 + SHN_UNDEF = 0 + SHN_LORESERVE = 0xff00 + SHN_LOPROC = 0xff00 + SHN_HIPROC = 0xff1f + SHN_LOOS = 0xff20 + SHN_HIOS = 0xff3f + SHN_ABS = 0xfff1 + SHN_COMMON = 0xfff2 + SHN_XINDEX = 0xffff + SHN_HIRESERVE = 0xffff + SHT_NULL = 0 + SHT_PROGBITS = 1 + SHT_SYMTAB = 2 + SHT_STRTAB = 3 + SHT_RELA = 4 + SHT_HASH = 5 + SHT_DYNAMIC = 6 + SHT_NOTE = 7 + SHT_NOBITS = 8 + SHT_REL = 9 + SHT_SHLIB = 10 + SHT_DYNSYM = 11 + SHT_INIT_ARRAY = 14 + SHT_FINI_ARRAY = 15 + SHT_PREINIT_ARRAY = 16 + SHT_GROUP = 17 + SHT_SYMTAB_SHNDX = 18 + SHT_LOOS = 0x60000000 + SHT_HIOS = 0x6fffffff + SHT_GNU_VERDEF = 0x6ffffffd + SHT_GNU_VERNEED = 0x6ffffffe + SHT_GNU_VERSYM = 0x6fffffff + SHT_LOPROC = 0x70000000 + SHT_HIPROC = 0x7fffffff + SHT_LOUSER = 0x80000000 + SHT_HIUSER = 0xffffffff + SHF_WRITE = 0x1 + SHF_ALLOC = 0x2 + SHF_EXECINSTR = 0x4 + SHF_MERGE = 0x10 + SHF_STRINGS = 0x20 + SHF_INFO_LINK = 0x40 + SHF_LINK_ORDER = 0x80 + SHF_OS_NONCONFORMING = 0x100 + SHF_GROUP = 0x200 + SHF_TLS = 0x400 + SHF_MASKOS = 0x0ff00000 + SHF_MASKPROC = 0xf0000000 + PT_NULL = 0 + PT_LOAD = 1 + PT_DYNAMIC = 2 + PT_INTERP = 3 + PT_NOTE = 4 + PT_SHLIB = 5 + PT_PHDR = 6 + PT_TLS = 7 + PT_LOOS = 0x60000000 + PT_HIOS = 0x6fffffff + PT_LOPROC = 0x70000000 + PT_HIPROC = 0x7fffffff + PT_GNU_STACK = 0x6474e551 + PT_PAX_FLAGS = 0x65041580 + PF_X = 0x1 + PF_W = 0x2 + PF_R = 0x4 + PF_MASKOS = 0x0ff00000 + PF_MASKPROC = 0xf0000000 + DT_NULL = 0 + DT_NEEDED = 1 + DT_PLTRELSZ = 2 + DT_PLTGOT = 3 + DT_HASH = 4 + DT_STRTAB = 5 + DT_SYMTAB = 6 + DT_RELA = 7 + DT_RELASZ = 8 + DT_RELAENT = 9 + DT_STRSZ = 10 + DT_SYMENT = 11 + DT_INIT = 12 + DT_FINI = 13 + DT_SONAME = 14 + DT_RPATH = 15 + DT_SYMBOLIC = 16 + DT_REL = 17 + DT_RELSZ = 18 + DT_RELENT = 19 + DT_PLTREL = 20 + DT_DEBUG = 21 + DT_TEXTREL = 22 + DT_JMPREL = 23 + DT_BIND_NOW = 24 + DT_INIT_ARRAY = 25 + DT_FINI_ARRAY = 26 + DT_INIT_ARRAYSZ = 27 + DT_FINI_ARRAYSZ = 28 + DT_RUNPATH = 29 + DT_FLAGS = 30 + DT_ENCODING = 32 + DT_PREINIT_ARRAY = 32 + DT_PREINIT_ARRAYSZ = 33 + DT_LOOS = 0x6000000d + DT_HIOS = 0x6ffff000 + DT_LOPROC = 0x70000000 + DT_HIPROC = 0x7fffffff + DT_VERNEED = 0x6ffffffe + DT_VERNEEDNUM = 0x6fffffff + DT_VERSYM = 0x6ffffff0 + DT_PPC64_GLINK = DT_LOPROC + 0 + DT_PPC64_OPT = DT_LOPROC + 3 + DF_ORIGIN = 0x0001 + DF_SYMBOLIC = 0x0002 + DF_TEXTREL = 0x0004 + DF_BIND_NOW = 0x0008 + DF_STATIC_TLS = 0x0010 + NT_PRSTATUS = 1 + NT_FPREGSET = 2 + NT_PRPSINFO = 3 + STB_LOCAL = 0 + STB_GLOBAL = 1 + STB_WEAK = 2 + STB_LOOS = 10 + STB_HIOS = 12 + STB_LOPROC = 13 + STB_HIPROC = 15 + STT_NOTYPE = 0 + STT_OBJECT = 1 + STT_FUNC = 2 + STT_SECTION = 3 + STT_FILE = 4 + STT_COMMON = 5 + STT_TLS = 6 + STT_LOOS = 10 + STT_HIOS = 12 + STT_LOPROC = 13 + STT_HIPROC = 15 + STV_DEFAULT = 0x0 + STV_INTERNAL = 0x1 + STV_HIDDEN = 0x2 + STV_PROTECTED = 0x3 + STN_UNDEF = 0 +) + +/* For accessing the fields of r_info. */ + +/* For constructing r_info from field values. */ + +/* + * Relocation types. + */ +const ( + R_X86_64_NONE = 0 + R_X86_64_64 = 1 + R_X86_64_PC32 = 2 + R_X86_64_GOT32 = 3 + R_X86_64_PLT32 = 4 + R_X86_64_COPY = 5 + R_X86_64_GLOB_DAT = 6 + R_X86_64_JMP_SLOT = 7 + R_X86_64_RELATIVE = 8 + R_X86_64_GOTPCREL = 9 + R_X86_64_32 = 10 + R_X86_64_32S = 11 + R_X86_64_16 = 12 + R_X86_64_PC16 = 13 + R_X86_64_8 = 14 + R_X86_64_PC8 = 15 + R_X86_64_DTPMOD64 = 16 + R_X86_64_DTPOFF64 = 17 + R_X86_64_TPOFF64 = 18 + R_X86_64_TLSGD = 19 + R_X86_64_TLSLD = 20 + R_X86_64_DTPOFF32 = 21 + R_X86_64_GOTTPOFF = 22 + R_X86_64_TPOFF32 = 23 + R_X86_64_COUNT = 24 + R_AARCH64_ABS64 = 257 + R_AARCH64_ABS32 = 258 + R_AARCH64_CALL26 = 283 + R_AARCH64_ADR_PREL_PG_HI21 = 275 + R_AARCH64_ADD_ABS_LO12_NC = 277 + R_ALPHA_NONE = 0 + R_ALPHA_REFLONG = 1 + R_ALPHA_REFQUAD = 2 + R_ALPHA_GPREL32 = 3 + R_ALPHA_LITERAL = 4 + R_ALPHA_LITUSE = 5 + R_ALPHA_GPDISP = 6 + R_ALPHA_BRADDR = 7 + R_ALPHA_HINT = 8 + R_ALPHA_SREL16 = 9 + R_ALPHA_SREL32 = 10 + R_ALPHA_SREL64 = 11 + R_ALPHA_OP_PUSH = 12 + R_ALPHA_OP_STORE = 13 + R_ALPHA_OP_PSUB = 14 + R_ALPHA_OP_PRSHIFT = 15 + R_ALPHA_GPVALUE = 16 + R_ALPHA_GPRELHIGH = 17 + R_ALPHA_GPRELLOW = 18 + R_ALPHA_IMMED_GP_16 = 19 + R_ALPHA_IMMED_GP_HI32 = 20 + R_ALPHA_IMMED_SCN_HI32 = 21 + R_ALPHA_IMMED_BR_HI32 = 22 + R_ALPHA_IMMED_LO32 = 23 + R_ALPHA_COPY = 24 + R_ALPHA_GLOB_DAT = 25 + R_ALPHA_JMP_SLOT = 26 + R_ALPHA_RELATIVE = 27 + R_ALPHA_COUNT = 28 + R_ARM_NONE = 0 + R_ARM_PC24 = 1 + R_ARM_ABS32 = 2 + R_ARM_REL32 = 3 + R_ARM_PC13 = 4 + R_ARM_ABS16 = 5 + R_ARM_ABS12 = 6 + R_ARM_THM_ABS5 = 7 + R_ARM_ABS8 = 8 + R_ARM_SBREL32 = 9 + R_ARM_THM_PC22 = 10 + R_ARM_THM_PC8 = 11 + R_ARM_AMP_VCALL9 = 12 + R_ARM_SWI24 = 13 + R_ARM_THM_SWI8 = 14 + R_ARM_XPC25 = 15 + R_ARM_THM_XPC22 = 16 + R_ARM_COPY = 20 + R_ARM_GLOB_DAT = 21 + R_ARM_JUMP_SLOT = 22 + R_ARM_RELATIVE = 23 + R_ARM_GOTOFF = 24 + R_ARM_GOTPC = 25 + R_ARM_GOT32 = 26 + R_ARM_PLT32 = 27 + R_ARM_CALL = 28 + R_ARM_JUMP24 = 29 + R_ARM_V4BX = 40 + R_ARM_GOT_PREL = 96 + R_ARM_GNU_VTENTRY = 100 + R_ARM_GNU_VTINHERIT = 101 + R_ARM_TLS_IE32 = 107 + R_ARM_TLS_LE32 = 108 + R_ARM_RSBREL32 = 250 + R_ARM_THM_RPC22 = 251 + R_ARM_RREL32 = 252 + R_ARM_RABS32 = 253 + R_ARM_RPC24 = 254 + R_ARM_RBASE = 255 + R_ARM_COUNT = 38 + R_386_NONE = 0 + R_386_32 = 1 + R_386_PC32 = 2 + R_386_GOT32 = 3 + R_386_PLT32 = 4 + R_386_COPY = 5 + R_386_GLOB_DAT = 6 + R_386_JMP_SLOT = 7 + R_386_RELATIVE = 8 + R_386_GOTOFF = 9 + R_386_GOTPC = 10 + R_386_TLS_TPOFF = 14 + R_386_TLS_IE = 15 + R_386_TLS_GOTIE = 16 + R_386_TLS_LE = 17 + R_386_TLS_GD = 18 + R_386_TLS_LDM = 19 + R_386_TLS_GD_32 = 24 + R_386_TLS_GD_PUSH = 25 + R_386_TLS_GD_CALL = 26 + R_386_TLS_GD_POP = 27 + R_386_TLS_LDM_32 = 28 + R_386_TLS_LDM_PUSH = 29 + R_386_TLS_LDM_CALL = 30 + R_386_TLS_LDM_POP = 31 + R_386_TLS_LDO_32 = 32 + R_386_TLS_IE_32 = 33 + R_386_TLS_LE_32 = 34 + R_386_TLS_DTPMOD32 = 35 + R_386_TLS_DTPOFF32 = 36 + R_386_TLS_TPOFF32 = 37 + R_386_COUNT = 38 + R_PPC_NONE = 0 + R_PPC_ADDR32 = 1 + R_PPC_ADDR24 = 2 + R_PPC_ADDR16 = 3 + R_PPC_ADDR16_LO = 4 + R_PPC_ADDR16_HI = 5 + R_PPC_ADDR16_HA = 6 + R_PPC_ADDR14 = 7 + R_PPC_ADDR14_BRTAKEN = 8 + R_PPC_ADDR14_BRNTAKEN = 9 + R_PPC_REL24 = 10 + R_PPC_REL14 = 11 + R_PPC_REL14_BRTAKEN = 12 + R_PPC_REL14_BRNTAKEN = 13 + R_PPC_GOT16 = 14 + R_PPC_GOT16_LO = 15 + R_PPC_GOT16_HI = 16 + R_PPC_GOT16_HA = 17 + R_PPC_PLTREL24 = 18 + R_PPC_COPY = 19 + R_PPC_GLOB_DAT = 20 + R_PPC_JMP_SLOT = 21 + R_PPC_RELATIVE = 22 + R_PPC_LOCAL24PC = 23 + R_PPC_UADDR32 = 24 + R_PPC_UADDR16 = 25 + R_PPC_REL32 = 26 + R_PPC_PLT32 = 27 + R_PPC_PLTREL32 = 28 + R_PPC_PLT16_LO = 29 + R_PPC_PLT16_HI = 30 + R_PPC_PLT16_HA = 31 + R_PPC_SDAREL16 = 32 + R_PPC_SECTOFF = 33 + R_PPC_SECTOFF_LO = 34 + R_PPC_SECTOFF_HI = 35 + R_PPC_SECTOFF_HA = 36 + R_PPC_COUNT = 37 + R_PPC_TLS = 67 + R_PPC_DTPMOD32 = 68 + R_PPC_TPREL16 = 69 + R_PPC_TPREL16_LO = 70 + R_PPC_TPREL16_HI = 71 + R_PPC_TPREL16_HA = 72 + R_PPC_TPREL32 = 73 + R_PPC_DTPREL16 = 74 + R_PPC_DTPREL16_LO = 75 + R_PPC_DTPREL16_HI = 76 + R_PPC_DTPREL16_HA = 77 + R_PPC_DTPREL32 = 78 + R_PPC_GOT_TLSGD16 = 79 + R_PPC_GOT_TLSGD16_LO = 80 + R_PPC_GOT_TLSGD16_HI = 81 + R_PPC_GOT_TLSGD16_HA = 82 + R_PPC_GOT_TLSLD16 = 83 + R_PPC_GOT_TLSLD16_LO = 84 + R_PPC_GOT_TLSLD16_HI = 85 + R_PPC_GOT_TLSLD16_HA = 86 + R_PPC_GOT_TPREL16 = 87 + R_PPC_GOT_TPREL16_LO = 88 + R_PPC_GOT_TPREL16_HI = 89 + R_PPC_GOT_TPREL16_HA = 90 + R_PPC_EMB_NADDR32 = 101 + R_PPC_EMB_NADDR16 = 102 + R_PPC_EMB_NADDR16_LO = 103 + R_PPC_EMB_NADDR16_HI = 104 + R_PPC_EMB_NADDR16_HA = 105 + R_PPC_EMB_SDAI16 = 106 + R_PPC_EMB_SDA2I16 = 107 + R_PPC_EMB_SDA2REL = 108 + R_PPC_EMB_SDA21 = 109 + R_PPC_EMB_MRKREF = 110 + R_PPC_EMB_RELSEC16 = 111 + R_PPC_EMB_RELST_LO = 112 + R_PPC_EMB_RELST_HI = 113 + R_PPC_EMB_RELST_HA = 114 + R_PPC_EMB_BIT_FLD = 115 + R_PPC_EMB_RELSDA = 116 + R_PPC_EMB_COUNT = R_PPC_EMB_RELSDA - R_PPC_EMB_NADDR32 + 1 + R_PPC64_REL24 = R_PPC_REL24 + R_PPC64_JMP_SLOT = R_PPC_JMP_SLOT + R_PPC64_ADDR64 = 38 + R_PPC64_TOC16 = 47 + R_PPC64_TOC16_LO = 48 + R_PPC64_TOC16_HI = 49 + R_PPC64_TOC16_HA = 50 + R_PPC64_TOC16_DS = 63 + R_PPC64_TOC16_LO_DS = 64 + R_PPC64_REL16_LO = 250 + R_PPC64_REL16_HI = 251 + R_PPC64_REL16_HA = 252 + R_SPARC_NONE = 0 + R_SPARC_8 = 1 + R_SPARC_16 = 2 + R_SPARC_32 = 3 + R_SPARC_DISP8 = 4 + R_SPARC_DISP16 = 5 + R_SPARC_DISP32 = 6 + R_SPARC_WDISP30 = 7 + R_SPARC_WDISP22 = 8 + R_SPARC_HI22 = 9 + R_SPARC_22 = 10 + R_SPARC_13 = 11 + R_SPARC_LO10 = 12 + R_SPARC_GOT10 = 13 + R_SPARC_GOT13 = 14 + R_SPARC_GOT22 = 15 + R_SPARC_PC10 = 16 + R_SPARC_PC22 = 17 + R_SPARC_WPLT30 = 18 + R_SPARC_COPY = 19 + R_SPARC_GLOB_DAT = 20 + R_SPARC_JMP_SLOT = 21 + R_SPARC_RELATIVE = 22 + R_SPARC_UA32 = 23 + R_SPARC_PLT32 = 24 + R_SPARC_HIPLT22 = 25 + R_SPARC_LOPLT10 = 26 + R_SPARC_PCPLT32 = 27 + R_SPARC_PCPLT22 = 28 + R_SPARC_PCPLT10 = 29 + R_SPARC_10 = 30 + R_SPARC_11 = 31 + R_SPARC_64 = 32 + R_SPARC_OLO10 = 33 + R_SPARC_HH22 = 34 + R_SPARC_HM10 = 35 + R_SPARC_LM22 = 36 + R_SPARC_PC_HH22 = 37 + R_SPARC_PC_HM10 = 38 + R_SPARC_PC_LM22 = 39 + R_SPARC_WDISP16 = 40 + R_SPARC_WDISP19 = 41 + R_SPARC_GLOB_JMP = 42 + R_SPARC_7 = 43 + R_SPARC_5 = 44 + R_SPARC_6 = 45 + R_SPARC_DISP64 = 46 + R_SPARC_PLT64 = 47 + R_SPARC_HIX22 = 48 + R_SPARC_LOX10 = 49 + R_SPARC_H44 = 50 + R_SPARC_M44 = 51 + R_SPARC_L44 = 52 + R_SPARC_REGISTER = 53 + R_SPARC_UA64 = 54 + R_SPARC_UA16 = 55 + ARM_MAGIC_TRAMP_NUMBER = 0x5c000003 +) + +/* + * Symbol table entries. + */ + +/* For accessing the fields of st_info. */ + +/* For constructing st_info from field values. */ + +/* For accessing the fields of st_other. */ + +/* + * ELF header. + */ +type ElfEhdr struct { + ident [EI_NIDENT]uint8 + type_ uint16 + machine uint16 + version uint32 + entry uint64 + phoff uint64 + shoff uint64 + flags uint32 + ehsize uint16 + phentsize uint16 + phnum uint16 + shentsize uint16 + shnum uint16 + shstrndx uint16 +} + +/* + * Section header. + */ +type ElfShdr struct { + name uint32 + type_ uint32 + flags uint64 + addr uint64 + off uint64 + size uint64 + link uint32 + info uint32 + addralign uint64 + entsize uint64 + shnum int + secsym *LSym +} + +/* + * Program header. + */ +type ElfPhdr struct { + type_ uint32 + flags uint32 + off uint64 + vaddr uint64 + paddr uint64 + filesz uint64 + memsz uint64 + align uint64 +} + +/* For accessing the fields of r_info. */ + +/* For constructing r_info from field values. */ + +/* + * Symbol table entries. + */ + +/* For accessing the fields of st_info. */ + +/* For constructing st_info from field values. */ + +/* For accessing the fields of st_other. */ + +/* + * Go linker interface + */ +const ( + ELF64HDRSIZE = 64 + ELF64PHDRSIZE = 56 + ELF64SHDRSIZE = 64 + ELF64RELSIZE = 16 + ELF64RELASIZE = 24 + ELF64SYMSIZE = 24 + ELF32HDRSIZE = 52 + ELF32PHDRSIZE = 32 + ELF32SHDRSIZE = 40 + ELF32SYMSIZE = 16 + ELF32RELSIZE = 8 +) + +/* + * The interface uses the 64-bit structures always, + * to avoid code duplication. The writers know how to + * marshal a 32-bit representation from the 64-bit structure. + */ + +var Elfstrdat []byte + +/* + * Total amount of space to reserve at the start of the file + * for Header, PHeaders, SHeaders, and interp. + * May waste some. + * On FreeBSD, cannot be larger than a page. + */ +const ( + ELFRESERVE = 3072 +) + +/* + * We use the 64-bit data structures on both 32- and 64-bit machines + * in order to write the code just once. The 64-bit data structure is + * written in the 32-bit format on the 32-bit machines. + */ +const ( + NSECT = 48 +) + +var Iself bool + +var Nelfsym int = 1 + +var elf64 bool + +var ehdr ElfEhdr + +var phdr [NSECT]*ElfPhdr + +var shdr [NSECT]*ElfShdr + +var interp string + +type Elfstring struct { + s string + off int +} + +var elfstr [100]Elfstring + +var nelfstr int + +var buildinfo []byte + +/* + Initialize the global variable that describes the ELF header. It will be updated as + we write section and prog headers. +*/ +func Elfinit() { + Iself = true + + switch Thearch.Thechar { + // 64-bit architectures + case '9': + if Ctxt.Arch.ByteOrder == binary.BigEndian { + ehdr.flags = 1 /* Version 1 ABI */ + } else { + ehdr.flags = 2 /* Version 2 ABI */ + } + fallthrough + + case '6', '7': + elf64 = true + + ehdr.phoff = ELF64HDRSIZE /* Must be be ELF64HDRSIZE: first PHdr must follow ELF header */ + ehdr.shoff = ELF64HDRSIZE /* Will move as we add PHeaders */ + ehdr.ehsize = ELF64HDRSIZE /* Must be ELF64HDRSIZE */ + ehdr.phentsize = ELF64PHDRSIZE /* Must be ELF64PHDRSIZE */ + ehdr.shentsize = ELF64SHDRSIZE /* Must be ELF64SHDRSIZE */ + + // we use EABI on both linux/arm and freebsd/arm. + // 32-bit architectures + case '5': + // we use EABI on both linux/arm and freebsd/arm. + if HEADTYPE == obj.Hlinux || HEADTYPE == obj.Hfreebsd { + ehdr.flags = 0x5000002 // has entry point, Version5 EABI + } + fallthrough + + default: + ehdr.phoff = ELF32HDRSIZE + /* Must be be ELF32HDRSIZE: first PHdr must follow ELF header */ + ehdr.shoff = ELF32HDRSIZE /* Will move as we add PHeaders */ + ehdr.ehsize = ELF32HDRSIZE /* Must be ELF32HDRSIZE */ + ehdr.phentsize = ELF32PHDRSIZE /* Must be ELF32PHDRSIZE */ + ehdr.shentsize = ELF32SHDRSIZE /* Must be ELF32SHDRSIZE */ + } +} + +func elf64phdr(e *ElfPhdr) { + Thearch.Lput(e.type_) + Thearch.Lput(e.flags) + Thearch.Vput(e.off) + Thearch.Vput(e.vaddr) + Thearch.Vput(e.paddr) + Thearch.Vput(e.filesz) + Thearch.Vput(e.memsz) + Thearch.Vput(e.align) +} + +func elf32phdr(e *ElfPhdr) { + if e.type_ == PT_LOAD { + // Correct ELF loaders will do this implicitly, + // but buggy ELF loaders like the one in some + // versions of QEMU won't. + frag := int(e.vaddr & (e.align - 1)) + + e.off -= uint64(frag) + e.vaddr -= uint64(frag) + e.paddr -= uint64(frag) + e.filesz += uint64(frag) + e.memsz += uint64(frag) + } + + Thearch.Lput(e.type_) + Thearch.Lput(uint32(e.off)) + Thearch.Lput(uint32(e.vaddr)) + Thearch.Lput(uint32(e.paddr)) + Thearch.Lput(uint32(e.filesz)) + Thearch.Lput(uint32(e.memsz)) + Thearch.Lput(e.flags) + Thearch.Lput(uint32(e.align)) +} + +func elf64shdr(e *ElfShdr) { + Thearch.Lput(e.name) + Thearch.Lput(e.type_) + Thearch.Vput(e.flags) + Thearch.Vput(e.addr) + Thearch.Vput(e.off) + Thearch.Vput(e.size) + Thearch.Lput(e.link) + Thearch.Lput(e.info) + Thearch.Vput(e.addralign) + Thearch.Vput(e.entsize) +} + +func elf32shdr(e *ElfShdr) { + Thearch.Lput(e.name) + Thearch.Lput(e.type_) + Thearch.Lput(uint32(e.flags)) + Thearch.Lput(uint32(e.addr)) + Thearch.Lput(uint32(e.off)) + Thearch.Lput(uint32(e.size)) + Thearch.Lput(e.link) + Thearch.Lput(e.info) + Thearch.Lput(uint32(e.addralign)) + Thearch.Lput(uint32(e.entsize)) +} + +func elfwriteshdrs() uint32 { + if elf64 { + for i := 0; i < int(ehdr.shnum); i++ { + elf64shdr(shdr[i]) + } + return uint32(ehdr.shnum) * ELF64SHDRSIZE + } + + for i := 0; i < int(ehdr.shnum); i++ { + elf32shdr(shdr[i]) + } + return uint32(ehdr.shnum) * ELF32SHDRSIZE +} + +func elfsetstring(s string, off int) { + if nelfstr >= len(elfstr) { + Diag("too many elf strings") + errorexit() + } + + elfstr[nelfstr].s = s + elfstr[nelfstr].off = off + nelfstr++ +} + +func elfwritephdrs() uint32 { + if elf64 { + for i := 0; i < int(ehdr.phnum); i++ { + elf64phdr(phdr[i]) + } + return uint32(ehdr.phnum) * ELF64PHDRSIZE + } + + for i := 0; i < int(ehdr.phnum); i++ { + elf32phdr(phdr[i]) + } + return uint32(ehdr.phnum) * ELF32PHDRSIZE +} + +func newElfPhdr() *ElfPhdr { + e := new(ElfPhdr) + if ehdr.phnum >= NSECT { + Diag("too many phdrs") + } else { + phdr[ehdr.phnum] = e + ehdr.phnum++ + } + if elf64 { + ehdr.shoff += ELF64PHDRSIZE + } else { + ehdr.shoff += ELF32PHDRSIZE + } + return e +} + +func newElfShdr(name int64) *ElfShdr { + e := new(ElfShdr) + e.name = uint32(name) + e.shnum = int(ehdr.shnum) + if ehdr.shnum >= NSECT { + Diag("too many shdrs") + } else { + shdr[ehdr.shnum] = e + ehdr.shnum++ + } + + return e +} + +func getElfEhdr() *ElfEhdr { + return &ehdr +} + +func elf64writehdr() uint32 { + for i := 0; i < EI_NIDENT; i++ { + Cput(ehdr.ident[i]) + } + Thearch.Wput(ehdr.type_) + Thearch.Wput(ehdr.machine) + Thearch.Lput(ehdr.version) + Thearch.Vput(ehdr.entry) + Thearch.Vput(ehdr.phoff) + Thearch.Vput(ehdr.shoff) + Thearch.Lput(ehdr.flags) + Thearch.Wput(ehdr.ehsize) + Thearch.Wput(ehdr.phentsize) + Thearch.Wput(ehdr.phnum) + Thearch.Wput(ehdr.shentsize) + Thearch.Wput(ehdr.shnum) + Thearch.Wput(ehdr.shstrndx) + return ELF64HDRSIZE +} + +func elf32writehdr() uint32 { + for i := 0; i < EI_NIDENT; i++ { + Cput(ehdr.ident[i]) + } + Thearch.Wput(ehdr.type_) + Thearch.Wput(ehdr.machine) + Thearch.Lput(ehdr.version) + Thearch.Lput(uint32(ehdr.entry)) + Thearch.Lput(uint32(ehdr.phoff)) + Thearch.Lput(uint32(ehdr.shoff)) + Thearch.Lput(ehdr.flags) + Thearch.Wput(ehdr.ehsize) + Thearch.Wput(ehdr.phentsize) + Thearch.Wput(ehdr.phnum) + Thearch.Wput(ehdr.shentsize) + Thearch.Wput(ehdr.shnum) + Thearch.Wput(ehdr.shstrndx) + return ELF32HDRSIZE +} + +func elfwritehdr() uint32 { + if elf64 { + return elf64writehdr() + } + return elf32writehdr() +} + +/* Taken directly from the definition document for ELF64 */ +func elfhash(name []byte) uint32 { + var h uint32 = 0 + var g uint32 + for len(name) != 0 { + h = (h << 4) + uint32(name[0]) + name = name[1:] + g = h & 0xf0000000 + if g != 0 { + h ^= g >> 24 + } + h &= 0x0fffffff + } + + return h +} + +func Elfwritedynent(s *LSym, tag int, val uint64) { + if elf64 { + Adduint64(Ctxt, s, uint64(tag)) + Adduint64(Ctxt, s, val) + } else { + Adduint32(Ctxt, s, uint32(tag)) + Adduint32(Ctxt, s, uint32(val)) + } +} + +func elfwritedynentsym(s *LSym, tag int, t *LSym) { + Elfwritedynentsymplus(s, tag, t, 0) +} + +func Elfwritedynentsymplus(s *LSym, tag int, t *LSym, add int64) { + if elf64 { + Adduint64(Ctxt, s, uint64(tag)) + } else { + Adduint32(Ctxt, s, uint32(tag)) + } + Addaddrplus(Ctxt, s, t, add) +} + +func elfwritedynentsymsize(s *LSym, tag int, t *LSym) { + if elf64 { + Adduint64(Ctxt, s, uint64(tag)) + } else { + Adduint32(Ctxt, s, uint32(tag)) + } + addsize(Ctxt, s, t) +} + +func elfinterp(sh *ElfShdr, startva uint64, resoff uint64, p string) int { + interp = p + n := len(interp) + 1 + sh.addr = startva + resoff - uint64(n) + sh.off = resoff - uint64(n) + sh.size = uint64(n) + + return n +} + +func elfwriteinterp() int { + sh := elfshname(".interp") + Cseek(int64(sh.off)) + coutbuf.WriteString(interp) + Cput(0) + return int(sh.size) +} + +func elfnote(sh *ElfShdr, startva uint64, resoff uint64, sz int, alloc bool) int { + n := 3*4 + uint64(sz) + resoff%4 + + sh.type_ = SHT_NOTE + if alloc { + sh.flags = SHF_ALLOC + } + sh.addralign = 4 + sh.addr = startva + resoff - n + sh.off = resoff - n + sh.size = n - resoff%4 + + return int(n) +} + +func elfwritenotehdr(str string, namesz uint32, descsz uint32, tag uint32) *ElfShdr { + sh := elfshname(str) + + // Write Elf_Note header. + Cseek(int64(sh.off)) + + Thearch.Lput(namesz) + Thearch.Lput(descsz) + Thearch.Lput(tag) + + return sh +} + +// NetBSD Signature (as per sys/exec_elf.h) +const ( + ELF_NOTE_NETBSD_NAMESZ = 7 + ELF_NOTE_NETBSD_DESCSZ = 4 + ELF_NOTE_NETBSD_TAG = 1 + ELF_NOTE_NETBSD_VERSION = 599000000 /* NetBSD 5.99 */ +) + +var ELF_NOTE_NETBSD_NAME = []byte("NetBSD\x00") + +func elfnetbsdsig(sh *ElfShdr, startva uint64, resoff uint64) int { + n := int(Rnd(ELF_NOTE_NETBSD_NAMESZ, 4) + Rnd(ELF_NOTE_NETBSD_DESCSZ, 4)) + return elfnote(sh, startva, resoff, n, true) +} + +func elfwritenetbsdsig() int { + // Write Elf_Note header. + sh := elfwritenotehdr(".note.netbsd.ident", ELF_NOTE_NETBSD_NAMESZ, ELF_NOTE_NETBSD_DESCSZ, ELF_NOTE_NETBSD_TAG) + + if sh == nil { + return 0 + } + + // Followed by NetBSD string and version. + Cwrite(ELF_NOTE_NETBSD_NAME) + Cput(0) + + Thearch.Lput(ELF_NOTE_NETBSD_VERSION) + + return int(sh.size) +} + +// OpenBSD Signature +const ( + ELF_NOTE_OPENBSD_NAMESZ = 8 + ELF_NOTE_OPENBSD_DESCSZ = 4 + ELF_NOTE_OPENBSD_TAG = 1 + ELF_NOTE_OPENBSD_VERSION = 0 +) + +var ELF_NOTE_OPENBSD_NAME = []byte("OpenBSD\x00") + +func elfopenbsdsig(sh *ElfShdr, startva uint64, resoff uint64) int { + n := ELF_NOTE_OPENBSD_NAMESZ + ELF_NOTE_OPENBSD_DESCSZ + return elfnote(sh, startva, resoff, n, true) +} + +func elfwriteopenbsdsig() int { + // Write Elf_Note header. + sh := elfwritenotehdr(".note.openbsd.ident", ELF_NOTE_OPENBSD_NAMESZ, ELF_NOTE_OPENBSD_DESCSZ, ELF_NOTE_OPENBSD_TAG) + + if sh == nil { + return 0 + } + + // Followed by OpenBSD string and version. + Cwrite(ELF_NOTE_OPENBSD_NAME) + + Thearch.Lput(ELF_NOTE_OPENBSD_VERSION) + + return int(sh.size) +} + +func addbuildinfo(val string) { + var j int + + if val[0] != '0' || val[1] != 'x' { + Exitf("-B argument must start with 0x: %s", val) + } + + ov := val + val = val[2:] + i := 0 + var b int + for val != "" { + if len(val) == 1 { + Exitf("-B argument must have even number of digits: %s", ov) + } + + b = 0 + for j = 0; j < 2; j, val = j+1, val[1:] { + b *= 16 + if val[0] >= '0' && val[0] <= '9' { + b += int(val[0]) - '0' + } else if val[0] >= 'a' && val[0] <= 'f' { + b += int(val[0]) - 'a' + 10 + } else if val[0] >= 'A' && val[0] <= 'F' { + b += int(val[0]) - 'A' + 10 + } else { + Exitf("-B argument contains invalid hex digit %c: %s", val[0], ov) + } + } + + const maxLen = 32 + if i >= maxLen { + Exitf("-B option too long (max %d digits): %s", maxLen, ov) + } + + buildinfo = append(buildinfo, uint8(b)) + i++ + } + + buildinfo = buildinfo[:i] +} + +// Build info note +const ( + ELF_NOTE_BUILDINFO_NAMESZ = 4 + ELF_NOTE_BUILDINFO_TAG = 3 +) + +var ELF_NOTE_BUILDINFO_NAME = []byte("GNU\x00") + +func elfbuildinfo(sh *ElfShdr, startva uint64, resoff uint64) int { + n := int(ELF_NOTE_BUILDINFO_NAMESZ + Rnd(int64(len(buildinfo)), 4)) + return elfnote(sh, startva, resoff, n, true) +} + +func elfwritebuildinfo() int { + sh := elfwritenotehdr(".note.gnu.build-id", ELF_NOTE_BUILDINFO_NAMESZ, uint32(len(buildinfo)), ELF_NOTE_BUILDINFO_TAG) + if sh == nil { + return 0 + } + + Cwrite(ELF_NOTE_BUILDINFO_NAME) + Cwrite(buildinfo) + var zero = make([]byte, 4) + Cwrite(zero[:int(Rnd(int64(len(buildinfo)), 4)-int64(len(buildinfo)))]) + + return int(sh.size) +} + +// Go package list note +const ( + ELF_NOTE_GOPKGLIST_TAG = 1 +) + +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 { + next *Elfaux + num int + vers string +} + +type Elflib struct { + next *Elflib + aux *Elfaux + file string +} + +func addelflib(list **Elflib, file string, vers string) *Elfaux { + var lib *Elflib + + for lib = *list; lib != nil; lib = lib.next { + if lib.file == file { + goto havelib + } + } + lib = new(Elflib) + lib.next = *list + lib.file = file + *list = lib + +havelib: + for aux := lib.aux; aux != nil; aux = aux.next { + if aux.vers == vers { + return aux + } + } + aux := new(Elfaux) + aux.next = lib.aux + aux.vers = vers + lib.aux = aux + + return aux +} + +func elfdynhash() { + if !Iself { + return + } + + nsym := Nelfsym + s := Linklookup(Ctxt, ".hash", 0) + s.Type = obj.SELFROSECT + s.Reachable = true + + i := nsym + nbucket := 1 + for i > 0 { + nbucket++ + i >>= 1 + } + + var needlib *Elflib + need := make([]*Elfaux, nsym) + chain := make([]uint32, nsym) + buckets := make([]uint32, nbucket) + + var b int + var hc uint32 + var name string + for sy := Ctxt.Allsym; sy != nil; sy = sy.Allsym { + if sy.Dynid <= 0 { + continue + } + + if sy.Dynimpvers != "" { + need[sy.Dynid] = addelflib(&needlib, sy.Dynimplib, sy.Dynimpvers) + } + + name = sy.Extname + hc = elfhash([]byte(name)) + + b = int(hc % uint32(nbucket)) + chain[sy.Dynid] = buckets[b] + buckets[b] = uint32(sy.Dynid) + } + + Adduint32(Ctxt, s, uint32(nbucket)) + Adduint32(Ctxt, s, uint32(nsym)) + for i := 0; i < nbucket; i++ { + Adduint32(Ctxt, s, buckets[i]) + } + for i := 0; i < nsym; i++ { + Adduint32(Ctxt, s, chain[i]) + } + + // version symbols + dynstr := Linklookup(Ctxt, ".dynstr", 0) + + s = Linklookup(Ctxt, ".gnu.version_r", 0) + i = 2 + nfile := 0 + var j int + var x *Elfaux + for l := needlib; l != nil; l = l.next { + nfile++ + + // header + Adduint16(Ctxt, s, 1) // table version + j = 0 + for x = l.aux; x != nil; x = x.next { + j++ + } + Adduint16(Ctxt, s, uint16(j)) // aux count + Adduint32(Ctxt, s, uint32(Addstring(dynstr, l.file))) // file string offset + Adduint32(Ctxt, s, 16) // offset from header to first aux + if l.next != nil { + Adduint32(Ctxt, s, 16+uint32(j)*16) // offset from this header to next + } else { + Adduint32(Ctxt, s, 0) + } + + for x = l.aux; x != nil; x = x.next { + x.num = i + i++ + + // aux struct + Adduint32(Ctxt, s, elfhash([]byte(x.vers))) // hash + Adduint16(Ctxt, s, 0) // flags + Adduint16(Ctxt, s, uint16(x.num)) // other - index we refer to this by + Adduint32(Ctxt, s, uint32(Addstring(dynstr, x.vers))) // version string offset + if x.next != nil { + Adduint32(Ctxt, s, 16) // offset from this aux to next + } else { + Adduint32(Ctxt, s, 0) + } + } + } + + // version references + s = Linklookup(Ctxt, ".gnu.version", 0) + + for i := 0; i < nsym; i++ { + if i == 0 { + Adduint16(Ctxt, s, 0) // first entry - no symbol + } else if need[i] == nil { + Adduint16(Ctxt, s, 1) // global + } else { + Adduint16(Ctxt, s, uint16(need[i].num)) + } + } + + s = Linklookup(Ctxt, ".dynamic", 0) + elfverneed = nfile + if elfverneed != 0 { + elfwritedynentsym(s, DT_VERNEED, Linklookup(Ctxt, ".gnu.version_r", 0)) + Elfwritedynent(s, DT_VERNEEDNUM, uint64(nfile)) + elfwritedynentsym(s, DT_VERSYM, Linklookup(Ctxt, ".gnu.version", 0)) + } + + switch Thearch.Thechar { + case '6', '7', '9': + sy := Linklookup(Ctxt, ".rela.plt", 0) + if sy.Size > 0 { + Elfwritedynent(s, DT_PLTREL, DT_RELA) + elfwritedynentsymsize(s, DT_PLTRELSZ, sy) + elfwritedynentsym(s, DT_JMPREL, sy) + } + default: + sy := Linklookup(Ctxt, ".rel.plt", 0) + if sy.Size > 0 { + Elfwritedynent(s, DT_PLTREL, DT_REL) + elfwritedynentsymsize(s, DT_PLTRELSZ, sy) + elfwritedynentsym(s, DT_JMPREL, sy) + } + } + + Elfwritedynent(s, DT_NULL, 0) +} + +func elfphload(seg *Segment) *ElfPhdr { + ph := newElfPhdr() + ph.type_ = PT_LOAD + if seg.Rwx&4 != 0 { + ph.flags |= PF_R + } + if seg.Rwx&2 != 0 { + ph.flags |= PF_W + } + if seg.Rwx&1 != 0 { + ph.flags |= PF_X + } + ph.vaddr = seg.Vaddr + ph.paddr = seg.Vaddr + ph.memsz = seg.Length + ph.off = seg.Fileoff + ph.filesz = seg.Filelen + ph.align = uint64(INITRND) + + return ph +} + +func elfshname(name string) *ElfShdr { + var off int + var sh *ElfShdr + + for i := 0; i < nelfstr; i++ { + if name == elfstr[i].s { + off = elfstr[i].off + for i = 0; i < int(ehdr.shnum); i++ { + sh = shdr[i] + if sh.name == uint32(off) { + return sh + } + } + + sh = newElfShdr(int64(off)) + return sh + } + } + + Diag("cannot find elf name %s", name) + errorexit() + return nil +} + +func elfshalloc(sect *Section) *ElfShdr { + sh := elfshname(sect.Name) + sect.Elfsect = sh + return sh +} + +func elfshbits(sect *Section) *ElfShdr { + sh := elfshalloc(sect) + if sh.type_ > 0 { + return sh + } + + if sect.Vaddr < sect.Seg.Vaddr+sect.Seg.Filelen { + sh.type_ = SHT_PROGBITS + } else { + sh.type_ = SHT_NOBITS + } + sh.flags = SHF_ALLOC + if sect.Rwx&1 != 0 { + sh.flags |= SHF_EXECINSTR + } + if sect.Rwx&2 != 0 { + sh.flags |= SHF_WRITE + } + if sect.Name == ".tbss" { + if goos != "android" { + sh.flags |= SHF_TLS // no TLS on android + } + sh.type_ = SHT_NOBITS + } + + if Linkmode != LinkExternal { + sh.addr = sect.Vaddr + } + sh.addralign = uint64(sect.Align) + sh.size = sect.Length + sh.off = sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr + + return sh +} + +func elfshreloc(sect *Section) *ElfShdr { + // If main section is SHT_NOBITS, nothing to relocate. + // Also nothing to relocate in .shstrtab. + if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { + return nil + } + if sect.Name == ".shstrtab" || sect.Name == ".tbss" { + return nil + } + + var prefix string + var typ int + switch Thearch.Thechar { + case '6', '7', '9': + prefix = ".rela" + typ = SHT_RELA + default: + prefix = ".rel" + typ = SHT_REL + } + + buf := fmt.Sprintf("%s%s", prefix, sect.Name) + sh := elfshname(buf) + sh.type_ = uint32(typ) + sh.entsize = uint64(Thearch.Regsize) * 2 + if typ == SHT_RELA { + sh.entsize += uint64(Thearch.Regsize) + } + sh.link = uint32(elfshname(".symtab").shnum) + sh.info = uint32((sect.Elfsect.(*ElfShdr)).shnum) + sh.off = sect.Reloff + sh.size = sect.Rellen + sh.addralign = uint64(Thearch.Regsize) + return sh +} + +func elfrelocsect(sect *Section, first *LSym) { + // If main section is SHT_NOBITS, nothing to relocate. + // Also nothing to relocate in .shstrtab. + if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { + return + } + if sect.Name == ".shstrtab" { + return + } + + sect.Reloff = uint64(Cpos()) + var sym *LSym + for sym = first; sym != nil; sym = sym.Next { + if !sym.Reachable { + continue + } + if uint64(sym.Value) >= sect.Vaddr { + break + } + } + + eaddr := int32(sect.Vaddr + sect.Length) + var r *Reloc + var ri int + for ; sym != nil; sym = sym.Next { + if !sym.Reachable { + continue + } + if sym.Value >= int64(eaddr) { + break + } + Ctxt.Cursym = sym + + for ri = 0; ri < len(sym.R); ri++ { + r = &sym.R[ri] + if r.Done != 0 { + continue + } + if r.Xsym == nil { + Diag("missing xsym in relocation") + continue + } + + if r.Xsym.Elfsym == 0 { + Diag("reloc %d to non-elf symbol %s (outer=%s) %d", r.Type, r.Sym.Name, r.Xsym.Name, r.Sym.Type) + } + if Thearch.Elfreloc1(r, int64(uint64(sym.Value+int64(r.Off))-sect.Vaddr)) < 0 { + Diag("unsupported obj reloc %d/%d to %s", r.Type, r.Siz, r.Sym.Name) + } + } + } + + sect.Rellen = uint64(Cpos()) - sect.Reloff +} + +func Elfemitreloc() { + for Cpos()&7 != 0 { + Cput(0) + } + + elfrelocsect(Segtext.Sect, Ctxt.Textp) + for sect := Segtext.Sect.Next; sect != nil; sect = sect.Next { + elfrelocsect(sect, datap) + } + for sect := Segrodata.Sect; sect != nil; sect = sect.Next { + elfrelocsect(sect, datap) + } + for sect := Segdata.Sect; sect != nil; sect = sect.Next { + elfrelocsect(sect, datap) + } +} + +func doelf() { + if !Iself { + return + } + + /* predefine strings we need for section headers */ + shstrtab := Linklookup(Ctxt, ".shstrtab", 0) + + shstrtab.Type = obj.SELFROSECT + shstrtab.Reachable = true + + Addstring(shstrtab, "") + Addstring(shstrtab, ".text") + Addstring(shstrtab, ".noptrdata") + Addstring(shstrtab, ".data") + Addstring(shstrtab, ".bss") + Addstring(shstrtab, ".noptrbss") + + // generate .tbss section (except for OpenBSD where it's not supported) + // for dynamic internal linker or external linking, so that various + // binutils could correctly calculate PT_TLS size. + // see http://golang.org/issue/5200. + if HEADTYPE != obj.Hopenbsd { + if Debug['d'] == 0 || Linkmode == LinkExternal { + Addstring(shstrtab, ".tbss") + } + } + if HEADTYPE == obj.Hnetbsd { + Addstring(shstrtab, ".note.netbsd.ident") + } + if HEADTYPE == obj.Hopenbsd { + Addstring(shstrtab, ".note.openbsd.ident") + } + 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") + Addstring(shstrtab, ".gosymtab") + Addstring(shstrtab, ".gopclntab") + + if Linkmode == LinkExternal { + debug_s = Debug['s'] + Debug['s'] = 0 + Debug['d'] = 1 + + switch Thearch.Thechar { + case '6', '7', '9': + Addstring(shstrtab, ".rela.text") + Addstring(shstrtab, ".rela.rodata") + Addstring(shstrtab, ".rela.typelink") + Addstring(shstrtab, ".rela.gosymtab") + Addstring(shstrtab, ".rela.gopclntab") + Addstring(shstrtab, ".rela.noptrdata") + Addstring(shstrtab, ".rela.data") + + default: + Addstring(shstrtab, ".rel.text") + Addstring(shstrtab, ".rel.rodata") + Addstring(shstrtab, ".rel.typelink") + Addstring(shstrtab, ".rel.gosymtab") + Addstring(shstrtab, ".rel.gopclntab") + Addstring(shstrtab, ".rel.noptrdata") + Addstring(shstrtab, ".rel.data") + } + + // add a .note.GNU-stack section to mark the stack as non-executable + Addstring(shstrtab, ".note.GNU-stack") + } + + hasinitarr := Linkshared + + /* shared library initializer */ + switch Buildmode { + case BuildmodeCArchive, BuildmodeCShared, BuildmodeShared: + hasinitarr = true + } + + if hasinitarr { + Addstring(shstrtab, ".init_array") + switch Thearch.Thechar { + case '6', '7', '9': + Addstring(shstrtab, ".rela.init_array") + default: + Addstring(shstrtab, ".rel.init_array") + } + } + + if Debug['s'] == 0 { + Addstring(shstrtab, ".symtab") + Addstring(shstrtab, ".strtab") + dwarfaddshstrings(shstrtab) + } + + Addstring(shstrtab, ".shstrtab") + + if Debug['d'] == 0 { /* -d suppresses dynamic loader format */ + Addstring(shstrtab, ".interp") + Addstring(shstrtab, ".hash") + Addstring(shstrtab, ".got") + if Thearch.Thechar == '9' { + Addstring(shstrtab, ".glink") + } + Addstring(shstrtab, ".got.plt") + Addstring(shstrtab, ".dynamic") + Addstring(shstrtab, ".dynsym") + Addstring(shstrtab, ".dynstr") + switch Thearch.Thechar { + case '6', '7', '9': + Addstring(shstrtab, ".rela") + Addstring(shstrtab, ".rela.plt") + default: + Addstring(shstrtab, ".rel") + Addstring(shstrtab, ".rel.plt") + } + + Addstring(shstrtab, ".plt") + Addstring(shstrtab, ".gnu.version") + Addstring(shstrtab, ".gnu.version_r") + + /* dynamic symbol table - first entry all zeros */ + s := Linklookup(Ctxt, ".dynsym", 0) + + s.Type = obj.SELFROSECT + s.Reachable = true + switch Thearch.Thechar { + case '6', '7', '9': + s.Size += ELF64SYMSIZE + default: + s.Size += ELF32SYMSIZE + } + + /* dynamic string table */ + s = Linklookup(Ctxt, ".dynstr", 0) + + s.Type = obj.SELFROSECT + s.Reachable = true + if s.Size == 0 { + Addstring(s, "") + } + dynstr := s + + /* relocation table */ + switch Thearch.Thechar { + case '6', '7', '9': + s = Linklookup(Ctxt, ".rela", 0) + default: + s = Linklookup(Ctxt, ".rel", 0) + } + s.Reachable = true + s.Type = obj.SELFROSECT + + /* global offset table */ + s = Linklookup(Ctxt, ".got", 0) + + s.Reachable = true + s.Type = obj.SELFGOT // writable + + /* ppc64 glink resolver */ + if Thearch.Thechar == '9' { + s := Linklookup(Ctxt, ".glink", 0) + s.Reachable = true + s.Type = obj.SELFRXSECT + } + + /* hash */ + s = Linklookup(Ctxt, ".hash", 0) + + s.Reachable = true + s.Type = obj.SELFROSECT + + s = Linklookup(Ctxt, ".got.plt", 0) + s.Reachable = true + s.Type = obj.SELFSECT // writable + + s = Linklookup(Ctxt, ".plt", 0) + + s.Reachable = true + if Thearch.Thechar == '9' { + // In the ppc64 ABI, .plt is a data section + // written by the dynamic linker. + s.Type = obj.SELFSECT + } else { + s.Type = obj.SELFRXSECT + } + + Thearch.Elfsetupplt() + + switch Thearch.Thechar { + case '6', '7', '9': + s = Linklookup(Ctxt, ".rela.plt", 0) + default: + s = Linklookup(Ctxt, ".rel.plt", 0) + } + s.Reachable = true + s.Type = obj.SELFROSECT + + s = Linklookup(Ctxt, ".gnu.version", 0) + s.Reachable = true + s.Type = obj.SELFROSECT + + s = Linklookup(Ctxt, ".gnu.version_r", 0) + s.Reachable = true + s.Type = obj.SELFROSECT + + /* define dynamic elf table */ + s = Linklookup(Ctxt, ".dynamic", 0) + + s.Reachable = true + s.Type = obj.SELFSECT // writable + + /* + * .dynamic table + */ + elfwritedynentsym(s, DT_HASH, Linklookup(Ctxt, ".hash", 0)) + + elfwritedynentsym(s, DT_SYMTAB, Linklookup(Ctxt, ".dynsym", 0)) + switch Thearch.Thechar { + case '6', '7', '9': + Elfwritedynent(s, DT_SYMENT, ELF64SYMSIZE) + default: + Elfwritedynent(s, DT_SYMENT, ELF32SYMSIZE) + } + elfwritedynentsym(s, DT_STRTAB, Linklookup(Ctxt, ".dynstr", 0)) + elfwritedynentsymsize(s, DT_STRSZ, Linklookup(Ctxt, ".dynstr", 0)) + switch Thearch.Thechar { + case '6', '7', '9': + elfwritedynentsym(s, DT_RELA, Linklookup(Ctxt, ".rela", 0)) + elfwritedynentsymsize(s, DT_RELASZ, Linklookup(Ctxt, ".rela", 0)) + Elfwritedynent(s, DT_RELAENT, ELF64RELASIZE) + default: + elfwritedynentsym(s, DT_REL, Linklookup(Ctxt, ".rel", 0)) + elfwritedynentsymsize(s, DT_RELSZ, Linklookup(Ctxt, ".rel", 0)) + Elfwritedynent(s, DT_RELENT, ELF32RELSIZE) + } + + if rpath.val != "" { + Elfwritedynent(s, DT_RUNPATH, uint64(Addstring(dynstr, rpath.val))) + } + + if Thearch.Thechar == '9' { + elfwritedynentsym(s, DT_PLTGOT, Linklookup(Ctxt, ".plt", 0)) + } else { + elfwritedynentsym(s, DT_PLTGOT, Linklookup(Ctxt, ".got.plt", 0)) + } + + if Thearch.Thechar == '9' { + Elfwritedynent(s, DT_PPC64_OPT, 0) + } + + // Solaris dynamic linker can't handle an empty .rela.plt if + // DT_JMPREL is emitted so we have to defer generation of DT_PLTREL, + // DT_PLTRELSZ, and DT_JMPREL dynamic entries until after we know the + // size of .rel(a).plt section. + Elfwritedynent(s, DT_DEBUG, 0) + } +} + +// Do not write DT_NULL. elfdynhash will finish it. +func shsym(sh *ElfShdr, s *LSym) { + addr := Symaddr(s) + if sh.flags&SHF_ALLOC != 0 { + sh.addr = uint64(addr) + } + sh.off = uint64(datoff(addr)) + sh.size = uint64(s.Size) +} + +func phsh(ph *ElfPhdr, sh *ElfShdr) { + ph.vaddr = sh.addr + ph.paddr = ph.vaddr + ph.off = sh.off + ph.filesz = sh.size + ph.memsz = sh.size + ph.align = sh.addralign +} + +func Asmbelfsetup() { + /* This null SHdr must appear before all others */ + elfshname("") + + for sect := Segtext.Sect; sect != nil; sect = sect.Next { + elfshalloc(sect) + } + for sect := Segrodata.Sect; sect != nil; sect = sect.Next { + elfshalloc(sect) + } + for sect := Segdata.Sect; sect != nil; sect = sect.Next { + elfshalloc(sect) + } +} + +func Asmbelf(symo int64) { + eh := getElfEhdr() + switch Thearch.Thechar { + default: + Exitf("unknown architecture in asmbelf: %v", Thearch.Thechar) + case '5': + eh.machine = EM_ARM + case '6': + eh.machine = EM_X86_64 + case '7': + eh.machine = EM_AARCH64 + case '8': + eh.machine = EM_386 + case '9': + eh.machine = EM_PPC64 + } + + elfreserve := int64(ELFRESERVE) + startva := INITTEXT - int64(HEADR) + resoff := elfreserve + + var pph *ElfPhdr + var pnote *ElfPhdr + if Linkmode == LinkExternal { + /* skip program headers */ + eh.phoff = 0 + + 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))) + } + goto elfobj + } + + /* program header info */ + pph = newElfPhdr() + + pph.type_ = PT_PHDR + pph.flags = PF_R + pph.off = uint64(eh.ehsize) + pph.vaddr = uint64(INITTEXT) - uint64(HEADR) + pph.off + pph.paddr = uint64(INITTEXT) - uint64(HEADR) + pph.off + pph.align = uint64(INITRND) + + /* + * PHDR must be in a loaded segment. Adjust the text + * segment boundaries downwards to include it. + * Except on NaCl where it must not be loaded. + */ + if HEADTYPE != obj.Hnacl { + o := int64(Segtext.Vaddr - pph.vaddr) + Segtext.Vaddr -= uint64(o) + Segtext.Length += uint64(o) + o = int64(Segtext.Fileoff - pph.off) + Segtext.Fileoff -= uint64(o) + Segtext.Filelen += uint64(o) + } + + if Debug['d'] == 0 { /* -d suppresses dynamic loader format */ + /* interpreter */ + sh := elfshname(".interp") + + sh.type_ = SHT_PROGBITS + sh.flags = SHF_ALLOC + sh.addralign = 1 + if interpreter == "" { + switch HEADTYPE { + case obj.Hlinux: + interpreter = Thearch.Linuxdynld + + case obj.Hfreebsd: + interpreter = Thearch.Freebsddynld + + case obj.Hnetbsd: + interpreter = Thearch.Netbsddynld + + case obj.Hopenbsd: + interpreter = Thearch.Openbsddynld + + case obj.Hdragonfly: + interpreter = Thearch.Dragonflydynld + + case obj.Hsolaris: + interpreter = Thearch.Solarisdynld + } + } + + resoff -= int64(elfinterp(sh, uint64(startva), uint64(resoff), interpreter)) + + ph := newElfPhdr() + ph.type_ = PT_INTERP + ph.flags = PF_R + phsh(ph, sh) + } + + pnote = nil + if HEADTYPE == obj.Hnetbsd || HEADTYPE == obj.Hopenbsd { + var sh *ElfShdr + switch HEADTYPE { + case obj.Hnetbsd: + sh = elfshname(".note.netbsd.ident") + resoff -= int64(elfnetbsdsig(sh, uint64(startva), uint64(resoff))) + + case obj.Hopenbsd: + sh = elfshname(".note.openbsd.ident") + resoff -= int64(elfopenbsdsig(sh, uint64(startva), uint64(resoff))) + } + + pnote = newElfPhdr() + pnote.type_ = PT_NOTE + pnote.flags = PF_R + phsh(pnote, sh) + } + + if len(buildinfo) > 0 { + sh := elfshname(".note.gnu.build-id") + resoff -= int64(elfbuildinfo(sh, uint64(startva), uint64(resoff))) + + if pnote == nil { + pnote = newElfPhdr() + pnote.type_ = PT_NOTE + pnote.flags = PF_R + } + + phsh(pnote, sh) + } + + // Additions to the reserved area must be above this line. + + elfphload(&Segtext) + if Segrodata.Sect != nil { + elfphload(&Segrodata) + } + elfphload(&Segdata) + + /* Dynamic linking sections */ + if Debug['d'] == 0 { + sh := elfshname(".dynsym") + sh.type_ = SHT_DYNSYM + sh.flags = SHF_ALLOC + if elf64 { + sh.entsize = ELF64SYMSIZE + } else { + sh.entsize = ELF32SYMSIZE + } + sh.addralign = uint64(Thearch.Regsize) + sh.link = uint32(elfshname(".dynstr").shnum) + + // sh->info = index of first non-local symbol (number of local symbols) + shsym(sh, Linklookup(Ctxt, ".dynsym", 0)) + + sh = elfshname(".dynstr") + sh.type_ = SHT_STRTAB + sh.flags = SHF_ALLOC + sh.addralign = 1 + shsym(sh, Linklookup(Ctxt, ".dynstr", 0)) + + if elfverneed != 0 { + sh := elfshname(".gnu.version") + sh.type_ = SHT_GNU_VERSYM + sh.flags = SHF_ALLOC + sh.addralign = 2 + sh.link = uint32(elfshname(".dynsym").shnum) + sh.entsize = 2 + shsym(sh, Linklookup(Ctxt, ".gnu.version", 0)) + + sh = elfshname(".gnu.version_r") + sh.type_ = SHT_GNU_VERNEED + sh.flags = SHF_ALLOC + sh.addralign = uint64(Thearch.Regsize) + sh.info = uint32(elfverneed) + sh.link = uint32(elfshname(".dynstr").shnum) + shsym(sh, Linklookup(Ctxt, ".gnu.version_r", 0)) + } + + switch eh.machine { + case EM_X86_64, EM_PPC64, EM_AARCH64: + sh := elfshname(".rela.plt") + sh.type_ = SHT_RELA + sh.flags = SHF_ALLOC + sh.entsize = ELF64RELASIZE + sh.addralign = uint64(Thearch.Regsize) + sh.link = uint32(elfshname(".dynsym").shnum) + sh.info = uint32(elfshname(".plt").shnum) + shsym(sh, Linklookup(Ctxt, ".rela.plt", 0)) + + sh = elfshname(".rela") + sh.type_ = SHT_RELA + sh.flags = SHF_ALLOC + sh.entsize = ELF64RELASIZE + sh.addralign = 8 + sh.link = uint32(elfshname(".dynsym").shnum) + shsym(sh, Linklookup(Ctxt, ".rela", 0)) + + default: + sh := elfshname(".rel.plt") + sh.type_ = SHT_REL + sh.flags = SHF_ALLOC + sh.entsize = ELF32RELSIZE + sh.addralign = 4 + sh.link = uint32(elfshname(".dynsym").shnum) + shsym(sh, Linklookup(Ctxt, ".rel.plt", 0)) + + sh = elfshname(".rel") + sh.type_ = SHT_REL + sh.flags = SHF_ALLOC + sh.entsize = ELF32RELSIZE + sh.addralign = 4 + sh.link = uint32(elfshname(".dynsym").shnum) + shsym(sh, Linklookup(Ctxt, ".rel", 0)) + } + + if eh.machine == EM_PPC64 { + sh := elfshname(".glink") + sh.type_ = SHT_PROGBITS + sh.flags = SHF_ALLOC + SHF_EXECINSTR + sh.addralign = 4 + shsym(sh, Linklookup(Ctxt, ".glink", 0)) + } + + sh = elfshname(".plt") + sh.type_ = SHT_PROGBITS + sh.flags = SHF_ALLOC + SHF_EXECINSTR + if eh.machine == EM_X86_64 { + sh.entsize = 16 + } else if eh.machine == EM_PPC64 { + // On ppc64, this is just a table of addresses + // filled by the dynamic linker + sh.type_ = SHT_NOBITS + + sh.flags = SHF_ALLOC + SHF_WRITE + sh.entsize = 8 + } else { + sh.entsize = 4 + } + sh.addralign = sh.entsize + shsym(sh, Linklookup(Ctxt, ".plt", 0)) + + // On ppc64, .got comes from the input files, so don't + // create it here, and .got.plt is not used. + if eh.machine != EM_PPC64 { + sh := elfshname(".got") + sh.type_ = SHT_PROGBITS + sh.flags = SHF_ALLOC + SHF_WRITE + sh.entsize = uint64(Thearch.Regsize) + sh.addralign = uint64(Thearch.Regsize) + shsym(sh, Linklookup(Ctxt, ".got", 0)) + + sh = elfshname(".got.plt") + sh.type_ = SHT_PROGBITS + sh.flags = SHF_ALLOC + SHF_WRITE + sh.entsize = uint64(Thearch.Regsize) + sh.addralign = uint64(Thearch.Regsize) + shsym(sh, Linklookup(Ctxt, ".got.plt", 0)) + } + + sh = elfshname(".hash") + sh.type_ = SHT_HASH + sh.flags = SHF_ALLOC + sh.entsize = 4 + sh.addralign = uint64(Thearch.Regsize) + sh.link = uint32(elfshname(".dynsym").shnum) + shsym(sh, Linklookup(Ctxt, ".hash", 0)) + + /* sh and PT_DYNAMIC for .dynamic section */ + sh = elfshname(".dynamic") + + sh.type_ = SHT_DYNAMIC + sh.flags = SHF_ALLOC + SHF_WRITE + sh.entsize = 2 * uint64(Thearch.Regsize) + sh.addralign = uint64(Thearch.Regsize) + sh.link = uint32(elfshname(".dynstr").shnum) + shsym(sh, Linklookup(Ctxt, ".dynamic", 0)) + ph := newElfPhdr() + ph.type_ = PT_DYNAMIC + ph.flags = PF_R + PF_W + phsh(ph, sh) + + /* + * Thread-local storage segment (really just size). + */ + // Do not emit PT_TLS for OpenBSD since ld.so(1) does + // not currently support it. This is handled + // appropriately in runtime/cgo. + if Ctxt.Tlsoffset != 0 && HEADTYPE != obj.Hopenbsd { + ph := newElfPhdr() + ph.type_ = PT_TLS + ph.flags = PF_R + ph.memsz = uint64(-Ctxt.Tlsoffset) + ph.align = uint64(Thearch.Regsize) + } + } + + if HEADTYPE == obj.Hlinux { + ph := newElfPhdr() + ph.type_ = PT_GNU_STACK + ph.flags = PF_W + PF_R + ph.align = uint64(Thearch.Regsize) + + ph = newElfPhdr() + ph.type_ = PT_PAX_FLAGS + ph.flags = 0x2a00 // mprotect, randexec, emutramp disabled + ph.align = uint64(Thearch.Regsize) + } + +elfobj: + sh := elfshname(".shstrtab") + sh.type_ = SHT_STRTAB + sh.addralign = 1 + shsym(sh, Linklookup(Ctxt, ".shstrtab", 0)) + eh.shstrndx = uint16(sh.shnum) + + // put these sections early in the list + if Debug['s'] == 0 { + elfshname(".symtab") + elfshname(".strtab") + } + + for sect := Segtext.Sect; sect != nil; sect = sect.Next { + elfshbits(sect) + } + for sect := Segrodata.Sect; sect != nil; sect = sect.Next { + elfshbits(sect) + } + for sect := Segdata.Sect; sect != nil; sect = sect.Next { + elfshbits(sect) + } + + if Linkmode == LinkExternal { + for sect := Segtext.Sect; sect != nil; sect = sect.Next { + elfshreloc(sect) + } + for sect := Segrodata.Sect; sect != nil; sect = sect.Next { + elfshreloc(sect) + } + for sect := Segdata.Sect; sect != nil; sect = sect.Next { + elfshreloc(sect) + } + + // add a .note.GNU-stack section to mark the stack as non-executable + sh := elfshname(".note.GNU-stack") + + sh.type_ = SHT_PROGBITS + sh.addralign = 1 + sh.flags = 0 + } + + // generate .tbss section for dynamic internal linking (except for OpenBSD) + // external linking generates .tbss in data.c + if Linkmode == LinkInternal && Debug['d'] == 0 && HEADTYPE != obj.Hopenbsd { + sh := elfshname(".tbss") + sh.type_ = SHT_NOBITS + sh.addralign = uint64(Thearch.Regsize) + sh.size = uint64(-Ctxt.Tlsoffset) + sh.flags = SHF_ALLOC | SHF_TLS | SHF_WRITE + } + + if Debug['s'] == 0 { + sh := elfshname(".symtab") + sh.type_ = SHT_SYMTAB + sh.off = uint64(symo) + sh.size = uint64(Symsize) + sh.addralign = uint64(Thearch.Regsize) + sh.entsize = 8 + 2*uint64(Thearch.Regsize) + sh.link = uint32(elfshname(".strtab").shnum) + sh.info = uint32(elfglobalsymndx) + + sh = elfshname(".strtab") + sh.type_ = SHT_STRTAB + sh.off = uint64(symo) + uint64(Symsize) + sh.size = uint64(len(Elfstrdat)) + sh.addralign = 1 + + dwarfaddelfheaders() + } + + /* Main header */ + eh.ident[EI_MAG0] = '\177' + + eh.ident[EI_MAG1] = 'E' + eh.ident[EI_MAG2] = 'L' + eh.ident[EI_MAG3] = 'F' + if HEADTYPE == obj.Hfreebsd { + eh.ident[EI_OSABI] = ELFOSABI_FREEBSD + } else if HEADTYPE == obj.Hnetbsd { + eh.ident[EI_OSABI] = ELFOSABI_NETBSD + } else if HEADTYPE == obj.Hopenbsd { + eh.ident[EI_OSABI] = ELFOSABI_OPENBSD + } else if HEADTYPE == obj.Hdragonfly { + eh.ident[EI_OSABI] = ELFOSABI_NONE + } + if elf64 { + eh.ident[EI_CLASS] = ELFCLASS64 + } else { + eh.ident[EI_CLASS] = ELFCLASS32 + } + if Ctxt.Arch.ByteOrder == binary.BigEndian { + eh.ident[EI_DATA] = ELFDATA2MSB + } else { + eh.ident[EI_DATA] = ELFDATA2LSB + } + eh.ident[EI_VERSION] = EV_CURRENT + + if Linkmode == LinkExternal { + eh.type_ = ET_REL + } else { + eh.type_ = ET_EXEC + } + + if Linkmode != LinkExternal { + eh.entry = uint64(Entryvalue()) + } + + eh.version = EV_CURRENT + + if pph != nil { + pph.filesz = uint64(eh.phnum) * uint64(eh.phentsize) + pph.memsz = pph.filesz + } + + Cseek(0) + a := int64(0) + a += int64(elfwritehdr()) + a += int64(elfwritephdrs()) + a += int64(elfwriteshdrs()) + if Debug['d'] == 0 { + a += int64(elfwriteinterp()) + } + if Linkmode != LinkExternal { + if HEADTYPE == obj.Hnetbsd { + a += int64(elfwritenetbsdsig()) + } + if HEADTYPE == obj.Hopenbsd { + a += int64(elfwriteopenbsdsig()) + } + if len(buildinfo) > 0 { + a += int64(elfwritebuildinfo()) + } + } + if Buildmode == BuildmodeShared { + a += int64(elfwritegopkgnote()) + } + + if a > elfreserve { + Diag("ELFRESERVE too small: %d > %d", a, elfreserve) + } +} + +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 +} + +func ELF32_R_TYPE(info uint32) uint32 { + return uint32(uint8(info)) +} + +func ELF32_R_INFO(sym uint32, type_ uint32) uint32 { + return sym<<8 | type_ +} + +func ELF32_ST_BIND(info uint8) uint8 { + return info >> 4 +} + +func ELF32_ST_TYPE(info uint8) uint8 { + return info & 0xf +} + +func ELF32_ST_INFO(bind uint8, type_ uint8) uint8 { + return bind<<4 | type_&0xf +} + +func ELF32_ST_VISIBILITY(oth uint8) uint8 { + return oth & 3 +} + +func ELF64_R_SYM(info uint64) uint32 { + return uint32(info >> 32) +} + +func ELF64_R_TYPE(info uint64) uint32 { + return uint32(info) +} + +func ELF64_R_INFO(sym uint32, type_ uint32) uint64 { + return uint64(sym)<<32 | uint64(type_) +} + +func ELF64_ST_BIND(info uint8) uint8 { + return info >> 4 +} + +func ELF64_ST_TYPE(info uint8) uint8 { + return info & 0xf +} + +func ELF64_ST_INFO(bind uint8, type_ uint8) uint8 { + return bind<<4 | type_&0xf +} + +func ELF64_ST_VISIBILITY(oth uint8) uint8 { + return oth & 3 +} diff --git a/src/cmd/link/internal/ld/go.go b/src/cmd/link/internal/ld/go.go new file mode 100644 index 0000000000..875b8d2e17 --- /dev/null +++ b/src/cmd/link/internal/ld/go.go @@ -0,0 +1,870 @@ +// Copyright 2009 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. + +// go-specific code shared across loaders (5l, 6l, 8l). + +package ld + +import ( + "bytes" + "cmd/internal/obj" + "fmt" + "os" + "strconv" + "strings" +) + +// go-specific code shared across loaders (5l, 6l, 8l). + +// replace all "". with pkg. +func expandpkg(t0 string, pkg string) string { + return strings.Replace(t0, `"".`, pkg+".", -1) +} + +// accumulate all type information from .6 files. +// check for inconsistencies. + +// TODO: +// generate debugging section in binary. +// once the dust settles, try to move some code to +// libmach, so that other linkers and ar can share. + +/* + * package import data + */ +type Import struct { + prefix string // "type", "var", "func", "const" + name string + def string + file string +} + +// importmap records type information about imported symbols to detect inconsistencies. +// Entries are keyed by qualified symbol name (e.g., "runtime.Callers" or "net/url.Error"). +var importmap = map[string]*Import{} + +func lookupImport(name string) *Import { + if x, ok := importmap[name]; ok { + return x + } + x := &Import{name: name} + importmap[name] = x + return x +} + +func ldpkg(f *obj.Biobuf, pkg string, length int64, filename string, whence int) { + var p0, p1 int + + if Debug['g'] != 0 { + return + } + + if int64(int(length)) != length { + fmt.Fprintf(os.Stderr, "%s: too much pkg data in %s\n", os.Args[0], filename) + if Debug['u'] != 0 { + errorexit() + } + return + } + + bdata := make([]byte, length) + if int64(obj.Bread(f, bdata)) != length { + fmt.Fprintf(os.Stderr, "%s: short pkg read %s\n", os.Args[0], filename) + if Debug['u'] != 0 { + errorexit() + } + return + } + data := string(bdata) + + // first \n$$ marks beginning of exports - skip rest of line + p0 = strings.Index(data, "\n$$") + if p0 < 0 { + if Debug['u'] != 0 && whence != ArchiveObj { + Exitf("cannot find export data in %s", filename) + } + return + } + + p0 += 3 + for p0 < len(data) && data[p0] != '\n' { + p0++ + } + + // second marks end of exports / beginning of local data + p1 = strings.Index(data[p0:], "\n$$") + if p1 < 0 { + fmt.Fprintf(os.Stderr, "%s: cannot find end of exports in %s\n", os.Args[0], filename) + if Debug['u'] != 0 { + errorexit() + } + return + } + p1 += p0 + + for p0 < p1 && (data[p0] == ' ' || data[p0] == '\t' || data[p0] == '\n') { + p0++ + } + if p0 < p1 { + if !strings.HasPrefix(data[p0:], "package ") { + fmt.Fprintf(os.Stderr, "%s: bad package section in %s - %.20s\n", os.Args[0], filename, data[p0:]) + if Debug['u'] != 0 { + errorexit() + } + return + } + + p0 += 8 + for p0 < p1 && (data[p0] == ' ' || data[p0] == '\t' || data[p0] == '\n') { + p0++ + } + pname := p0 + for p0 < p1 && data[p0] != ' ' && data[p0] != '\t' && data[p0] != '\n' { + p0++ + } + if Debug['u'] != 0 && whence != ArchiveObj && (p0+6 > p1 || !strings.HasPrefix(data[p0:], " safe\n")) { + Exitf("load of unsafe package %s", filename) + } + + name := data[pname:p0] + for p0 < p1 && data[p0] != '\n' { + p0++ + } + if p0 < p1 { + p0++ + } + + if pkg == "main" && name != "main" { + Exitf("%s: not package main (package %s)", filename, name) + } + + loadpkgdata(filename, pkg, data[p0:p1]) + } + + // __.PKGDEF has no cgo section - those are in the C compiler-generated object files. + if whence == Pkgdef { + return + } + + // look for cgo section + p0 = strings.Index(data[p1:], "\n$$ // cgo") + if p0 >= 0 { + p0 += p1 + i := strings.IndexByte(data[p0+1:], '\n') + if i < 0 { + fmt.Fprintf(os.Stderr, "%s: found $$ // cgo but no newline in %s\n", os.Args[0], filename) + if Debug['u'] != 0 { + errorexit() + } + return + } + p0 += 1 + i + + p1 = strings.Index(data[p0:], "\n$$") + if p1 < 0 { + p1 = strings.Index(data[p0:], "\n!\n") + } + if p1 < 0 { + fmt.Fprintf(os.Stderr, "%s: cannot find end of // cgo section in %s\n", os.Args[0], filename) + if Debug['u'] != 0 { + errorexit() + } + return + } + p1 += p0 + + loadcgo(filename, pkg, data[p0:p1]) + } +} + +func loadpkgdata(file string, pkg string, data string) { + var prefix string + var name string + var def string + + p := data + for parsepkgdata(file, pkg, &p, &prefix, &name, &def) > 0 { + x := lookupImport(name) + if x.prefix == "" { + x.prefix = prefix + x.def = def + x.file = file + } else if x.prefix != prefix { + fmt.Fprintf(os.Stderr, "%s: conflicting definitions for %s\n", os.Args[0], name) + fmt.Fprintf(os.Stderr, "%s:\t%s %s ...\n", x.file, x.prefix, name) + fmt.Fprintf(os.Stderr, "%s:\t%s %s ...\n", file, prefix, name) + nerrors++ + } else if x.def != def { + fmt.Fprintf(os.Stderr, "%s: conflicting definitions for %s\n", os.Args[0], name) + fmt.Fprintf(os.Stderr, "%s:\t%s %s %s\n", x.file, x.prefix, name, x.def) + fmt.Fprintf(os.Stderr, "%s:\t%s %s %s\n", file, prefix, name, def) + nerrors++ + } + } +} + +func parsepkgdata(file string, pkg string, pp *string, prefixp *string, namep *string, defp *string) int { + // skip white space + p := *pp + +loop: + for len(p) > 0 && (p[0] == ' ' || p[0] == '\t' || p[0] == '\n') { + p = p[1:] + } + if len(p) == 0 || strings.HasPrefix(p, "$$\n") { + return 0 + } + + // prefix: (var|type|func|const) + prefix := p + + if len(p) < 7 { + return -1 + } + if strings.HasPrefix(p, "var ") { + p = p[4:] + } else if strings.HasPrefix(p, "type ") { + p = p[5:] + } else if strings.HasPrefix(p, "func ") { + p = p[5:] + } else if strings.HasPrefix(p, "const ") { + p = p[6:] + } else if strings.HasPrefix(p, "import ") { + p = p[7:] + for len(p) > 0 && p[0] != ' ' { + p = p[1:] + } + p = p[1:] + line := p + for len(p) > 0 && p[0] != '\n' { + p = p[1:] + } + if len(p) == 0 { + fmt.Fprintf(os.Stderr, "%s: %s: confused in import line\n", os.Args[0], file) + nerrors++ + return -1 + } + line = line[:len(line)-len(p)] + line = strings.TrimSuffix(line, " // indirect") + path, err := strconv.Unquote(line) + if err != nil { + fmt.Fprintf(os.Stderr, "%s: %s: confused in import path: %q\n", os.Args[0], file, line) + nerrors++ + return -1 + } + p = p[1:] + imported(pkg, path) + goto loop + } else { + fmt.Fprintf(os.Stderr, "%s: %s: confused in pkg data near <<%.40s>>\n", os.Args[0], file, prefix) + nerrors++ + return -1 + } + + prefix = prefix[:len(prefix)-len(p)-1] + + // name: a.b followed by space + name := p + + inquote := false + for len(p) > 0 { + if p[0] == ' ' && !inquote { + break + } + + if p[0] == '\\' { + p = p[1:] + } else if p[0] == '"' { + inquote = !inquote + } + + p = p[1:] + } + + if len(p) == 0 { + return -1 + } + name = name[:len(name)-len(p)] + p = p[1:] + + // def: free form to new line + def := p + + for len(p) > 0 && p[0] != '\n' { + p = p[1:] + } + if len(p) == 0 { + return -1 + } + def = def[:len(def)-len(p)] + var defbuf *bytes.Buffer + p = p[1:] + + // include methods on successive lines in def of named type + var meth string + for parsemethod(&p, &meth) > 0 { + if defbuf == nil { + defbuf = new(bytes.Buffer) + defbuf.WriteString(def) + } + defbuf.WriteString("\n\t") + defbuf.WriteString(meth) + } + if defbuf != nil { + def = defbuf.String() + } + + name = expandpkg(name, pkg) + def = expandpkg(def, pkg) + + // done + *pp = p + + *prefixp = prefix + *namep = name + *defp = def + return 1 +} + +func parsemethod(pp *string, methp *string) int { + // skip white space + p := *pp + + for len(p) > 0 && (p[0] == ' ' || p[0] == '\t') { + p = p[1:] + } + if len(p) == 0 { + return 0 + } + + // might be a comment about the method + if strings.HasPrefix(p, "//") { + goto useline + } + + // if it says "func (", it's a method + if strings.HasPrefix(p, "func (") { + goto useline + } + return 0 + + // definition to end of line +useline: + *methp = p + + for len(p) > 0 && p[0] != '\n' { + p = p[1:] + } + if len(p) == 0 { + fmt.Fprintf(os.Stderr, "%s: lost end of line in method definition\n", os.Args[0]) + *pp = "" + return -1 + } + + *methp = (*methp)[:len(*methp)-len(p)] + *pp = p[1:] + return 1 +} + +func loadcgo(file string, pkg string, p string) { + var next string + var q string + var f []string + var local string + var remote string + var lib string + var s *LSym + + p0 := "" + for ; p != ""; p = next { + if i := strings.Index(p, "\n"); i >= 0 { + p, next = p[:i], p[i+1:] + } else { + next = "" + } + + p0 = p // save for error message + f = tokenize(p) + if len(f) == 0 { + continue + } + + if f[0] == "cgo_import_dynamic" { + if len(f) < 2 || len(f) > 4 { + goto err + } + + local = f[1] + remote = local + if len(f) > 2 { + remote = f[2] + } + lib = "" + if len(f) > 3 { + lib = f[3] + } + + if Debug['d'] != 0 { + fmt.Fprintf(os.Stderr, "%s: %s: cannot use dynamic imports with -d flag\n", os.Args[0], file) + nerrors++ + return + } + + if local == "_" && remote == "_" { + // allow #pragma dynimport _ _ "foo.so" + // to force a link of foo.so. + havedynamic = 1 + + if HEADTYPE == obj.Hdarwin { + Machoadddynlib(lib) + } else { + dynlib = append(dynlib, lib) + } + continue + } + + local = expandpkg(local, pkg) + q = "" + if i := strings.Index(remote, "#"); i >= 0 { + remote, q = remote[:i], remote[i+1:] + } + s = Linklookup(Ctxt, local, 0) + if local != f[1] { + } + if s.Type == 0 || s.Type == obj.SXREF || s.Type == obj.SHOSTOBJ { + s.Dynimplib = lib + s.Extname = remote + s.Dynimpvers = q + if s.Type != obj.SHOSTOBJ { + s.Type = obj.SDYNIMPORT + } + havedynamic = 1 + } + + continue + } + + if f[0] == "cgo_import_static" { + if len(f) != 2 { + goto err + } + local = f[1] + s = Linklookup(Ctxt, local, 0) + s.Type = obj.SHOSTOBJ + s.Size = 0 + continue + } + + if f[0] == "cgo_export_static" || f[0] == "cgo_export_dynamic" { + if len(f) < 2 || len(f) > 3 { + goto err + } + local = f[1] + if len(f) > 2 { + remote = f[2] + } else { + remote = local + } + local = expandpkg(local, pkg) + s = Linklookup(Ctxt, local, 0) + + switch Buildmode { + case BuildmodeCShared, BuildmodeCArchive: + if s == Linklookup(Ctxt, "main", 0) { + continue + } + } + + // export overrides import, for openbsd/cgo. + // see issue 4878. + if s.Dynimplib != "" { + s.Dynimplib = "" + s.Extname = "" + s.Dynimpvers = "" + s.Type = 0 + } + + if s.Cgoexport == 0 { + s.Extname = remote + dynexp = append(dynexp, s) + } else if s.Extname != remote { + fmt.Fprintf(os.Stderr, "%s: conflicting cgo_export directives: %s as %s and %s\n", os.Args[0], s.Name, s.Extname, remote) + nerrors++ + return + } + + if f[0] == "cgo_export_static" { + s.Cgoexport |= CgoExportStatic + } else { + s.Cgoexport |= CgoExportDynamic + } + if local != f[1] { + } + continue + } + + if f[0] == "cgo_dynamic_linker" { + if len(f) != 2 { + goto err + } + + if Debug['I'] == 0 { + if interpreter != "" && interpreter != f[1] { + fmt.Fprintf(os.Stderr, "%s: conflict dynlinker: %s and %s\n", os.Args[0], interpreter, f[1]) + nerrors++ + return + } + + interpreter = f[1] + } + + continue + } + + if f[0] == "cgo_ldflag" { + if len(f) != 2 { + goto err + } + ldflag = append(ldflag, f[1]) + continue + } + } + + return + +err: + fmt.Fprintf(os.Stderr, "%s: %s: invalid dynimport line: %s\n", os.Args[0], file, p0) + nerrors++ +} + +var seenlib = make(map[string]bool) + +func adddynlib(lib string) { + if seenlib[lib] || Linkmode == LinkExternal { + 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 { + Diag("adddynlib: unsupported binary format") + } +} + +func Adddynsym(ctxt *Link, s *LSym) { + if s.Dynid >= 0 || Linkmode == LinkExternal { + 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 + +func mark1(s *LSym, parent *LSym) { + if s == nil || s.Reachable { + return + } + if strings.HasPrefix(s.Name, "go.weak.") { + return + } + s.Reachable = true + s.Reachparent = parent + if markq == nil { + markq = s + } else { + emarkq.Queue = s + } + emarkq = s +} + +func mark(s *LSym) { + mark1(s, nil) +} + +func markflood() { + var a *Auto + var i int + + for s := markq; s != nil; s = s.Queue { + if s.Type == obj.STEXT { + if Debug['v'] > 1 { + fmt.Fprintf(&Bso, "marktext %s\n", s.Name) + } + for a = s.Autom; a != nil; a = a.Link { + mark1(a.Gotype, s) + } + } + + for i = 0; i < len(s.R); i++ { + mark1(s.R[i].Sym, s) + } + if s.Pcln != nil { + for i = 0; i < s.Pcln.Nfuncdata; i++ { + mark1(s.Pcln.Funcdata[i], s) + } + } + + mark1(s.Gotype, s) + mark1(s.Sub, s) + mark1(s.Outer, s) + } +} + +var markextra = []string{ + "runtime.morestack", + "runtime.morestackx", + "runtime.morestack00", + "runtime.morestack10", + "runtime.morestack01", + "runtime.morestack11", + "runtime.morestack8", + "runtime.morestack16", + "runtime.morestack24", + "runtime.morestack32", + "runtime.morestack40", + "runtime.morestack48", + // on arm, lock in the div/mod helpers too + "_div", + "_divu", + "_mod", + "_modu", +} + +func deadcode() { + if Debug['v'] != 0 { + fmt.Fprintf(&Bso, "%5.2f deadcode\n", obj.Cputime()) + } + + if Buildmode == BuildmodeShared { + // Mark all symbols as reachable when building a + // shared library. + for s := Ctxt.Allsym; s != nil; s = s.Allsym { + if s.Type != 0 { + mark(s) + } + } + mark(Linkrlookup(Ctxt, "main.main", 0)) + mark(Linkrlookup(Ctxt, "main.init", 0)) + } else { + mark(Linklookup(Ctxt, INITENTRY, 0)) + if Linkshared && Buildmode == BuildmodeExe { + mark(Linkrlookup(Ctxt, "main.main", 0)) + mark(Linkrlookup(Ctxt, "main.init", 0)) + } + for i := 0; i < len(markextra); i++ { + mark(Linklookup(Ctxt, markextra[i], 0)) + } + + for i := 0; i < len(dynexp); i++ { + mark(dynexp[i]) + } + markflood() + + // keep each beginning with 'typelink.' if the symbol it points at is being kept. + for s := Ctxt.Allsym; s != nil; s = s.Allsym { + if strings.HasPrefix(s.Name, "go.typelink.") { + s.Reachable = len(s.R) == 1 && s.R[0].Sym.Reachable + } + } + + // remove dead text but keep file information (z symbols). + var last *LSym + + for s := Ctxt.Textp; s != nil; s = s.Next { + if !s.Reachable { + continue + } + + // NOTE: Removing s from old textp and adding to new, shorter textp. + if last == nil { + Ctxt.Textp = s + } else { + last.Next = s + } + last = s + } + + if last == nil { + Ctxt.Textp = nil + Ctxt.Etextp = nil + } else { + last.Next = nil + Ctxt.Etextp = last + } + } + + for s := Ctxt.Allsym; s != nil; s = s.Allsym { + if strings.HasPrefix(s.Name, "go.weak.") { + s.Special = 1 // do not lay out in data segment + s.Reachable = true + s.Hide = 1 + } + } + + // record field tracking references + var buf bytes.Buffer + var p *LSym + for s := Ctxt.Allsym; s != nil; s = s.Allsym { + if strings.HasPrefix(s.Name, "go.track.") { + s.Special = 1 // do not lay out in data segment + s.Hide = 1 + if s.Reachable { + buf.WriteString(s.Name[9:]) + for p = s.Reachparent; p != nil; p = p.Reachparent { + buf.WriteString("\t") + buf.WriteString(p.Name) + } + buf.WriteString("\n") + } + + s.Type = obj.SCONST + s.Value = 0 + } + } + + if tracksym == "" { + return + } + s := Linklookup(Ctxt, tracksym, 0) + if !s.Reachable { + return + } + addstrdata(tracksym, buf.String()) +} + +func doweak() { + var t *LSym + + // resolve weak references only if + // target symbol will be in binary anyway. + for s := Ctxt.Allsym; s != nil; s = s.Allsym { + if strings.HasPrefix(s.Name, "go.weak.") { + t = Linkrlookup(Ctxt, s.Name[8:], int(s.Version)) + if t != nil && t.Type != 0 && t.Reachable { + s.Value = t.Value + s.Type = t.Type + s.Outer = t + } else { + s.Type = obj.SCONST + s.Value = 0 + } + + continue + } + } +} + +func addexport() { + if HEADTYPE == obj.Hdarwin { + return + } + + for _, exp := range dynexp { + Adddynsym(Ctxt, exp) + } + for _, lib := range dynlib { + adddynlib(lib) + } +} + +type Pkg struct { + mark bool + checked bool + path string + impby []*Pkg +} + +var ( + // pkgmap records the imported-by relationship between packages. + // Entries are keyed by package path (e.g., "runtime" or "net/url"). + pkgmap = map[string]*Pkg{} + + pkgall []*Pkg +) + +func lookupPkg(path string) *Pkg { + if p, ok := pkgmap[path]; ok { + return p + } + p := &Pkg{path: path} + pkgmap[path] = p + pkgall = append(pkgall, p) + return p +} + +// imported records that package pkg imports package imp. +func imported(pkg, imp string) { + // everyone imports runtime, even runtime. + if imp == "runtime" { + return + } + + p := lookupPkg(pkg) + i := lookupPkg(imp) + i.impby = append(i.impby, p) +} + +func (p *Pkg) cycle() *Pkg { + if p.checked { + return nil + } + + if p.mark { + nerrors++ + fmt.Printf("import cycle:\n") + fmt.Printf("\t%s\n", p.path) + return p + } + + p.mark = true + for _, q := range p.impby { + if bad := q.cycle(); bad != nil { + p.mark = false + p.checked = true + fmt.Printf("\timports %s\n", p.path) + if bad == p { + return nil + } + return bad + } + } + + p.checked = true + p.mark = false + return nil +} + +func importcycles() { + for _, p := range pkgall { + p.cycle() + } +} + +func setlinkmode(arg string) { + if arg == "internal" { + Linkmode = LinkInternal + } else if arg == "external" { + Linkmode = LinkExternal + } else if arg == "auto" { + Linkmode = LinkAuto + } else { + Exitf("unknown link mode -linkmode %s", arg) + } +} diff --git a/src/cmd/link/internal/ld/ld.go b/src/cmd/link/internal/ld/ld.go new file mode 100644 index 0000000000..1068bdd767 --- /dev/null +++ b/src/cmd/link/internal/ld/ld.go @@ -0,0 +1,130 @@ +// Derived from Inferno utils/6l/obj.c and utils/6l/span.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/span.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "cmd/internal/obj" + "fmt" + "io/ioutil" + "os" + "path" + "strconv" + "strings" +) + +func addlib(ctxt *Link, src string, obj string, pathname string) { + name := path.Clean(pathname) + + // runtime.a -> runtime, runtime.6 -> runtime + pkg := name + if len(pkg) >= 2 && pkg[len(pkg)-2] == '.' { + pkg = pkg[:len(pkg)-2] + } + + // already loaded? + for i := 0; i < len(ctxt.Library); i++ { + if ctxt.Library[i].Pkg == pkg { + return + } + } + + var pname string + isshlib := false + if (ctxt.Windows == 0 && strings.HasPrefix(name, "/")) || (ctxt.Windows != 0 && len(name) >= 2 && name[1] == ':') { + pname = name + } else { + // try dot, -L "libdir", and then goroot. + for _, dir := range ctxt.Libdir { + if Linkshared { + pname = dir + "/" + pkg + ".shlibname" + if _, err := os.Stat(pname); err == nil { + isshlib = true + break + } + } + pname = dir + "/" + name + if _, err := os.Stat(pname); err == nil { + break + } + } + } + + pname = path.Clean(pname) + + if ctxt.Debugvlog > 1 && ctxt.Bso != nil { + fmt.Fprintf(ctxt.Bso, "%5.2f addlib: %s %s pulls in %s isshlib %v\n", elapsed(), obj, src, pname, isshlib) + } + + if isshlib { + addlibpath(ctxt, src, obj, "", pkg, pname) + } else { + addlibpath(ctxt, src, obj, pname, pkg, "") + } +} + +/* + * add library to library list. + * srcref: src file referring to package + * objref: object file referring to package + * file: object file, e.g., /home/rsc/go/pkg/container/vector.a + * pkg: package import path, e.g. container/vector + */ +func addlibpath(ctxt *Link, srcref string, objref string, file string, pkg string, shlibnamefile string) { + for i := 0; i < len(ctxt.Library); i++ { + if pkg == ctxt.Library[i].Pkg { + return + } + } + + if ctxt.Debugvlog > 1 && ctxt.Bso != nil { + 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] + l.Objref = objref + l.Srcref = srcref + l.File = file + l.Pkg = pkg + if shlibnamefile != "" { + shlibbytes, err := ioutil.ReadFile(shlibnamefile) + if err != nil { + Diag("cannot read %s: %v", shlibnamefile, err) + } + l.Shlib = strings.TrimSpace(string(shlibbytes)) + } +} + +func atolwhex(s string) int64 { + n, _ := strconv.ParseInt(s, 0, 64) + return n +} diff --git a/src/cmd/link/internal/ld/ldelf.go b/src/cmd/link/internal/ld/ldelf.go new file mode 100644 index 0000000000..3efdb75b89 --- /dev/null +++ b/src/cmd/link/internal/ld/ldelf.go @@ -0,0 +1,1019 @@ +package ld + +import ( + "bytes" + "cmd/internal/obj" + "encoding/binary" + "fmt" + "log" + "sort" + "strings" +) + +/* +Derived from Plan 9 from User Space's src/libmach/elf.h, elf.c +http://code.swtch.com/plan9port/src/tip/src/libmach/ + + Copyright © 2004 Russ Cox. + Portions Copyright © 2008-2010 Google Inc. + Portions Copyright © 2010 The Go Authors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +const ( + ElfClassNone = 0 + ElfClass32 = 1 + ElfClass64 = 2 +) + +const ( + ElfDataNone = 0 + ElfDataLsb = 1 + ElfDataMsb = 2 +) + +const ( + ElfTypeNone = 0 + ElfTypeRelocatable = 1 + ElfTypeExecutable = 2 + ElfTypeSharedObject = 3 + ElfTypeCore = 4 +) + +const ( + ElfMachNone = 0 + ElfMach32100 = 1 + ElfMachSparc = 2 + ElfMach386 = 3 + ElfMach68000 = 4 + ElfMach88000 = 5 + ElfMach486 = 6 + ElfMach860 = 7 + ElfMachMips = 8 + ElfMachS370 = 9 + ElfMachMipsLe = 10 + ElfMachParisc = 15 + ElfMachVpp500 = 17 + ElfMachSparc32Plus = 18 + ElfMach960 = 19 + ElfMachPower = 20 + ElfMachPower64 = 21 + ElfMachS390 = 22 + ElfMachV800 = 36 + ElfMachFr20 = 37 + ElfMachRh32 = 38 + ElfMachRce = 39 + ElfMachArm = 40 + ElfMachAlpha = 41 + ElfMachSH = 42 + ElfMachSparc9 = 43 + ElfMachAmd64 = 62 + ElfMachArm64 = 183 +) + +const ( + ElfAbiNone = 0 + ElfAbiSystemV = 0 + ElfAbiHPUX = 1 + ElfAbiNetBSD = 2 + ElfAbiLinux = 3 + ElfAbiSolaris = 6 + ElfAbiAix = 7 + ElfAbiIrix = 8 + ElfAbiFreeBSD = 9 + ElfAbiTru64 = 10 + ElfAbiModesto = 11 + ElfAbiOpenBSD = 12 + ElfAbiARM = 97 + ElfAbiEmbedded = 255 +) + +const ( + ElfSectNone = 0 + ElfSectProgbits = 1 + ElfSectSymtab = 2 + ElfSectStrtab = 3 + ElfSectRela = 4 + ElfSectHash = 5 + ElfSectDynamic = 6 + ElfSectNote = 7 + ElfSectNobits = 8 + ElfSectRel = 9 + ElfSectShlib = 10 + ElfSectDynsym = 11 + ElfSectFlagWrite = 0x1 + ElfSectFlagAlloc = 0x2 + ElfSectFlagExec = 0x4 +) + +const ( + ElfSymBindLocal = 0 + ElfSymBindGlobal = 1 + ElfSymBindWeak = 2 +) + +const ( + ElfSymTypeNone = 0 + ElfSymTypeObject = 1 + ElfSymTypeFunc = 2 + ElfSymTypeSection = 3 + ElfSymTypeFile = 4 +) + +const ( + ElfSymShnNone = 0 + ElfSymShnAbs = 0xFFF1 + ElfSymShnCommon = 0xFFF2 +) + +const ( + ElfProgNone = 0 + ElfProgLoad = 1 + ElfProgDynamic = 2 + ElfProgInterp = 3 + ElfProgNote = 4 + ElfProgShlib = 5 + ElfProgPhdr = 6 + ElfProgFlagExec = 0x1 + ElfProgFlagWrite = 0x2 + ElfProgFlagRead = 0x4 +) + +const ( + ElfNotePrStatus = 1 + ElfNotePrFpreg = 2 + ElfNotePrPsinfo = 3 + ElfNotePrTaskstruct = 4 + ElfNotePrAuxv = 6 + ElfNotePrXfpreg = 0x46e62b7f +) + +type ElfHdrBytes struct { + Ident [16]uint8 + Type [2]uint8 + Machine [2]uint8 + Version [4]uint8 + Entry [4]uint8 + Phoff [4]uint8 + Shoff [4]uint8 + Flags [4]uint8 + Ehsize [2]uint8 + Phentsize [2]uint8 + Phnum [2]uint8 + Shentsize [2]uint8 + Shnum [2]uint8 + Shstrndx [2]uint8 +} + +type ElfSectBytes struct { + Name [4]uint8 + Type [4]uint8 + Flags [4]uint8 + Addr [4]uint8 + Off [4]uint8 + Size [4]uint8 + Link [4]uint8 + Info [4]uint8 + Align [4]uint8 + Entsize [4]uint8 +} + +type ElfProgBytes struct { +} + +type ElfSymBytes struct { + Name [4]uint8 + Value [4]uint8 + Size [4]uint8 + Info uint8 + Other uint8 + Shndx [2]uint8 +} + +type ElfHdrBytes64 struct { + Ident [16]uint8 + Type [2]uint8 + Machine [2]uint8 + Version [4]uint8 + Entry [8]uint8 + Phoff [8]uint8 + Shoff [8]uint8 + Flags [4]uint8 + Ehsize [2]uint8 + Phentsize [2]uint8 + Phnum [2]uint8 + Shentsize [2]uint8 + Shnum [2]uint8 + Shstrndx [2]uint8 +} + +type ElfSectBytes64 struct { + Name [4]uint8 + Type [4]uint8 + Flags [8]uint8 + Addr [8]uint8 + Off [8]uint8 + Size [8]uint8 + Link [4]uint8 + Info [4]uint8 + Align [8]uint8 + Entsize [8]uint8 +} + +type ElfProgBytes64 struct { +} + +type ElfSymBytes64 struct { + Name [4]uint8 + Info uint8 + Other uint8 + Shndx [2]uint8 + Value [8]uint8 + Size [8]uint8 +} + +type ElfSect struct { + name string + nameoff uint32 + type_ uint32 + flags uint64 + addr uint64 + off uint64 + size uint64 + link uint32 + info uint32 + align uint64 + entsize uint64 + base []byte + sym *LSym +} + +type ElfObj struct { + f *obj.Biobuf + base int64 // offset in f where ELF begins + length int64 // length of ELF + is64 int + name string + e binary.ByteOrder + sect []ElfSect + nsect uint + shstrtab string + nsymtab int + symtab *ElfSect + symstr *ElfSect + type_ uint32 + machine uint32 + version uint32 + entry uint64 + phoff uint64 + shoff uint64 + flags uint32 + ehsize uint32 + phentsize uint32 + phnum uint32 + shentsize uint32 + shnum uint32 + shstrndx uint32 +} + +type ElfSym struct { + name string + value uint64 + size uint64 + bind uint8 + type_ uint8 + other uint8 + shndx uint16 + sym *LSym +} + +var ElfMagic = [4]uint8{0x7F, 'E', 'L', 'F'} + +func valuecmp(a *LSym, b *LSym) int { + if a.Value < b.Value { + return -1 + } + if a.Value > b.Value { + return +1 + } + return 0 +} + +func ldelf(f *obj.Biobuf, pkg string, length int64, pn string) { + if Debug['v'] != 0 { + fmt.Fprintf(&Bso, "%5.2f ldelf %s\n", obj.Cputime(), pn) + } + + Ctxt.Version++ + base := int32(obj.Boffset(f)) + + var add uint64 + var e binary.ByteOrder + var elfobj *ElfObj + var err error + var flag int + var hdr *ElfHdrBytes + var hdrbuf [64]uint8 + var info uint64 + var is64 int + var j int + var n int + var name string + var p []byte + var r []Reloc + var rela int + var rp *Reloc + var rsect *ElfSect + var s *LSym + var sect *ElfSect + var sym ElfSym + var symbols []*LSym + if obj.Bread(f, hdrbuf[:]) != len(hdrbuf) { + goto bad + } + hdr = new(ElfHdrBytes) + binary.Read(bytes.NewReader(hdrbuf[:]), binary.BigEndian, hdr) // only byte arrays; byte order doesn't matter + if string(hdr.Ident[:4]) != "\x7FELF" { + goto bad + } + switch hdr.Ident[5] { + case ElfDataLsb: + e = binary.LittleEndian + + case ElfDataMsb: + e = binary.BigEndian + + default: + goto bad + } + + // read header + elfobj = new(ElfObj) + + elfobj.e = e + elfobj.f = f + elfobj.base = int64(base) + elfobj.length = length + elfobj.name = pn + + is64 = 0 + if hdr.Ident[4] == ElfClass64 { + is64 = 1 + hdr := new(ElfHdrBytes64) + binary.Read(bytes.NewReader(hdrbuf[:]), binary.BigEndian, hdr) // only byte arrays; byte order doesn't matter + elfobj.type_ = uint32(e.Uint16(hdr.Type[:])) + elfobj.machine = uint32(e.Uint16(hdr.Machine[:])) + elfobj.version = e.Uint32(hdr.Version[:]) + elfobj.phoff = e.Uint64(hdr.Phoff[:]) + elfobj.shoff = e.Uint64(hdr.Shoff[:]) + elfobj.flags = e.Uint32(hdr.Flags[:]) + elfobj.ehsize = uint32(e.Uint16(hdr.Ehsize[:])) + elfobj.phentsize = uint32(e.Uint16(hdr.Phentsize[:])) + elfobj.phnum = uint32(e.Uint16(hdr.Phnum[:])) + elfobj.shentsize = uint32(e.Uint16(hdr.Shentsize[:])) + elfobj.shnum = uint32(e.Uint16(hdr.Shnum[:])) + elfobj.shstrndx = uint32(e.Uint16(hdr.Shstrndx[:])) + } else { + elfobj.type_ = uint32(e.Uint16(hdr.Type[:])) + elfobj.machine = uint32(e.Uint16(hdr.Machine[:])) + elfobj.version = e.Uint32(hdr.Version[:]) + elfobj.entry = uint64(e.Uint32(hdr.Entry[:])) + elfobj.phoff = uint64(e.Uint32(hdr.Phoff[:])) + elfobj.shoff = uint64(e.Uint32(hdr.Shoff[:])) + elfobj.flags = e.Uint32(hdr.Flags[:]) + elfobj.ehsize = uint32(e.Uint16(hdr.Ehsize[:])) + elfobj.phentsize = uint32(e.Uint16(hdr.Phentsize[:])) + elfobj.phnum = uint32(e.Uint16(hdr.Phnum[:])) + elfobj.shentsize = uint32(e.Uint16(hdr.Shentsize[:])) + elfobj.shnum = uint32(e.Uint16(hdr.Shnum[:])) + elfobj.shstrndx = uint32(e.Uint16(hdr.Shstrndx[:])) + } + + elfobj.is64 = is64 + + if uint32(hdr.Ident[6]) != elfobj.version { + goto bad + } + + if e.Uint16(hdr.Type[:]) != ElfTypeRelocatable { + Diag("%s: elf but not elf relocatable object", pn) + return + } + + switch Thearch.Thechar { + default: + Diag("%s: elf %s unimplemented", pn, Thestring) + return + + case '5': + if e != binary.LittleEndian || elfobj.machine != ElfMachArm || hdr.Ident[4] != ElfClass32 { + Diag("%s: elf object but not arm", pn) + return + } + + case '6': + if e != binary.LittleEndian || elfobj.machine != ElfMachAmd64 || hdr.Ident[4] != ElfClass64 { + Diag("%s: elf object but not amd64", pn) + return + } + + case '7': + if e != binary.LittleEndian || elfobj.machine != ElfMachArm64 || hdr.Ident[4] != ElfClass64 { + Diag("%s: elf object but not arm64", pn) + return + } + + case '8': + if e != binary.LittleEndian || elfobj.machine != ElfMach386 || hdr.Ident[4] != ElfClass32 { + Diag("%s: elf object but not 386", pn) + return + } + + case '9': + if elfobj.machine != ElfMachPower64 || hdr.Ident[4] != ElfClass64 { + Diag("%s: elf object but not ppc64", pn) + return + } + } + + // load section list into memory. + elfobj.sect = make([]ElfSect, elfobj.shnum) + + elfobj.nsect = uint(elfobj.shnum) + for i := 0; uint(i) < elfobj.nsect; i++ { + if obj.Bseek(f, int64(uint64(base)+elfobj.shoff+uint64(int64(i)*int64(elfobj.shentsize))), 0) < 0 { + goto bad + } + sect = &elfobj.sect[i] + if is64 != 0 { + var b ElfSectBytes64 + + if err = binary.Read(f, e, &b); err != nil { + goto bad + } + + sect.nameoff = uint32(e.Uint32(b.Name[:])) + sect.type_ = e.Uint32(b.Type[:]) + sect.flags = e.Uint64(b.Flags[:]) + sect.addr = e.Uint64(b.Addr[:]) + sect.off = e.Uint64(b.Off[:]) + sect.size = e.Uint64(b.Size[:]) + sect.link = e.Uint32(b.Link[:]) + sect.info = e.Uint32(b.Info[:]) + sect.align = e.Uint64(b.Align[:]) + sect.entsize = e.Uint64(b.Entsize[:]) + } else { + var b ElfSectBytes + + if err = binary.Read(f, e, &b); err != nil { + goto bad + } + + sect.nameoff = uint32(e.Uint32(b.Name[:])) + sect.type_ = e.Uint32(b.Type[:]) + sect.flags = uint64(e.Uint32(b.Flags[:])) + sect.addr = uint64(e.Uint32(b.Addr[:])) + sect.off = uint64(e.Uint32(b.Off[:])) + sect.size = uint64(e.Uint32(b.Size[:])) + sect.link = e.Uint32(b.Link[:]) + sect.info = e.Uint32(b.Info[:]) + sect.align = uint64(e.Uint32(b.Align[:])) + sect.entsize = uint64(e.Uint32(b.Entsize[:])) + } + } + + // read section string table and translate names + if elfobj.shstrndx >= uint32(elfobj.nsect) { + err = fmt.Errorf("shstrndx out of range %d >= %d", elfobj.shstrndx, elfobj.nsect) + goto bad + } + + sect = &elfobj.sect[elfobj.shstrndx] + if err = elfmap(elfobj, sect); err != nil { + goto bad + } + for i := 0; uint(i) < elfobj.nsect; i++ { + if elfobj.sect[i].nameoff != 0 { + elfobj.sect[i].name = cstring(sect.base[elfobj.sect[i].nameoff:]) + } + } + + // load string table for symbols into memory. + elfobj.symtab = section(elfobj, ".symtab") + + if elfobj.symtab == nil { + // our work is done here - no symbols means nothing can refer to this file + return + } + + if elfobj.symtab.link <= 0 || elfobj.symtab.link >= uint32(elfobj.nsect) { + Diag("%s: elf object has symbol table with invalid string table link", pn) + return + } + + elfobj.symstr = &elfobj.sect[elfobj.symtab.link] + if is64 != 0 { + elfobj.nsymtab = int(elfobj.symtab.size / ELF64SYMSIZE) + } else { + elfobj.nsymtab = int(elfobj.symtab.size / ELF32SYMSIZE) + } + + if err = elfmap(elfobj, elfobj.symtab); err != nil { + goto bad + } + if err = elfmap(elfobj, elfobj.symstr); err != nil { + goto bad + } + + // load text and data segments into memory. + // they are not as small as the section lists, but we'll need + // the memory anyway for the symbol images, so we might + // as well use one large chunk. + + // create symbols for elfmapped sections + for i := 0; uint(i) < elfobj.nsect; i++ { + sect = &elfobj.sect[i] + if (sect.type_ != ElfSectProgbits && sect.type_ != ElfSectNobits) || sect.flags&ElfSectFlagAlloc == 0 { + continue + } + if sect.type_ != ElfSectNobits { + if err = elfmap(elfobj, sect); err != nil { + goto bad + } + } + + name = fmt.Sprintf("%s(%s)", pkg, sect.name) + s = Linklookup(Ctxt, name, Ctxt.Version) + + switch int(sect.flags) & (ElfSectFlagAlloc | ElfSectFlagWrite | ElfSectFlagExec) { + default: + err = fmt.Errorf("unexpected flags for ELF section %s", sect.name) + goto bad + + case ElfSectFlagAlloc: + s.Type = obj.SRODATA + + case ElfSectFlagAlloc + ElfSectFlagWrite: + if sect.type_ == ElfSectNobits { + s.Type = obj.SNOPTRBSS + } else { + s.Type = obj.SNOPTRDATA + } + + case ElfSectFlagAlloc + ElfSectFlagExec: + s.Type = obj.STEXT + } + + if sect.name == ".got" || sect.name == ".toc" { + s.Type = obj.SELFGOT + } + if sect.type_ == ElfSectProgbits { + s.P = sect.base + s.P = s.P[:sect.size] + } + + s.Size = int64(sect.size) + s.Align = int32(sect.align) + sect.sym = s + } + + // enter sub-symbols into symbol table. + // symbol 0 is the null symbol. + symbols = make([]*LSym, elfobj.nsymtab) + + for i := 1; i < elfobj.nsymtab; i++ { + if err = readelfsym(elfobj, i, &sym, 1); err != nil { + goto bad + } + symbols[i] = sym.sym + if sym.type_ != ElfSymTypeFunc && sym.type_ != ElfSymTypeObject && sym.type_ != ElfSymTypeNone { + continue + } + if sym.shndx == ElfSymShnCommon { + s = sym.sym + if uint64(s.Size) < sym.size { + s.Size = int64(sym.size) + } + if s.Type == 0 || s.Type == obj.SXREF { + s.Type = obj.SNOPTRBSS + } + continue + } + + if uint(sym.shndx) >= elfobj.nsect || sym.shndx == 0 { + continue + } + + // even when we pass needSym == 1 to readelfsym, it might still return nil to skip some unwanted symbols + if sym.sym == nil { + continue + } + sect = &elfobj.sect[sym.shndx:][0] + if sect.sym == nil { + if strings.HasPrefix(sym.name, ".Linfo_string") { // clang does this + continue + } + Diag("%s: sym#%d: ignoring %s in section %d (type %d)", pn, i, sym.name, sym.shndx, sym.type_) + continue + } + + s = sym.sym + if s.Outer != nil { + if s.Dupok != 0 { + continue + } + Exitf("%s: duplicate symbol reference: %s in both %s and %s", pn, s.Name, s.Outer.Name, sect.sym.Name) + } + + s.Sub = sect.sym.Sub + sect.sym.Sub = s + s.Type = sect.sym.Type | s.Type&^obj.SMASK | obj.SSUB + if s.Cgoexport&CgoExportDynamic == 0 { + s.Dynimplib = "" // satisfy dynimport + } + s.Value = int64(sym.value) + s.Size = int64(sym.size) + s.Outer = sect.sym + if sect.sym.Type == obj.STEXT { + if s.External != 0 && s.Dupok == 0 { + Diag("%s: duplicate definition of %s", pn, s.Name) + } + s.External = 1 + } + + if elfobj.machine == ElfMachPower64 { + flag = int(sym.other) >> 5 + if 2 <= flag && flag <= 6 { + s.Localentry = 1 << uint(flag-2) + } else if flag == 7 { + Diag("%s: invalid sym.other 0x%x for %s", pn, sym.other, s.Name) + } + } + } + + // Sort outer lists by address, adding to textp. + // This keeps textp in increasing address order. + for i := 0; uint(i) < elfobj.nsect; i++ { + s = elfobj.sect[i].sym + if s == nil { + continue + } + if s.Sub != nil { + s.Sub = listsort(s.Sub, valuecmp, listsubp) + } + if s.Type == obj.STEXT { + if s.Onlist != 0 { + log.Fatalf("symbol %s listed multiple times", s.Name) + } + s.Onlist = 1 + if Ctxt.Etextp != nil { + Ctxt.Etextp.Next = s + } else { + Ctxt.Textp = s + } + Ctxt.Etextp = s + for s = s.Sub; s != nil; s = s.Sub { + if s.Onlist != 0 { + log.Fatalf("symbol %s listed multiple times", s.Name) + } + s.Onlist = 1 + Ctxt.Etextp.Next = s + Ctxt.Etextp = s + } + } + } + + // load relocations + for i := 0; uint(i) < elfobj.nsect; i++ { + rsect = &elfobj.sect[i] + if rsect.type_ != ElfSectRela && rsect.type_ != ElfSectRel { + continue + } + if rsect.info >= uint32(elfobj.nsect) || elfobj.sect[rsect.info].base == nil { + continue + } + sect = &elfobj.sect[rsect.info] + if err = elfmap(elfobj, rsect); err != nil { + goto bad + } + rela = 0 + if rsect.type_ == ElfSectRela { + rela = 1 + } + n = int(rsect.size / uint64(4+4*is64) / uint64(2+rela)) + r = make([]Reloc, n) + p = rsect.base + for j = 0; j < n; j++ { + add = 0 + rp = &r[j] + if is64 != 0 { + // 64-bit rel/rela + rp.Off = int32(e.Uint64(p)) + + p = p[8:] + info = e.Uint64(p) + p = p[8:] + if rela != 0 { + add = e.Uint64(p) + p = p[8:] + } + } else { + // 32-bit rel/rela + rp.Off = int32(e.Uint32(p)) + + p = p[4:] + info = uint64(e.Uint32(p)) + info = info>>8<<32 | info&0xff // convert to 64-bit info + p = p[4:] + if rela != 0 { + add = uint64(e.Uint32(p)) + p = p[4:] + } + } + + if info&0xffffffff == 0 { // skip R_*_NONE relocation + j-- + n-- + continue + } + + if info>>32 == 0 { // absolute relocation, don't bother reading the null symbol + rp.Sym = nil + } else { + if err = readelfsym(elfobj, int(info>>32), &sym, 0); err != nil { + goto bad + } + sym.sym = symbols[info>>32] + if sym.sym == nil { + err = fmt.Errorf("%s#%d: reloc of invalid sym #%d %s shndx=%d type=%d", sect.sym.Name, j, int(info>>32), sym.name, sym.shndx, sym.type_) + goto bad + } + + rp.Sym = sym.sym + } + + rp.Type = int32(reltype(pn, int(uint32(info)), &rp.Siz)) + if rela != 0 { + rp.Add = int64(add) + } else { + // load addend from image + if rp.Siz == 4 { + rp.Add = int64(e.Uint32(sect.base[rp.Off:])) + } else if rp.Siz == 8 { + rp.Add = int64(e.Uint64(sect.base[rp.Off:])) + } else { + Diag("invalid rela size %d", rp.Siz) + } + } + + if rp.Siz == 2 { + rp.Add = int64(int16(rp.Add)) + } + if rp.Siz == 4 { + rp.Add = int64(int32(rp.Add)) + } + } + + //print("rel %s %d %d %s %#llx\n", sect->sym->name, rp->type, rp->siz, rp->sym->name, rp->add); + sort.Sort(rbyoff(r[:n])) + // just in case + + s = sect.sym + s.R = r + s.R = s.R[:n] + } + + return + +bad: + Diag("%s: malformed elf file: %v", pn, err) +} + +func section(elfobj *ElfObj, name string) *ElfSect { + for i := 0; uint(i) < elfobj.nsect; i++ { + if elfobj.sect[i].name != "" && name != "" && elfobj.sect[i].name == name { + return &elfobj.sect[i] + } + } + return nil +} + +func elfmap(elfobj *ElfObj, sect *ElfSect) (err error) { + if sect.base != nil { + return nil + } + + if sect.off+sect.size > uint64(elfobj.length) { + err = fmt.Errorf("elf section past end of file") + return err + } + + sect.base = make([]byte, sect.size) + err = fmt.Errorf("short read") + if obj.Bseek(elfobj.f, int64(uint64(elfobj.base)+sect.off), 0) < 0 || obj.Bread(elfobj.f, sect.base) != len(sect.base) { + return err + } + + return nil +} + +func readelfsym(elfobj *ElfObj, i int, sym *ElfSym, needSym int) (err error) { + if i >= elfobj.nsymtab || i < 0 { + err = fmt.Errorf("invalid elf symbol index") + return err + } + + if i == 0 { + Diag("readym: read null symbol!") + } + + if elfobj.is64 != 0 { + b := new(ElfSymBytes64) + binary.Read(bytes.NewReader(elfobj.symtab.base[i*ELF64SYMSIZE:(i+1)*ELF64SYMSIZE]), elfobj.e, b) + sym.name = cstring(elfobj.symstr.base[elfobj.e.Uint32(b.Name[:]):]) + sym.value = elfobj.e.Uint64(b.Value[:]) + sym.size = elfobj.e.Uint64(b.Size[:]) + sym.shndx = elfobj.e.Uint16(b.Shndx[:]) + sym.bind = b.Info >> 4 + sym.type_ = b.Info & 0xf + sym.other = b.Other + } else { + b := new(ElfSymBytes) + binary.Read(bytes.NewReader(elfobj.symtab.base[i*ELF32SYMSIZE:(i+1)*ELF32SYMSIZE]), elfobj.e, b) + sym.name = cstring(elfobj.symstr.base[elfobj.e.Uint32(b.Name[:]):]) + sym.value = uint64(elfobj.e.Uint32(b.Value[:])) + sym.size = uint64(elfobj.e.Uint32(b.Size[:])) + sym.shndx = elfobj.e.Uint16(b.Shndx[:]) + sym.bind = b.Info >> 4 + sym.type_ = b.Info & 0xf + sym.other = b.Other + } + + var s *LSym + if sym.name == "_GLOBAL_OFFSET_TABLE_" { + sym.name = ".got" + } + if sym.name == ".TOC." { + // Magic symbol on ppc64. Will be set to this object + // file's .got+0x8000. + sym.bind = ElfSymBindLocal + } + + switch sym.type_ { + case ElfSymTypeSection: + s = elfobj.sect[sym.shndx].sym + + case ElfSymTypeObject, ElfSymTypeFunc, ElfSymTypeNone: + switch sym.bind { + case ElfSymBindGlobal: + if needSym != 0 { + s = Linklookup(Ctxt, sym.name, 0) + + // for global scoped hidden symbols we should insert it into + // symbol hash table, but mark them as hidden. + // __i686.get_pc_thunk.bx is allowed to be duplicated, to + // workaround that we set dupok. + // TODO(minux): correctly handle __i686.get_pc_thunk.bx without + // set dupok generally. See http://codereview.appspot.com/5823055/ + // comment #5 for details. + if s != nil && sym.other == 2 { + s.Type |= obj.SHIDDEN + s.Dupok = 1 + } + } + + case ElfSymBindLocal: + if Thearch.Thechar == '5' && (strings.HasPrefix(sym.name, "$a") || strings.HasPrefix(sym.name, "$d")) { + // binutils for arm generate these mapping + // symbols, ignore these + break + } + + if sym.name == ".TOC." { + // We need to be able to look this up, + // so put it in the hash table. + if needSym != 0 { + s = Linklookup(Ctxt, sym.name, Ctxt.Version) + s.Type |= obj.SHIDDEN + } + + break + } + + if needSym != 0 { + // local names and hidden visiblity global names are unique + // and should only reference by its index, not name, so we + // don't bother to add them into hash table + s = linknewsym(Ctxt, sym.name, Ctxt.Version) + + s.Type |= obj.SHIDDEN + } + + case ElfSymBindWeak: + if needSym != 0 { + s = linknewsym(Ctxt, sym.name, 0) + if sym.other == 2 { + s.Type |= obj.SHIDDEN + } + } + + default: + err = fmt.Errorf("%s: invalid symbol binding %d", sym.name, sym.bind) + return err + } + } + + if s != nil && s.Type == 0 && sym.type_ != ElfSymTypeSection { + s.Type = obj.SXREF + } + sym.sym = s + + return nil +} + +type rbyoff []Reloc + +func (x rbyoff) Len() int { + return len(x) +} + +func (x rbyoff) Swap(i, j int) { + x[i], x[j] = x[j], x[i] +} + +func (x rbyoff) Less(i, j int) bool { + a := &x[i] + b := &x[j] + if a.Off < b.Off { + return true + } + if a.Off > b.Off { + return false + } + return false +} + +func reltype(pn string, elftype int, siz *uint8) int { + switch uint32(Thearch.Thechar) | uint32(elftype)<<24 { + default: + Diag("%s: unknown relocation type %d; compiled without -fpic?", pn, elftype) + fallthrough + + case '9' | R_PPC64_TOC16<<24, + '9' | R_PPC64_TOC16_LO<<24, + '9' | R_PPC64_TOC16_HI<<24, + '9' | R_PPC64_TOC16_HA<<24, + '9' | R_PPC64_TOC16_DS<<24, + '9' | R_PPC64_TOC16_LO_DS<<24, + '9' | R_PPC64_REL16_LO<<24, + '9' | R_PPC64_REL16_HI<<24, + '9' | R_PPC64_REL16_HA<<24: + *siz = 2 + + case '5' | R_ARM_ABS32<<24, + '5' | R_ARM_GOT32<<24, + '5' | R_ARM_PLT32<<24, + '5' | R_ARM_GOTOFF<<24, + '5' | R_ARM_GOTPC<<24, + '5' | R_ARM_THM_PC22<<24, + '5' | R_ARM_REL32<<24, + '5' | R_ARM_CALL<<24, + '5' | R_ARM_V4BX<<24, + '5' | R_ARM_GOT_PREL<<24, + '5' | R_ARM_PC24<<24, + '5' | R_ARM_JUMP24<<24, + '6' | R_X86_64_PC32<<24, + '6' | R_X86_64_PLT32<<24, + '6' | R_X86_64_GOTPCREL<<24, + '8' | R_386_32<<24, + '8' | R_386_PC32<<24, + '8' | R_386_GOT32<<24, + '8' | R_386_PLT32<<24, + '8' | R_386_GOTOFF<<24, + '8' | R_386_GOTPC<<24, + '9' | R_PPC64_REL24<<24: + *siz = 4 + + case '6' | R_X86_64_64<<24, + '9' | R_PPC64_ADDR64<<24: + *siz = 8 + } + + return 256 + elftype +} diff --git a/src/cmd/link/internal/ld/ldmacho.go b/src/cmd/link/internal/ld/ldmacho.go new file mode 100644 index 0000000000..2abfa3336f --- /dev/null +++ b/src/cmd/link/internal/ld/ldmacho.go @@ -0,0 +1,893 @@ +package ld + +import ( + "cmd/internal/obj" + "encoding/binary" + "fmt" + "log" + "sort" +) + +/* +Derived from Plan 9 from User Space's src/libmach/elf.h, elf.c +http://code.swtch.com/plan9port/src/tip/src/libmach/ + + Copyright © 2004 Russ Cox. + Portions Copyright © 2008-2010 Google Inc. + Portions Copyright © 2010 The Go Authors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +const ( + N_EXT = 0x01 + N_TYPE = 0x1e + N_STAB = 0xe0 +) + +type LdMachoObj struct { + f *obj.Biobuf + base int64 // off in f where Mach-O begins + length int64 // length of Mach-O + is64 bool + name string + e binary.ByteOrder + cputype uint + subcputype uint + filetype uint32 + flags uint32 + cmd []LdMachoCmd + ncmd uint +} + +type LdMachoCmd struct { + type_ int + off uint32 + size uint32 + seg LdMachoSeg + sym LdMachoSymtab + dsym LdMachoDysymtab +} + +type LdMachoSeg struct { + name string + vmaddr uint64 + vmsize uint64 + fileoff uint32 + filesz uint32 + maxprot uint32 + initprot uint32 + nsect uint32 + flags uint32 + sect []LdMachoSect +} + +type LdMachoSect struct { + name string + segname string + addr uint64 + size uint64 + off uint32 + align uint32 + reloff uint32 + nreloc uint32 + flags uint32 + res1 uint32 + res2 uint32 + sym *LSym + rel []LdMachoRel +} + +type LdMachoRel struct { + addr uint32 + symnum uint32 + pcrel uint8 + length uint8 + extrn uint8 + type_ uint8 + scattered uint8 + value uint32 +} + +type LdMachoSymtab struct { + symoff uint32 + nsym uint32 + stroff uint32 + strsize uint32 + str []byte + sym []LdMachoSym +} + +type LdMachoSym struct { + name string + type_ uint8 + sectnum uint8 + desc uint16 + kind int8 + value uint64 + sym *LSym +} + +type LdMachoDysymtab struct { + ilocalsym uint32 + nlocalsym uint32 + iextdefsym uint32 + nextdefsym uint32 + iundefsym uint32 + nundefsym uint32 + tocoff uint32 + ntoc uint32 + modtaboff uint32 + nmodtab uint32 + extrefsymoff uint32 + nextrefsyms uint32 + indirectsymoff uint32 + nindirectsyms uint32 + extreloff uint32 + nextrel uint32 + locreloff uint32 + nlocrel uint32 + indir []uint32 +} + +const ( + LdMachoCpuVax = 1 + LdMachoCpu68000 = 6 + LdMachoCpu386 = 7 + LdMachoCpuAmd64 = 0x1000007 + LdMachoCpuMips = 8 + LdMachoCpu98000 = 10 + LdMachoCpuHppa = 11 + LdMachoCpuArm = 12 + LdMachoCpu88000 = 13 + LdMachoCpuSparc = 14 + LdMachoCpu860 = 15 + LdMachoCpuAlpha = 16 + LdMachoCpuPower = 18 + LdMachoCmdSegment = 1 + LdMachoCmdSymtab = 2 + LdMachoCmdSymseg = 3 + LdMachoCmdThread = 4 + LdMachoCmdDysymtab = 11 + LdMachoCmdSegment64 = 25 + LdMachoFileObject = 1 + LdMachoFileExecutable = 2 + LdMachoFileFvmlib = 3 + LdMachoFileCore = 4 + LdMachoFilePreload = 5 +) + +func unpackcmd(p []byte, m *LdMachoObj, c *LdMachoCmd, type_ uint, sz uint) int { + e4 := m.e.Uint32 + e8 := m.e.Uint64 + + c.type_ = int(type_) + c.size = uint32(sz) + switch type_ { + default: + return -1 + + case LdMachoCmdSegment: + if sz < 56 { + return -1 + } + c.seg.name = cstring(p[8:24]) + c.seg.vmaddr = uint64(e4(p[24:])) + c.seg.vmsize = uint64(e4(p[28:])) + c.seg.fileoff = e4(p[32:]) + c.seg.filesz = e4(p[36:]) + c.seg.maxprot = e4(p[40:]) + c.seg.initprot = e4(p[44:]) + c.seg.nsect = e4(p[48:]) + c.seg.flags = e4(p[52:]) + c.seg.sect = make([]LdMachoSect, c.seg.nsect) + if uint32(sz) < 56+c.seg.nsect*68 { + return -1 + } + p = p[56:] + var s *LdMachoSect + for i := 0; uint32(i) < c.seg.nsect; i++ { + s = &c.seg.sect[i] + s.name = cstring(p[0:16]) + s.segname = cstring(p[16:32]) + s.addr = uint64(e4(p[32:])) + s.size = uint64(e4(p[36:])) + s.off = e4(p[40:]) + s.align = e4(p[44:]) + s.reloff = e4(p[48:]) + s.nreloc = e4(p[52:]) + s.flags = e4(p[56:]) + s.res1 = e4(p[60:]) + s.res2 = e4(p[64:]) + p = p[68:] + } + + case LdMachoCmdSegment64: + if sz < 72 { + return -1 + } + c.seg.name = cstring(p[8:24]) + c.seg.vmaddr = e8(p[24:]) + c.seg.vmsize = e8(p[32:]) + c.seg.fileoff = uint32(e8(p[40:])) + c.seg.filesz = uint32(e8(p[48:])) + c.seg.maxprot = e4(p[56:]) + c.seg.initprot = e4(p[60:]) + c.seg.nsect = e4(p[64:]) + c.seg.flags = e4(p[68:]) + c.seg.sect = make([]LdMachoSect, c.seg.nsect) + if uint32(sz) < 72+c.seg.nsect*80 { + return -1 + } + p = p[72:] + var s *LdMachoSect + for i := 0; uint32(i) < c.seg.nsect; i++ { + s = &c.seg.sect[i] + s.name = cstring(p[0:16]) + s.segname = cstring(p[16:32]) + s.addr = e8(p[32:]) + s.size = e8(p[40:]) + s.off = e4(p[48:]) + s.align = e4(p[52:]) + s.reloff = e4(p[56:]) + s.nreloc = e4(p[60:]) + s.flags = e4(p[64:]) + s.res1 = e4(p[68:]) + s.res2 = e4(p[72:]) + + // p+76 is reserved + p = p[80:] + } + + case LdMachoCmdSymtab: + if sz < 24 { + return -1 + } + c.sym.symoff = e4(p[8:]) + c.sym.nsym = e4(p[12:]) + c.sym.stroff = e4(p[16:]) + c.sym.strsize = e4(p[20:]) + + case LdMachoCmdDysymtab: + if sz < 80 { + return -1 + } + c.dsym.ilocalsym = e4(p[8:]) + c.dsym.nlocalsym = e4(p[12:]) + c.dsym.iextdefsym = e4(p[16:]) + c.dsym.nextdefsym = e4(p[20:]) + c.dsym.iundefsym = e4(p[24:]) + c.dsym.nundefsym = e4(p[28:]) + c.dsym.tocoff = e4(p[32:]) + c.dsym.ntoc = e4(p[36:]) + c.dsym.modtaboff = e4(p[40:]) + c.dsym.nmodtab = e4(p[44:]) + c.dsym.extrefsymoff = e4(p[48:]) + c.dsym.nextrefsyms = e4(p[52:]) + c.dsym.indirectsymoff = e4(p[56:]) + c.dsym.nindirectsyms = e4(p[60:]) + c.dsym.extreloff = e4(p[64:]) + c.dsym.nextrel = e4(p[68:]) + c.dsym.locreloff = e4(p[72:]) + c.dsym.nlocrel = e4(p[76:]) + } + + return 0 +} + +func macholoadrel(m *LdMachoObj, sect *LdMachoSect) int { + if sect.rel != nil || sect.nreloc == 0 { + return 0 + } + rel := make([]LdMachoRel, sect.nreloc) + n := int(sect.nreloc * 8) + buf := make([]byte, n) + if obj.Bseek(m.f, m.base+int64(sect.reloff), 0) < 0 || obj.Bread(m.f, buf) != n { + return -1 + } + var p []byte + var r *LdMachoRel + var v uint32 + for i := 0; uint32(i) < sect.nreloc; i++ { + r = &rel[i] + p = buf[i*8:] + r.addr = m.e.Uint32(p) + + // TODO(rsc): Wrong interpretation for big-endian bitfields? + if r.addr&0x80000000 != 0 { + // scatterbrained relocation + r.scattered = 1 + + v = r.addr >> 24 + r.addr &= 0xFFFFFF + r.type_ = uint8(v & 0xF) + v >>= 4 + r.length = 1 << (v & 3) + v >>= 2 + r.pcrel = uint8(v & 1) + r.value = m.e.Uint32(p[4:]) + } else { + v = m.e.Uint32(p[4:]) + r.symnum = v & 0xFFFFFF + v >>= 24 + r.pcrel = uint8(v & 1) + v >>= 1 + r.length = 1 << (v & 3) + v >>= 2 + r.extrn = uint8(v & 1) + v >>= 1 + r.type_ = uint8(v) + } + } + + sect.rel = rel + return 0 +} + +func macholoaddsym(m *LdMachoObj, d *LdMachoDysymtab) int { + n := int(d.nindirectsyms) + + p := make([]byte, n*4) + if obj.Bseek(m.f, m.base+int64(d.indirectsymoff), 0) < 0 || obj.Bread(m.f, p) != len(p) { + return -1 + } + + d.indir = make([]uint32, n) + for i := 0; i < n; i++ { + d.indir[i] = m.e.Uint32(p[4*i:]) + } + return 0 +} + +func macholoadsym(m *LdMachoObj, symtab *LdMachoSymtab) int { + if symtab.sym != nil { + return 0 + } + + strbuf := make([]byte, symtab.strsize) + if obj.Bseek(m.f, m.base+int64(symtab.stroff), 0) < 0 || obj.Bread(m.f, strbuf) != len(strbuf) { + return -1 + } + + symsize := 12 + if m.is64 { + symsize = 16 + } + n := int(symtab.nsym * uint32(symsize)) + symbuf := make([]byte, n) + if obj.Bseek(m.f, m.base+int64(symtab.symoff), 0) < 0 || obj.Bread(m.f, symbuf) != len(symbuf) { + return -1 + } + sym := make([]LdMachoSym, symtab.nsym) + p := symbuf + var s *LdMachoSym + var v uint32 + for i := 0; uint32(i) < symtab.nsym; i++ { + s = &sym[i] + v = m.e.Uint32(p) + if v >= symtab.strsize { + return -1 + } + s.name = cstring(strbuf[v:]) + s.type_ = uint8(p[4]) + s.sectnum = uint8(p[5]) + s.desc = m.e.Uint16(p[6:]) + if m.is64 { + s.value = m.e.Uint64(p[8:]) + } else { + s.value = uint64(m.e.Uint32(p[8:])) + } + p = p[symsize:] + } + + symtab.str = strbuf + symtab.sym = sym + return 0 +} + +func ldmacho(f *obj.Biobuf, pkg string, length int64, pn string) { + var err error + var j int + var is64 bool + var secaddr uint64 + var hdr [7 * 4]uint8 + var cmdp []byte + var dat []byte + var ncmd uint32 + var cmdsz uint32 + var ty uint32 + var sz uint32 + var off uint32 + var m *LdMachoObj + var e binary.ByteOrder + var sect *LdMachoSect + var rel *LdMachoRel + var rpi int + var s *LSym + var s1 *LSym + var outer *LSym + var c *LdMachoCmd + var symtab *LdMachoSymtab + var dsymtab *LdMachoDysymtab + var sym *LdMachoSym + var r []Reloc + var rp *Reloc + var name string + + Ctxt.Version++ + base := obj.Boffset(f) + if obj.Bread(f, hdr[:]) != len(hdr) { + goto bad + } + + if binary.BigEndian.Uint32(hdr[:])&^1 == 0xFEEDFACE { + e = binary.BigEndian + } else if binary.LittleEndian.Uint32(hdr[:])&^1 == 0xFEEDFACE { + e = binary.LittleEndian + } else { + err = fmt.Errorf("bad magic - not mach-o file") + goto bad + } + + is64 = e.Uint32(hdr[:]) == 0xFEEDFACF + ncmd = e.Uint32([]byte(hdr[4*4:])) + cmdsz = e.Uint32([]byte(hdr[5*4:])) + if ncmd > 0x10000 || cmdsz >= 0x01000000 { + err = fmt.Errorf("implausible mach-o header ncmd=%d cmdsz=%d", ncmd, cmdsz) + goto bad + } + + if is64 { + var tmp [4]uint8 + obj.Bread(f, tmp[:4]) // skip reserved word in header + } + + m = new(LdMachoObj) + + m.f = f + m.e = e + m.cputype = uint(e.Uint32([]byte(hdr[1*4:]))) + m.subcputype = uint(e.Uint32([]byte(hdr[2*4:]))) + m.filetype = e.Uint32([]byte(hdr[3*4:])) + m.ncmd = uint(ncmd) + m.flags = e.Uint32([]byte(hdr[6*4:])) + m.is64 = is64 + m.base = base + m.length = length + m.name = pn + + switch Thearch.Thechar { + default: + Diag("%s: mach-o %s unimplemented", pn, Thestring) + return + + case '6': + if e != binary.LittleEndian || m.cputype != LdMachoCpuAmd64 { + Diag("%s: mach-o object but not amd64", pn) + return + } + + case '8': + if e != binary.LittleEndian || m.cputype != LdMachoCpu386 { + Diag("%s: mach-o object but not 386", pn) + return + } + } + + m.cmd = make([]LdMachoCmd, ncmd) + off = uint32(len(hdr)) + cmdp = make([]byte, cmdsz) + if obj.Bread(f, cmdp) != len(cmdp) { + err = fmt.Errorf("reading cmds: %v", err) + goto bad + } + + // read and parse load commands + c = nil + + symtab = nil + dsymtab = nil + + for i := 0; uint32(i) < ncmd; i++ { + ty = e.Uint32(cmdp) + sz = e.Uint32(cmdp[4:]) + m.cmd[i].off = off + unpackcmd(cmdp, m, &m.cmd[i], uint(ty), uint(sz)) + cmdp = cmdp[sz:] + off += sz + if ty == LdMachoCmdSymtab { + if symtab != nil { + err = fmt.Errorf("multiple symbol tables") + goto bad + } + + symtab = &m.cmd[i].sym + macholoadsym(m, symtab) + } + + if ty == LdMachoCmdDysymtab { + dsymtab = &m.cmd[i].dsym + macholoaddsym(m, dsymtab) + } + + if (is64 && ty == LdMachoCmdSegment64) || (!is64 && ty == LdMachoCmdSegment) { + if c != nil { + err = fmt.Errorf("multiple load commands") + goto bad + } + + c = &m.cmd[i] + } + } + + // load text and data segments into memory. + // they are not as small as the load commands, but we'll need + // the memory anyway for the symbol images, so we might + // as well use one large chunk. + if c == nil { + err = fmt.Errorf("no load command") + goto bad + } + + if symtab == nil { + // our work is done here - no symbols means nothing can refer to this file + return + } + + if int64(c.seg.fileoff+c.seg.filesz) >= length { + err = fmt.Errorf("load segment out of range") + goto bad + } + + dat = make([]byte, c.seg.filesz) + if obj.Bseek(f, m.base+int64(c.seg.fileoff), 0) < 0 || obj.Bread(f, dat) != len(dat) { + err = fmt.Errorf("cannot load object data: %v", err) + goto bad + } + + for i := 0; uint32(i) < c.seg.nsect; i++ { + sect = &c.seg.sect[i] + if sect.segname != "__TEXT" && sect.segname != "__DATA" { + continue + } + if sect.name == "__eh_frame" { + continue + } + name = fmt.Sprintf("%s(%s/%s)", pkg, sect.segname, sect.name) + s = Linklookup(Ctxt, name, Ctxt.Version) + if s.Type != 0 { + err = fmt.Errorf("duplicate %s/%s", sect.segname, sect.name) + goto bad + } + + if sect.flags&0xff == 1 { // S_ZEROFILL + s.P = make([]byte, sect.size) + } else { + s.P = dat[sect.addr-c.seg.vmaddr:][:sect.size] + } + s.Size = int64(len(s.P)) + + if sect.segname == "__TEXT" { + if sect.name == "__text" { + s.Type = obj.STEXT + } else { + s.Type = obj.SRODATA + } + } else { + if sect.name == "__bss" { + s.Type = obj.SNOPTRBSS + s.P = s.P[:0] + } else { + s.Type = obj.SNOPTRDATA + } + } + + sect.sym = s + } + + // enter sub-symbols into symbol table. + // have to guess sizes from next symbol. + for i := 0; uint32(i) < symtab.nsym; i++ { + sym = &symtab.sym[i] + if sym.type_&N_STAB != 0 { + continue + } + + // TODO: check sym->type against outer->type. + name = sym.name + + if name[0] == '_' && name[1] != '\x00' { + name = name[1:] + } + v := 0 + if sym.type_&N_EXT == 0 { + v = Ctxt.Version + } + s = Linklookup(Ctxt, name, v) + if sym.type_&N_EXT == 0 { + s.Dupok = 1 + } + sym.sym = s + if sym.sectnum == 0 { // undefined + continue + } + if uint32(sym.sectnum) > c.seg.nsect { + err = fmt.Errorf("reference to invalid section %d", sym.sectnum) + goto bad + } + + sect = &c.seg.sect[sym.sectnum-1] + outer = sect.sym + if outer == nil { + err = fmt.Errorf("reference to invalid section %s/%s", sect.segname, sect.name) + continue + } + + if s.Outer != nil { + if s.Dupok != 0 { + continue + } + Exitf("%s: duplicate symbol reference: %s in both %s and %s", pn, s.Name, s.Outer.Name, sect.sym.Name) + } + + s.Type = outer.Type | obj.SSUB + s.Sub = outer.Sub + outer.Sub = s + s.Outer = outer + s.Value = int64(sym.value - sect.addr) + if s.Cgoexport&CgoExportDynamic == 0 { + s.Dynimplib = "" // satisfy dynimport + } + if outer.Type == obj.STEXT { + if s.External != 0 && s.Dupok == 0 { + Diag("%s: duplicate definition of %s", pn, s.Name) + } + s.External = 1 + } + + sym.sym = s + } + + // Sort outer lists by address, adding to textp. + // This keeps textp in increasing address order. + for i := 0; uint32(i) < c.seg.nsect; i++ { + sect = &c.seg.sect[i] + s = sect.sym + if s == nil { + continue + } + if s.Sub != nil { + s.Sub = listsort(s.Sub, valuecmp, listsubp) + + // assign sizes, now that we know symbols in sorted order. + for s1 = s.Sub; s1 != nil; s1 = s1.Sub { + if s1.Sub != nil { + s1.Size = s1.Sub.Value - s1.Value + } else { + s1.Size = s.Value + s.Size - s1.Value + } + } + } + + if s.Type == obj.STEXT { + if s.Onlist != 0 { + log.Fatalf("symbol %s listed multiple times", s.Name) + } + s.Onlist = 1 + if Ctxt.Etextp != nil { + Ctxt.Etextp.Next = s + } else { + Ctxt.Textp = s + } + Ctxt.Etextp = s + for s1 = s.Sub; s1 != nil; s1 = s1.Sub { + if s1.Onlist != 0 { + log.Fatalf("symbol %s listed multiple times", s1.Name) + } + s1.Onlist = 1 + Ctxt.Etextp.Next = s1 + Ctxt.Etextp = s1 + } + } + } + + // load relocations + for i := 0; uint32(i) < c.seg.nsect; i++ { + sect = &c.seg.sect[i] + s = sect.sym + if s == nil { + continue + } + macholoadrel(m, sect) + if sect.rel == nil { + continue + } + r = make([]Reloc, sect.nreloc) + rpi = 0 + Reloc: + for j = 0; uint32(j) < sect.nreloc; j++ { + rp = &r[rpi] + rel = §.rel[j] + if rel.scattered != 0 { + if Thearch.Thechar != '8' { + // mach-o only uses scattered relocation on 32-bit platforms + Diag("unexpected scattered relocation") + + continue + } + + // on 386, rewrite scattered 4/1 relocation and some + // scattered 2/1 relocation into the pseudo-pc-relative + // reference that it is. + // assume that the second in the pair is in this section + // and use that as the pc-relative base. + if uint32(j+1) >= sect.nreloc { + err = fmt.Errorf("unsupported scattered relocation %d", int(rel.type_)) + goto bad + } + + if sect.rel[j+1].scattered == 0 || sect.rel[j+1].type_ != 1 || (rel.type_ != 4 && rel.type_ != 2) || uint64(sect.rel[j+1].value) < sect.addr || uint64(sect.rel[j+1].value) >= sect.addr+sect.size { + err = fmt.Errorf("unsupported scattered relocation %d/%d", int(rel.type_), int(sect.rel[j+1].type_)) + goto bad + } + + rp.Siz = rel.length + rp.Off = int32(rel.addr) + + // NOTE(rsc): I haven't worked out why (really when) + // we should ignore the addend on a + // scattered relocation, but it seems that the + // common case is we ignore it. + // It's likely that this is not strictly correct + // and that the math should look something + // like the non-scattered case below. + rp.Add = 0 + + // want to make it pc-relative aka relative to rp->off+4 + // but the scatter asks for relative to off = sect->rel[j+1].value - sect->addr. + // adjust rp->add accordingly. + rp.Type = obj.R_PCREL + + rp.Add += int64(uint64(int64(rp.Off)+4) - (uint64(sect.rel[j+1].value) - sect.addr)) + + // now consider the desired symbol. + // find the section where it lives. + var ks *LdMachoSect + for k := 0; uint32(k) < c.seg.nsect; k++ { + ks = &c.seg.sect[k] + if ks.addr <= uint64(rel.value) && uint64(rel.value) < ks.addr+ks.size { + if ks.sym != nil { + rp.Sym = ks.sym + rp.Add += int64(uint64(rel.value) - ks.addr) + } else if ks.segname == "__IMPORT" && ks.name == "__pointers" { + // handle reference to __IMPORT/__pointers. + // how much worse can this get? + // why are we supporting 386 on the mac anyway? + rp.Type = 512 + MACHO_FAKE_GOTPCREL + + // figure out which pointer this is a reference to. + k = int(uint64(ks.res1) + (uint64(rel.value)-ks.addr)/4) + + // load indirect table for __pointers + // fetch symbol number + if dsymtab == nil || k < 0 || uint32(k) >= dsymtab.nindirectsyms || dsymtab.indir == nil { + err = fmt.Errorf("invalid scattered relocation: indirect symbol reference out of range") + goto bad + } + + k = int(dsymtab.indir[k]) + if k < 0 || uint32(k) >= symtab.nsym { + err = fmt.Errorf("invalid scattered relocation: symbol reference out of range") + goto bad + } + + rp.Sym = symtab.sym[k].sym + } else { + err = fmt.Errorf("unsupported scattered relocation: reference to %s/%s", ks.segname, ks.name) + goto bad + } + + rpi++ + + // skip #1 of 2 rel; continue skips #2 of 2. + j++ + + continue Reloc + } + } + + err = fmt.Errorf("unsupported scattered relocation: invalid address %#x", rel.addr) + goto bad + + } + + rp.Siz = rel.length + rp.Type = 512 + (int32(rel.type_) << 1) + int32(rel.pcrel) + rp.Off = int32(rel.addr) + + // Handle X86_64_RELOC_SIGNED referencing a section (rel->extrn == 0). + if Thearch.Thechar == '6' && rel.extrn == 0 && rel.type_ == 1 { + // Calculate the addend as the offset into the section. + // + // The rip-relative offset stored in the object file is encoded + // as follows: + // + // movsd 0x00000360(%rip),%xmm0 + // + // To get the absolute address of the value this rip-relative address is pointing + // to, we must add the address of the next instruction to it. This is done by + // taking the address of the relocation and adding 4 to it (since the rip-relative + // offset can at most be 32 bits long). To calculate the offset into the section the + // relocation is referencing, we subtract the vaddr of the start of the referenced + // section found in the original object file. + // + // [For future reference, see Darwin's /usr/include/mach-o/x86_64/reloc.h] + secaddr = c.seg.sect[rel.symnum-1].addr + + rp.Add = int64(uint64(int64(int32(e.Uint32(s.P[rp.Off:])))+int64(rp.Off)+4) - secaddr) + } else { + rp.Add = int64(int32(e.Uint32(s.P[rp.Off:]))) + } + + // For i386 Mach-O PC-relative, the addend is written such that + // it *is* the PC being subtracted. Use that to make + // it match our version of PC-relative. + if rel.pcrel != 0 && Thearch.Thechar == '8' { + rp.Add += int64(rp.Off) + int64(rp.Siz) + } + if rel.extrn == 0 { + if rel.symnum < 1 || rel.symnum > c.seg.nsect { + err = fmt.Errorf("invalid relocation: section reference out of range %d vs %d", rel.symnum, c.seg.nsect) + goto bad + } + + rp.Sym = c.seg.sect[rel.symnum-1].sym + if rp.Sym == nil { + err = fmt.Errorf("invalid relocation: %s", c.seg.sect[rel.symnum-1].name) + goto bad + } + + // References to symbols in other sections + // include that information in the addend. + // We only care about the delta from the + // section base. + if Thearch.Thechar == '8' { + rp.Add -= int64(c.seg.sect[rel.symnum-1].addr) + } + } else { + if rel.symnum >= symtab.nsym { + err = fmt.Errorf("invalid relocation: symbol reference out of range") + goto bad + } + + rp.Sym = symtab.sym[rel.symnum].sym + } + + rpi++ + } + + sort.Sort(rbyoff(r[:rpi])) + s.R = r + s.R = s.R[:rpi] + } + + return + +bad: + Diag("%s: malformed mach-o file: %v", pn, err) +} diff --git a/src/cmd/link/internal/ld/ldpe.go b/src/cmd/link/internal/ld/ldpe.go new file mode 100644 index 0000000000..b98cf0206b --- /dev/null +++ b/src/cmd/link/internal/ld/ldpe.go @@ -0,0 +1,534 @@ +// 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. + +package ld + +import ( + "cmd/internal/obj" + "encoding/binary" + "fmt" + "log" + "sort" + "strings" +) + +const ( + IMAGE_SYM_UNDEFINED = 0 + IMAGE_SYM_ABSOLUTE = -1 + IMAGE_SYM_DEBUG = -2 + IMAGE_SYM_TYPE_NULL = 0 + IMAGE_SYM_TYPE_VOID = 1 + IMAGE_SYM_TYPE_CHAR = 2 + IMAGE_SYM_TYPE_SHORT = 3 + IMAGE_SYM_TYPE_INT = 4 + IMAGE_SYM_TYPE_LONG = 5 + IMAGE_SYM_TYPE_FLOAT = 6 + IMAGE_SYM_TYPE_DOUBLE = 7 + IMAGE_SYM_TYPE_STRUCT = 8 + IMAGE_SYM_TYPE_UNION = 9 + IMAGE_SYM_TYPE_ENUM = 10 + IMAGE_SYM_TYPE_MOE = 11 + IMAGE_SYM_TYPE_BYTE = 12 + IMAGE_SYM_TYPE_WORD = 13 + IMAGE_SYM_TYPE_UINT = 14 + IMAGE_SYM_TYPE_DWORD = 15 + IMAGE_SYM_TYPE_PCODE = 32768 + IMAGE_SYM_DTYPE_NULL = 0 + IMAGE_SYM_DTYPE_POINTER = 0x10 + IMAGE_SYM_DTYPE_FUNCTION = 0x20 + IMAGE_SYM_DTYPE_ARRAY = 0x30 + IMAGE_SYM_CLASS_END_OF_FUNCTION = -1 + IMAGE_SYM_CLASS_NULL = 0 + IMAGE_SYM_CLASS_AUTOMATIC = 1 + IMAGE_SYM_CLASS_EXTERNAL = 2 + IMAGE_SYM_CLASS_STATIC = 3 + IMAGE_SYM_CLASS_REGISTER = 4 + IMAGE_SYM_CLASS_EXTERNAL_DEF = 5 + IMAGE_SYM_CLASS_LABEL = 6 + IMAGE_SYM_CLASS_UNDEFINED_LABEL = 7 + IMAGE_SYM_CLASS_MEMBER_OF_STRUCT = 8 + IMAGE_SYM_CLASS_ARGUMENT = 9 + IMAGE_SYM_CLASS_STRUCT_TAG = 10 + IMAGE_SYM_CLASS_MEMBER_OF_UNION = 11 + IMAGE_SYM_CLASS_UNION_TAG = 12 + IMAGE_SYM_CLASS_TYPE_DEFINITION = 13 + IMAGE_SYM_CLASS_UNDEFINED_STATIC = 14 + IMAGE_SYM_CLASS_ENUM_TAG = 15 + IMAGE_SYM_CLASS_MEMBER_OF_ENUM = 16 + IMAGE_SYM_CLASS_REGISTER_PARAM = 17 + IMAGE_SYM_CLASS_BIT_FIELD = 18 + IMAGE_SYM_CLASS_FAR_EXTERNAL = 68 /* Not in PECOFF v8 spec */ + IMAGE_SYM_CLASS_BLOCK = 100 + IMAGE_SYM_CLASS_FUNCTION = 101 + IMAGE_SYM_CLASS_END_OF_STRUCT = 102 + IMAGE_SYM_CLASS_FILE = 103 + IMAGE_SYM_CLASS_SECTION = 104 + IMAGE_SYM_CLASS_WEAK_EXTERNAL = 105 + IMAGE_SYM_CLASS_CLR_TOKEN = 107 + IMAGE_REL_I386_ABSOLUTE = 0x0000 + IMAGE_REL_I386_DIR16 = 0x0001 + IMAGE_REL_I386_REL16 = 0x0002 + IMAGE_REL_I386_DIR32 = 0x0006 + IMAGE_REL_I386_DIR32NB = 0x0007 + IMAGE_REL_I386_SEG12 = 0x0009 + IMAGE_REL_I386_SECTION = 0x000A + IMAGE_REL_I386_SECREL = 0x000B + IMAGE_REL_I386_TOKEN = 0x000C + IMAGE_REL_I386_SECREL7 = 0x000D + IMAGE_REL_I386_REL32 = 0x0014 + IMAGE_REL_AMD64_ABSOLUTE = 0x0000 + IMAGE_REL_AMD64_ADDR64 = 0x0001 + IMAGE_REL_AMD64_ADDR32 = 0x0002 + IMAGE_REL_AMD64_ADDR32NB = 0x0003 + IMAGE_REL_AMD64_REL32 = 0x0004 + IMAGE_REL_AMD64_REL32_1 = 0x0005 + IMAGE_REL_AMD64_REL32_2 = 0x0006 + IMAGE_REL_AMD64_REL32_3 = 0x0007 + IMAGE_REL_AMD64_REL32_4 = 0x0008 + IMAGE_REL_AMD64_REL32_5 = 0x0009 + IMAGE_REL_AMD64_SECTION = 0x000A + IMAGE_REL_AMD64_SECREL = 0x000B + IMAGE_REL_AMD64_SECREL7 = 0x000C + IMAGE_REL_AMD64_TOKEN = 0x000D + IMAGE_REL_AMD64_SREL32 = 0x000E + IMAGE_REL_AMD64_PAIR = 0x000F + IMAGE_REL_AMD64_SSPAN32 = 0x0010 +) + +type PeSym struct { + name string + value uint32 + sectnum uint16 + type_ uint16 + sclass uint8 + aux uint8 + sym *LSym +} + +type PeSect struct { + name string + base []byte + size uint64 + sym *LSym + sh IMAGE_SECTION_HEADER +} + +type PeObj struct { + f *obj.Biobuf + name string + base uint32 + sect []PeSect + nsect uint + pesym []PeSym + npesym uint + fh IMAGE_FILE_HEADER + snames []byte +} + +func ldpe(f *obj.Biobuf, pkg string, length int64, pn string) { + if Debug['v'] != 0 { + fmt.Fprintf(&Bso, "%5.2f ldpe %s\n", obj.Cputime(), pn) + } + + var sect *PeSect + Ctxt.Version++ + base := int32(obj.Boffset(f)) + + peobj := new(PeObj) + peobj.f = f + peobj.base = uint32(base) + peobj.name = pn + + // read header + var err error + var j int + var l uint32 + var name string + var numaux int + var r []Reloc + var rp *Reloc + var rsect *PeSect + var s *LSym + var sym *PeSym + var symbuf [18]uint8 + if err = binary.Read(f, binary.LittleEndian, &peobj.fh); err != nil { + goto bad + } + + // load section list + peobj.sect = make([]PeSect, peobj.fh.NumberOfSections) + + peobj.nsect = uint(peobj.fh.NumberOfSections) + for i := 0; i < int(peobj.fh.NumberOfSections); i++ { + if err = binary.Read(f, binary.LittleEndian, &peobj.sect[i].sh); err != nil { + goto bad + } + peobj.sect[i].size = uint64(peobj.sect[i].sh.SizeOfRawData) + peobj.sect[i].name = cstring(peobj.sect[i].sh.Name[:]) + } + + // TODO return error if found .cormeta + + // load string table + obj.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(peobj.fh.NumberOfSymbols), 0) + + if obj.Bread(f, symbuf[:4]) != 4 { + goto bad + } + l = Le32(symbuf[:]) + peobj.snames = make([]byte, l) + obj.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(peobj.fh.NumberOfSymbols), 0) + if obj.Bread(f, peobj.snames) != len(peobj.snames) { + goto bad + } + + // rewrite section names if they start with / + for i := 0; i < int(peobj.fh.NumberOfSections); i++ { + if peobj.sect[i].name == "" { + continue + } + if peobj.sect[i].name[0] != '/' { + continue + } + l = uint32(obj.Atoi(peobj.sect[i].name[1:])) + peobj.sect[i].name = cstring(peobj.snames[l:]) + } + + // read symbols + peobj.pesym = make([]PeSym, peobj.fh.NumberOfSymbols) + + peobj.npesym = uint(peobj.fh.NumberOfSymbols) + obj.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable), 0) + for i := 0; uint32(i) < peobj.fh.NumberOfSymbols; i += numaux + 1 { + obj.Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(i), 0) + if obj.Bread(f, symbuf[:]) != len(symbuf) { + goto bad + } + + if (symbuf[0] == 0) && (symbuf[1] == 0) && (symbuf[2] == 0) && (symbuf[3] == 0) { + l = Le32(symbuf[4:]) + peobj.pesym[i].name = cstring(peobj.snames[l:]) // sym name length <= 8 + } else { + peobj.pesym[i].name = cstring(symbuf[:8]) + } + + peobj.pesym[i].value = Le32(symbuf[8:]) + peobj.pesym[i].sectnum = Le16(symbuf[12:]) + peobj.pesym[i].sclass = symbuf[16] + peobj.pesym[i].aux = symbuf[17] + peobj.pesym[i].type_ = Le16(symbuf[14:]) + numaux = int(peobj.pesym[i].aux) + if numaux < 0 { + numaux = 0 + } + } + + // create symbols for mapped sections + for i := 0; uint(i) < peobj.nsect; i++ { + sect = &peobj.sect[i] + if sect.sh.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 { + continue + } + + if sect.sh.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 { + // This has been seen for .idata sections, which we + // want to ignore. See issues 5106 and 5273. + continue + } + + if pemap(peobj, sect) < 0 { + goto bad + } + + name = fmt.Sprintf("%s(%s)", pkg, sect.name) + s = Linklookup(Ctxt, name, Ctxt.Version) + + switch sect.sh.Characteristics & (IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE) { + case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ: //.rdata + s.Type = obj.SRODATA + + case IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.bss + s.Type = obj.SNOPTRBSS + + case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.data + s.Type = obj.SNOPTRDATA + + case IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ: //.text + s.Type = obj.STEXT + + default: + err = fmt.Errorf("unexpected flags %#06x for PE section %s", sect.sh.Characteristics, sect.name) + goto bad + } + + s.P = sect.base + s.P = s.P[:sect.size] + s.Size = int64(sect.size) + sect.sym = s + if sect.name == ".rsrc" { + setpersrc(sect.sym) + } + } + + // load relocations + for i := 0; uint(i) < peobj.nsect; i++ { + rsect = &peobj.sect[i] + if rsect.sym == nil || rsect.sh.NumberOfRelocations == 0 { + continue + } + if rsect.sh.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 { + continue + } + if sect.sh.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 { + // This has been seen for .idata sections, which we + // want to ignore. See issues 5106 and 5273. + continue + } + + r = make([]Reloc, rsect.sh.NumberOfRelocations) + obj.Bseek(f, int64(peobj.base)+int64(rsect.sh.PointerToRelocations), 0) + for j = 0; j < int(rsect.sh.NumberOfRelocations); j++ { + rp = &r[j] + if obj.Bread(f, symbuf[:10]) != 10 { + goto bad + } + rva := Le32(symbuf[0:]) + symindex := Le32(symbuf[4:]) + type_ := Le16(symbuf[8:]) + if err = readpesym(peobj, int(symindex), &sym); err != nil { + goto bad + } + if sym.sym == nil { + err = fmt.Errorf("reloc of invalid sym %s idx=%d type=%d", sym.name, symindex, sym.type_) + goto bad + } + + rp.Sym = sym.sym + rp.Siz = 4 + rp.Off = int32(rva) + switch type_ { + default: + Diag("%s: unknown relocation type %d;", pn, type_) + fallthrough + + case IMAGE_REL_I386_REL32, IMAGE_REL_AMD64_REL32, + IMAGE_REL_AMD64_ADDR32, // R_X86_64_PC32 + IMAGE_REL_AMD64_ADDR32NB: + rp.Type = obj.R_PCREL + + rp.Add = int64(int32(Le32(rsect.base[rp.Off:]))) + + case IMAGE_REL_I386_DIR32NB, IMAGE_REL_I386_DIR32: + rp.Type = obj.R_ADDR + + // load addend from image + rp.Add = int64(int32(Le32(rsect.base[rp.Off:]))) + + case IMAGE_REL_AMD64_ADDR64: // R_X86_64_64 + rp.Siz = 8 + + rp.Type = obj.R_ADDR + + // load addend from image + rp.Add = int64(Le64(rsect.base[rp.Off:])) + } + + // ld -r could generate multiple section symbols for the + // same section but with different values, we have to take + // that into account + if issect(&peobj.pesym[symindex]) { + rp.Add += int64(peobj.pesym[symindex].value) + } + } + + sort.Sort(rbyoff(r[:rsect.sh.NumberOfRelocations])) + + s = rsect.sym + s.R = r + s.R = s.R[:rsect.sh.NumberOfRelocations] + } + + // enter sub-symbols into symbol table. + for i := 0; uint(i) < peobj.npesym; i++ { + if peobj.pesym[i].name == "" { + continue + } + if issect(&peobj.pesym[i]) { + continue + } + if uint(peobj.pesym[i].sectnum) > peobj.nsect { + continue + } + if peobj.pesym[i].sectnum > 0 { + sect = &peobj.sect[peobj.pesym[i].sectnum-1] + if sect.sym == nil { + continue + } + } + + if err = readpesym(peobj, i, &sym); err != nil { + goto bad + } + + s = sym.sym + if sym.sectnum == 0 { // extern + if s.Type == obj.SDYNIMPORT { + s.Plt = -2 // flag for dynimport in PE object files. + } + if s.Type == obj.SXREF && sym.value > 0 { // global data + s.Type = obj.SNOPTRDATA + s.Size = int64(sym.value) + } + + continue + } else if sym.sectnum > 0 && uint(sym.sectnum) <= peobj.nsect { + sect = &peobj.sect[sym.sectnum-1] + if sect.sym == nil { + Diag("%s: %s sym == 0!", pn, s.Name) + } + } else { + Diag("%s: %s sectnum < 0!", pn, s.Name) + } + + if sect == nil { + return + } + + if s.Outer != nil { + if s.Dupok != 0 { + continue + } + Exitf("%s: duplicate symbol reference: %s in both %s and %s", pn, s.Name, s.Outer.Name, sect.sym.Name) + } + + s.Sub = sect.sym.Sub + sect.sym.Sub = s + s.Type = sect.sym.Type | obj.SSUB + s.Value = int64(sym.value) + s.Size = 4 + s.Outer = sect.sym + if sect.sym.Type == obj.STEXT { + if s.External != 0 && s.Dupok == 0 { + Diag("%s: duplicate definition of %s", pn, s.Name) + } + s.External = 1 + } + } + + // Sort outer lists by address, adding to textp. + // This keeps textp in increasing address order. + for i := 0; uint(i) < peobj.nsect; i++ { + s = peobj.sect[i].sym + if s == nil { + continue + } + if s.Sub != nil { + s.Sub = listsort(s.Sub, valuecmp, listsubp) + } + if s.Type == obj.STEXT { + if s.Onlist != 0 { + log.Fatalf("symbol %s listed multiple times", s.Name) + } + s.Onlist = 1 + if Ctxt.Etextp != nil { + Ctxt.Etextp.Next = s + } else { + Ctxt.Textp = s + } + Ctxt.Etextp = s + for s = s.Sub; s != nil; s = s.Sub { + if s.Onlist != 0 { + log.Fatalf("symbol %s listed multiple times", s.Name) + } + s.Onlist = 1 + Ctxt.Etextp.Next = s + Ctxt.Etextp = s + } + } + } + + return + +bad: + Diag("%s: malformed pe file: %v", pn, err) +} + +func pemap(peobj *PeObj, sect *PeSect) int { + if sect.base != nil { + return 0 + } + + sect.base = make([]byte, sect.sh.SizeOfRawData) + if sect.sh.PointerToRawData == 0 { // .bss doesn't have data in object file + return 0 + } + if obj.Bseek(peobj.f, int64(peobj.base)+int64(sect.sh.PointerToRawData), 0) < 0 || obj.Bread(peobj.f, sect.base) != len(sect.base) { + return -1 + } + + return 0 +} + +func issect(s *PeSym) bool { + return s.sclass == IMAGE_SYM_CLASS_STATIC && s.type_ == 0 && s.name[0] == '.' +} + +func readpesym(peobj *PeObj, i int, y **PeSym) (err error) { + if uint(i) >= peobj.npesym || i < 0 { + err = fmt.Errorf("invalid pe symbol index") + return err + } + + sym := &peobj.pesym[i] + *y = sym + + var name string + if issect(sym) { + name = peobj.sect[sym.sectnum-1].sym.Name + } else { + name = sym.name + if strings.HasPrefix(name, "__imp_") { + name = name[6:] // __imp_Name => Name + } + if Thearch.Thechar == '8' && name[0] == '_' { + name = name[1:] // _Name => Name + } + } + + // remove last @XXX + if i := strings.LastIndex(name, "@"); i >= 0 { + name = name[:i] + } + + var s *LSym + switch sym.type_ { + default: + err = fmt.Errorf("%s: invalid symbol type %d", sym.name, sym.type_) + return err + + case IMAGE_SYM_DTYPE_FUNCTION, IMAGE_SYM_DTYPE_NULL: + switch sym.sclass { + case IMAGE_SYM_CLASS_EXTERNAL: //global + s = Linklookup(Ctxt, name, 0) + + case IMAGE_SYM_CLASS_NULL, IMAGE_SYM_CLASS_STATIC, IMAGE_SYM_CLASS_LABEL: + s = Linklookup(Ctxt, name, Ctxt.Version) + s.Dupok = 1 + + default: + err = fmt.Errorf("%s: invalid symbol binding %d", sym.name, sym.sclass) + return err + } + } + + if s != nil && s.Type == 0 && (sym.sclass != IMAGE_SYM_CLASS_STATIC || sym.value != 0) { + s.Type = obj.SXREF + } + if strings.HasPrefix(sym.name, "__imp_") { + s.Got = -2 // flag for __imp_ + } + sym.sym = s + + return nil +} diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go new file mode 100644 index 0000000000..a36cd0f8f4 --- /dev/null +++ b/src/cmd/link/internal/ld/lib.go @@ -0,0 +1,1946 @@ +// Inferno utils/8l/asm.c +// http://code.google.com/p/inferno-os/source/browse/utils/8l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "bufio" + "bytes" + "cmd/internal/obj" + "crypto/sha1" + "debug/elf" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "os/exec" + "path/filepath" + "strings" +) + +// Data layout and relocation. + +// Derived from Inferno utils/6l/l.h +// http://code.google.com/p/inferno-os/source/browse/utils/6l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +type Arch struct { + Thechar int + Ptrsize int + Intsize int + Regsize int + Funcalign int + Maxalign int + Minlc int + Dwarfregsp int + Dwarfreglr int + Linuxdynld string + Freebsddynld string + Netbsddynld string + Openbsddynld string + Dragonflydynld string + Solarisdynld string + Adddynrel func(*LSym, *Reloc) + Archinit func() + Archreloc func(*Reloc, *LSym, *int64) int + Archrelocvariant func(*Reloc, *LSym, int64) int64 + Asmb func() + Elfreloc1 func(*Reloc, int64) int + Elfsetupplt func() + Gentext func() + Machoreloc1 func(*Reloc, int64) int + PEreloc1 func(*Reloc, int64) bool + Lput func(uint32) + Wput func(uint16) + Vput func(uint64) +} + +type Rpath struct { + set bool + val string +} + +func (r *Rpath) Set(val string) error { + r.set = true + r.val = val + return nil +} + +func (r *Rpath) String() string { + return r.val +} + +var ( + Thearch Arch + datap *LSym + Debug [128]int + Lcsize int32 + rpath Rpath + Spsize int32 + Symsize int32 +) + +// Terrible but standard terminology. +// A segment describes a block of file to load into memory. +// A section further describes the pieces of that block for +// use in debuggers and such. + +const ( + MAXIO = 8192 + MINFUNC = 16 // minimum size for a function +) + +type Segment struct { + Rwx uint8 // permission as usual unix bits (5 = r-x etc) + Vaddr uint64 // virtual address + Length uint64 // length in memory + Fileoff uint64 // file offset + Filelen uint64 // length on disk + Sect *Section +} + +type Section struct { + Rwx uint8 + Extnum int16 + Align int32 + Name string + Vaddr uint64 + Length uint64 + Next *Section + Seg *Segment + Elfsect interface{} + Reloff uint64 + Rellen uint64 +} + +// DynlinkingGo returns whether we are producing Go code that can live +// in separate shared libraries linked together at runtime. +func DynlinkingGo() bool { + return Buildmode == BuildmodeShared || Linkshared +} + +var ( + Thestring string + Thelinkarch *LinkArch + outfile string + dynexp []*LSym + dynlib []string + ldflag []string + havedynamic int + Funcalign int + iscgo bool + elfglobalsymndx int + flag_installsuffix string + flag_race int + Buildmode BuildMode + Linkshared bool + tracksym string + interpreter string + tmpdir string + extld string + extldflags string + debug_s int // backup old value of debug['s'] + Ctxt *Link + HEADR int32 + HEADTYPE int32 + INITRND int32 + INITTEXT int64 + INITDAT int64 + INITENTRY string /* entry point */ + nerrors int + Linkmode int + liveness int64 +) + +// for dynexport field of LSym +const ( + CgoExportDynamic = 1 << 0 + CgoExportStatic = 1 << 1 +) + +var ( + Segtext Segment + Segrodata Segment + Segdata Segment + Segdwarf Segment +) + +/* set by call to mywhatsys() */ + +/* whence for ldpkg */ +const ( + FileObj = 0 + iota + ArchiveObj + Pkgdef +) + +var ( + headstring string + // buffered output + Bso obj.Biobuf +) + +var coutbuf struct { + *bufio.Writer + f *os.File +} + +const ( + // Whether to assume that the external linker is "gold" + // (http://sourceware.org/ml/binutils/2008-03/msg00162.html). + AssumeGoldLinker = 0 +) + +const ( + symname = "__.GOSYMDEF" + pkgname = "__.PKGDEF" +) + +var ( + // Set if we see an object compiled by the host compiler that is not + // from a package that is known to support internal linking mode. + externalobj = false + goroot string + goarch string + goos string + theline string +) + +func Lflag(arg string) { + Ctxt.Libdir = append(Ctxt.Libdir, arg) +} + +// A BuildMode indicates the sort of object we are building: +// "exe": build a main package and everything it imports into an executable. +// "c-shared": build a main package, plus all packages that it imports, into a +// single C shared library. The only callable symbols will be those functions +// marked as exported. +// "shared": combine all packages passed on the command line, and their +// dependencies, into a single shared library that will be used when +// building with the -linkshared option. +type BuildMode uint8 + +const ( + BuildmodeExe BuildMode = iota + BuildmodeCArchive + BuildmodeCShared + BuildmodeShared +) + +func (mode *BuildMode) Set(s string) error { + goos := obj.Getgoos() + goarch := obj.Getgoarch() + badmode := func() error { + return fmt.Errorf("buildmode %s not supported on %s/%s", s, goos, goarch) + } + switch s { + default: + return fmt.Errorf("invalid buildmode: %q", s) + case "exe": + *mode = BuildmodeExe + case "c-archive": + switch goos { + case "darwin", "linux": + default: + return badmode() + } + *mode = BuildmodeCArchive + case "c-shared": + if goarch != "amd64" && goarch != "arm" { + return badmode() + } + *mode = BuildmodeCShared + case "shared": + if goos != "linux" || goarch != "amd64" { + return badmode() + } + *mode = BuildmodeShared + } + return nil +} + +func (mode *BuildMode) String() string { + switch *mode { + case BuildmodeExe: + return "exe" + case BuildmodeCArchive: + return "c-archive" + case BuildmodeCShared: + return "c-shared" + case BuildmodeShared: + return "shared" + } + return fmt.Sprintf("BuildMode(%d)", uint8(*mode)) +} + +/* + * Unix doesn't like it when we write to a running (or, sometimes, + * recently run) binary, so remove the output file before writing it. + * On Windows 7, remove() can force a subsequent create() to fail. + * S_ISREG() does not exist on Plan 9. + */ +func mayberemoveoutfile() { + if fi, err := os.Lstat(outfile); err == nil && !fi.Mode().IsRegular() { + return + } + os.Remove(outfile) +} + +func libinit() { + Funcalign = Thearch.Funcalign + mywhatsys() // get goroot, goarch, goos + + // add goroot to the end of the libdir list. + suffix := "" + + suffixsep := "" + if flag_installsuffix != "" { + suffixsep = "_" + suffix = flag_installsuffix + } else if flag_race != 0 { + suffixsep = "_" + suffix = "race" + } + + Lflag(fmt.Sprintf("%s/pkg/%s_%s%s%s", goroot, goos, goarch, suffixsep, suffix)) + + mayberemoveoutfile() + f, err := os.OpenFile(outfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0775) + if err != nil { + Exitf("cannot create %s: %v", outfile, err) + } + + coutbuf.Writer = bufio.NewWriter(f) + coutbuf.f = f + + if INITENTRY == "" { + switch Buildmode { + case BuildmodeCShared, BuildmodeCArchive: + INITENTRY = fmt.Sprintf("_rt0_%s_%s_lib", goarch, goos) + case BuildmodeExe: + INITENTRY = fmt.Sprintf("_rt0_%s_%s", goarch, goos) + case BuildmodeShared: + // No INITENTRY for -buildmode=shared + default: + Diag("unknown INITENTRY for buildmode %v", Buildmode) + } + } + + if !DynlinkingGo() { + Linklookup(Ctxt, INITENTRY, 0).Type = obj.SXREF + } +} + +func Exitf(format string, a ...interface{}) { + fmt.Fprintf(os.Stderr, os.Args[0]+": "+format+"\n", a...) + if coutbuf.f != nil { + coutbuf.f.Close() + mayberemoveoutfile() + } + Exit(2) +} + +func errorexit() { + if coutbuf.f != nil { + if nerrors != 0 { + Cflush() + } + // For rmtemp run at atexit time on Windows. + if err := coutbuf.f.Close(); err != nil { + Exitf("close: %v", err) + } + } + + if nerrors != 0 { + if coutbuf.f != nil { + mayberemoveoutfile() + } + Exit(2) + } + + Exit(0) +} + +func loadinternal(name string) { + found := 0 + for i := 0; i < len(Ctxt.Libdir); i++ { + if Linkshared { + shlibname := fmt.Sprintf("%s/%s.shlibname", Ctxt.Libdir[i], name) + if Debug['v'] != 0 { + fmt.Fprintf(&Bso, "searching for %s.a in %s\n", name, shlibname) + } + if obj.Access(shlibname, obj.AEXIST) >= 0 { + addlibpath(Ctxt, "internal", "internal", "", name, shlibname) + found = 1 + break + } + } + pname := fmt.Sprintf("%s/%s.a", Ctxt.Libdir[i], name) + if Debug['v'] != 0 { + fmt.Fprintf(&Bso, "searching for %s.a in %s\n", name, pname) + } + if obj.Access(pname, obj.AEXIST) >= 0 { + addlibpath(Ctxt, "internal", "internal", pname, name, "") + found = 1 + break + } + } + + if found == 0 { + fmt.Fprintf(&Bso, "warning: unable to find %s.a\n", name) + } +} + +func loadlib() { + switch Buildmode { + case BuildmodeCShared: + s := Linklookup(Ctxt, "runtime.islibrary", 0) + s.Dupok = 1 + Adduint8(Ctxt, s, 1) + case BuildmodeCArchive: + s := Linklookup(Ctxt, "runtime.isarchive", 0) + s.Dupok = 1 + Adduint8(Ctxt, s, 1) + } + + loadinternal("runtime") + if Thearch.Thechar == '5' { + loadinternal("math") + } + if flag_race != 0 { + loadinternal("runtime/race") + } + + var i int + for i = 0; i < len(Ctxt.Library); i++ { + if Debug['v'] > 1 { + fmt.Fprintf(&Bso, "%5.2f autolib: %s (from %s)\n", obj.Cputime(), Ctxt.Library[i].File, Ctxt.Library[i].Objref) + } + iscgo = iscgo || Ctxt.Library[i].Pkg == "runtime/cgo" + if Ctxt.Library[i].Shlib != "" { + ldshlibsyms(Ctxt.Library[i].Shlib) + } else { + objfile(Ctxt.Library[i]) + } + } + + if Linkmode == LinkAuto { + if iscgo && externalobj { + Linkmode = LinkExternal + } else { + Linkmode = LinkInternal + } + + // Force external linking for android. + if goos == "android" { + Linkmode = LinkExternal + } + + // cgo on Darwin must use external linking + // we can always use external linking, but then there will be circular + // dependency problems when compiling natively (external linking requires + // runtime/cgo, runtime/cgo requires cmd/cgo, but cmd/cgo needs to be + // compiled using external linking.) + if (Thearch.Thechar == '5' || Thearch.Thechar == '7') && HEADTYPE == obj.Hdarwin && iscgo { + Linkmode = LinkExternal + } + } + + // cmd/7l doesn't support cgo internal linking + // This is https://golang.org/issue/10373. + if iscgo && goarch == "arm64" { + Linkmode = LinkExternal + } + + if Linkmode == LinkExternal && !iscgo { + // This indicates a user requested -linkmode=external. + // The startup code uses an import of runtime/cgo to decide + // whether to initialize the TLS. So give it one. This could + // be handled differently but it's an unusual case. + loadinternal("runtime/cgo") + + if i < len(Ctxt.Library) { + if Ctxt.Library[i].Shlib != "" { + ldshlibsyms(Ctxt.Library[i].Shlib) + } else { + if DynlinkingGo() { + Exitf("cannot implicitly include runtime/cgo in a shared library") + } + objfile(Ctxt.Library[i]) + } + } + } + + if Linkmode == LinkInternal { + // Drop all the cgo_import_static declarations. + // Turns out we won't be needing them. + for s := Ctxt.Allsym; s != nil; s = s.Allsym { + if s.Type == obj.SHOSTOBJ { + // If a symbol was marked both + // cgo_import_static and cgo_import_dynamic, + // then we want to make it cgo_import_dynamic + // now. + if s.Extname != "" && s.Dynimplib != "" && s.Cgoexport == 0 { + s.Type = obj.SDYNIMPORT + } else { + s.Type = 0 + } + } + } + } + + tlsg := Linklookup(Ctxt, "runtime.tlsg", 0) + + // For most ports, runtime.tlsg is a placeholder symbol for TLS + // relocation. However, the Android and Darwin arm ports need it + // to be a real variable. + // + // TODO(crawshaw): android should require leaving the tlsg->type + // alone (as the runtime-provided SNOPTRBSS) just like darwin/arm. + // But some other part of the linker is expecting STLSBSS. + if tlsg.Type != obj.SDYNIMPORT && (goos != "darwin" || Thearch.Thechar != '5') { + tlsg.Type = obj.STLSBSS + } + tlsg.Size = int64(Thearch.Ptrsize) + tlsg.Reachable = true + Ctxt.Tlsg = tlsg + + // Now that we know the link mode, trim the dynexp list. + x := CgoExportDynamic + + if Linkmode == LinkExternal { + x = CgoExportStatic + } + w := 0 + for i := 0; i < len(dynexp); i++ { + if int(dynexp[i].Cgoexport)&x != 0 { + dynexp[w] = dynexp[i] + w++ + } + } + dynexp = dynexp[:w] + + // In internal link mode, read the host object files. + if Linkmode == LinkInternal { + hostobjs() + } else { + hostlinksetup() + } + + // We've loaded all the code now. + // If there are no dynamic libraries needed, gcc disables dynamic linking. + // Because of this, glibc's dynamic ELF loader occasionally (like in version 2.13) + // assumes that a dynamic binary always refers to at least one dynamic library. + // Rather than be a source of test cases for glibc, disable dynamic linking + // the same way that gcc would. + // + // Exception: on OS X, programs such as Shark only work with dynamic + // binaries, so leave it enabled on OS X (Mach-O) binaries. + // Also leave it enabled on Solaris which doesn't support + // statically linked binaries. + if Buildmode == BuildmodeExe && havedynamic == 0 && HEADTYPE != obj.Hdarwin && HEADTYPE != obj.Hsolaris { + Debug['d'] = 1 + } + + importcycles() +} + +/* + * look for the next file in an archive. + * adapted from libmach. + */ +func nextar(bp *obj.Biobuf, off int64, a *ArHdr) int64 { + if off&1 != 0 { + off++ + } + obj.Bseek(bp, off, 0) + buf := make([]byte, SAR_HDR) + if n := obj.Bread(bp, buf); n < len(buf) { + if n >= 0 { + return 0 + } + return -1 + } + + a.name = artrim(buf[0:16]) + a.date = artrim(buf[16:28]) + a.uid = artrim(buf[28:34]) + a.gid = artrim(buf[34:40]) + a.mode = artrim(buf[40:48]) + a.size = artrim(buf[48:58]) + a.fmag = artrim(buf[58:60]) + + arsize := atolwhex(a.size) + if arsize&1 != 0 { + arsize++ + } + return int64(arsize) + SAR_HDR +} + +func objfile(lib *Library) { + pkg := pathtoprefix(lib.Pkg) + + if Debug['v'] > 1 { + 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(lib.File) + if err != nil { + Exitf("cannot open file %s: %v", lib.File, err) + } + + magbuf := make([]byte, len(ARMAG)) + if obj.Bread(f, magbuf) != len(magbuf) || !strings.HasPrefix(string(magbuf), ARMAG) { + /* load it as a regular file */ + l := obj.Bseek(f, 0, 2) + + obj.Bseek(f, 0, 0) + ldobj(f, pkg, l, lib.File, lib.File, FileObj) + obj.Bterm(f) + + return + } + + /* skip over optional __.GOSYMDEF and process __.PKGDEF */ + off := obj.Boffset(f) + + var arhdr ArHdr + l := nextar(f, off, &arhdr) + var pname string + if l <= 0 { + Diag("%s: short read on archive file symbol header", lib.File) + goto out + } + + if strings.HasPrefix(arhdr.name, symname) { + off += l + l = nextar(f, off, &arhdr) + if l <= 0 { + 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", 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), lib.File, Pkgdef) + } + + /* + * load all the object files from the archive now. + * this gives us sequential file access and keeps us + * from needing to come back later to pick up more + * objects. it breaks the usual C archive model, but + * this is Go, not C. the common case in Go is that + * we need to load all the objects, and then we throw away + * the individual symbols that are unused. + * + * loading every object will also make it possible to + * load foreign objects not referenced by __.GOSYMDEF. + */ + for { + l = nextar(f, off, &arhdr) + if l == 0 { + break + } + if l < 0 { + Exitf("%s: malformed archive", lib.File) + } + + off += l + + pname = fmt.Sprintf("%s(%s)", lib.File, arhdr.name) + l = atolwhex(arhdr.size) + ldobj(f, pkg, l, pname, lib.File, ArchiveObj) + } + +out: + obj.Bterm(f) +} + +type Hostobj struct { + ld func(*obj.Biobuf, string, int64, string) + pkg string + pn string + file string + off int64 + length int64 +} + +var hostobj []Hostobj + +// These packages can use internal linking mode. +// Others trigger external mode. +var internalpkg = []string{ + "crypto/x509", + "net", + "os/user", + "runtime/cgo", + "runtime/race", +} + +func ldhostobj(ld func(*obj.Biobuf, string, int64, string), f *obj.Biobuf, pkg string, length int64, pn string, file string) { + isinternal := false + for i := 0; i < len(internalpkg); i++ { + if pkg == internalpkg[i] { + isinternal = true + break + } + } + + // DragonFly declares errno with __thread, which results in a symbol + // type of R_386_TLS_GD or R_X86_64_TLSGD. The Go linker does not + // currently know how to handle TLS relocations, hence we have to + // force external linking for any libraries that link in code that + // uses errno. This can be removed if the Go linker ever supports + // these relocation types. + if HEADTYPE == obj.Hdragonfly { + if pkg == "net" || pkg == "os/user" { + isinternal = false + } + } + + if !isinternal { + externalobj = true + } + + hostobj = append(hostobj, Hostobj{}) + h := &hostobj[len(hostobj)-1] + h.ld = ld + h.pkg = pkg + h.pn = pn + h.file = file + h.off = obj.Boffset(f) + h.length = length +} + +func hostobjs() { + var f *obj.Biobuf + var h *Hostobj + + for i := 0; i < len(hostobj); i++ { + h = &hostobj[i] + var err error + f, err = obj.Bopenr(h.file) + if f == nil { + Exitf("cannot reopen %s: %v", h.pn, err) + } + + obj.Bseek(f, h.off, 0) + h.ld(f, h.pkg, h.length, h.pn) + obj.Bterm(f) + } +} + +// provided by lib9 + +func rmtemp() { + os.RemoveAll(tmpdir) +} + +func hostlinksetup() { + if Linkmode != LinkExternal { + return + } + + // create temporary directory and arrange cleanup + if tmpdir == "" { + dir, err := ioutil.TempDir("", "go-link-") + if err != nil { + log.Fatal(err) + } + tmpdir = dir + AtExit(rmtemp) + } + + // change our output to temporary object file + coutbuf.f.Close() + + p := fmt.Sprintf("%s/go.o", tmpdir) + var err error + f, err := os.OpenFile(p, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0775) + if err != nil { + Exitf("cannot create %s: %v", p, err) + } + + coutbuf.Writer = bufio.NewWriter(f) + coutbuf.f = f +} + +// hostobjCopy creates a copy of the object files in hostobj in a +// temporary directory. +func hostobjCopy() (paths []string) { + for i, h := range hostobj { + f, err := os.Open(h.file) + if err != nil { + Exitf("cannot reopen %s: %v", h.pn, err) + } + if _, err := f.Seek(h.off, 0); err != nil { + Exitf("cannot seek %s: %v", h.pn, err) + } + + p := fmt.Sprintf("%s/%06d.o", tmpdir, i) + paths = append(paths, p) + w, err := os.Create(p) + if err != nil { + Exitf("cannot create %s: %v", p, err) + } + if _, err := io.CopyN(w, f, h.length); err != nil { + Exitf("cannot write %s: %v", p, err) + } + if err := w.Close(); err != nil { + Exitf("cannot close %s: %v", p, err) + } + } + return paths +} + +// archive builds a .a archive from the hostobj object files. +func archive() { + if Buildmode != BuildmodeCArchive { + return + } + + os.Remove(outfile) + argv := []string{"ar", "-q", "-c", outfile} + argv = append(argv, hostobjCopy()...) + argv = append(argv, fmt.Sprintf("%s/go.o", tmpdir)) + + if Debug['v'] != 0 { + fmt.Fprintf(&Bso, "archive: %s\n", strings.Join(argv, " ")) + Bso.Flush() + } + + if out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput(); err != nil { + Exitf("running %s failed: %v\n%s", argv[0], err, out) + } +} + +func hostlink() { + if Linkmode != LinkExternal || nerrors > 0 { + return + } + if Buildmode == BuildmodeCArchive { + return + } + + if extld == "" { + extld = "gcc" + } + + var argv []string + argv = append(argv, extld) + switch Thearch.Thechar { + case '8': + argv = append(argv, "-m32") + + case '6', '9': + argv = append(argv, "-m64") + + case '5': + argv = append(argv, "-marm") + + case '7': + // nothing needed + } + + if Debug['s'] == 0 && debug_s == 0 { + argv = append(argv, "-gdwarf-2") + } else { + argv = append(argv, "-s") + } + + if HEADTYPE == obj.Hdarwin { + argv = append(argv, "-Wl,-no_pie,-pagezero_size,4000000,-headerpad,1144") + } + if HEADTYPE == obj.Hopenbsd { + argv = append(argv, "-Wl,-nopie") + } + if HEADTYPE == obj.Hwindows { + if headstring == "windowsgui" { + argv = append(argv, "-mwindows") + } else { + argv = append(argv, "-mconsole") + } + } + + if Iself && AssumeGoldLinker != 0 /*TypeKind(100016)*/ { + argv = append(argv, "-Wl,--rosegment") + } + + switch Buildmode { + case BuildmodeCShared: + argv = append(argv, "-Wl,-Bsymbolic") + argv = append(argv, "-shared") + case BuildmodeShared: + // TODO(mwhudson): unless you do this, dynamic relocations fill + // out the findfunctab table and for some reason shared libraries + // and the executable both define a main function and putting the + // address of executable's main into the shared libraries + // findfunctab violates the assumptions of the runtime. TBH, I + // think we may well end up wanting to use -Bsymbolic here + // anyway. + argv = append(argv, "-Wl,-Bsymbolic-functions") + argv = append(argv, "-shared") + } + + if Linkshared && Iself { + // We force all symbol resolution to be done at program startup + // because lazy PLT resolution can use large amounts of stack at + // times we cannot allow it to do so. + argv = append(argv, "-Wl,-znow") + } + + argv = append(argv, "-o") + argv = append(argv, outfile) + + if rpath.val != "" { + argv = append(argv, fmt.Sprintf("-Wl,-rpath,%s", rpath.val)) + } + + // Force global symbols to be exported for dlopen, etc. + if Iself { + argv = append(argv, "-rdynamic") + } + + if strings.Contains(argv[0], "clang") { + argv = append(argv, "-Qunused-arguments") + } + + argv = append(argv, hostobjCopy()...) + 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) + } + base = strings.TrimSuffix(base, ".so") + base = strings.TrimPrefix(base, "lib") + argv = append(argv, "-l"+base) + } + } + + argv = append(argv, ldflag...) + + for _, p := range strings.Fields(extldflags) { + argv = append(argv, p) + + // clang, unlike GCC, passes -rdynamic to the linker + // even when linking with -static, causing a linker + // error when using GNU ld. So take out -rdynamic if + // we added it. We do it in this order, rather than + // only adding -rdynamic later, so that -extldflags + // can override -rdynamic without using -static. + if Iself && p == "-static" { + for i := range argv { + if argv[i] == "-rdynamic" { + argv[i] = "-static" + } + } + } + } + if HEADTYPE == obj.Hwindows { + argv = append(argv, peimporteddlls()...) + } + + if Debug['v'] != 0 { + fmt.Fprintf(&Bso, "host link:") + for _, v := range argv { + fmt.Fprintf(&Bso, " %q", v) + } + fmt.Fprintf(&Bso, "\n") + Bso.Flush() + } + + 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) { + eof := obj.Boffset(f) + length + + start := obj.Boffset(f) + c1 := obj.Bgetc(f) + c2 := obj.Bgetc(f) + c3 := obj.Bgetc(f) + c4 := obj.Bgetc(f) + obj.Bseek(f, start, 0) + + magic := uint32(c1)<<24 | uint32(c2)<<16 | uint32(c3)<<8 | uint32(c4) + if magic == 0x7f454c46 { // \x7F E L F + ldhostobj(ldelf, f, pkg, length, pn, file) + return + } + + if magic&^1 == 0xfeedface || magic&^0x01000000 == 0xcefaedfe { + ldhostobj(ldmacho, f, pkg, length, pn, file) + return + } + + if c1 == 0x4c && c2 == 0x01 || c1 == 0x64 && c2 == 0x86 { + ldhostobj(ldpe, f, pkg, length, pn, file) + return + } + + /* check the header */ + line := obj.Brdline(f, '\n') + if line == "" { + if obj.Blinelen(f) > 0 { + Diag("%s: not an object file", pn) + return + } + Diag("truncated object file: %s", pn) + return + } + + if !strings.HasPrefix(line, "go object ") { + if strings.HasSuffix(pn, ".go") { + Exitf("%cl: input %s is not .%c file (use %cg to compile .go files)", Thearch.Thechar, pn, Thearch.Thechar, Thearch.Thechar) + } + + if line == Thestring { + // old header format: just $GOOS + Diag("%s: stale object file", pn) + return + } + + Diag("%s: not an object file", pn) + return + } + + // First, check that the basic goos, goarch, and version match. + t := fmt.Sprintf("%s %s %s ", goos, obj.Getgoarch(), obj.Getgoversion()) + + line = strings.TrimRight(line, "\n") + if !strings.HasPrefix(line[10:]+" ", t) && Debug['f'] == 0 { + Diag("%s: object is [%s] expected [%s]", pn, line[10:], t) + return + } + + // Second, check that longer lines match each other exactly, + // so that the Go compiler and write additional information + // that must be the same from run to run. + if len(line) >= len(t)+10 { + if theline == "" { + theline = line[10:] + } else if theline != line[10:] { + Diag("%s: object is [%s] expected [%s]", pn, line[10:], theline) + return + } + } + + /* skip over exports and other info -- ends with \n!\n */ + import0 := obj.Boffset(f) + + c1 = '\n' // the last line ended in \n + c2 = obj.Bgetc(f) + c3 = obj.Bgetc(f) + for c1 != '\n' || c2 != '!' || c3 != '\n' { + c1 = c2 + c2 = c3 + c3 = obj.Bgetc(f) + if c3 == obj.Beof { + Diag("truncated object file: %s", pn) + return + } + } + + import1 := obj.Boffset(f) + + obj.Bseek(f, import0, 0) + ldpkg(f, pkg, import1-import0-2, pn, whence) // -2 for !\n + obj.Bseek(f, import1, 0) + + 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 := "" + for _, libdir := range Ctxt.Libdir { + libpath = filepath.Join(libdir, shlib) + if _, err := os.Stat(libpath); err == nil { + found = true + break + } + } + if !found { + Diag("cannot find shared library: %s", shlib) + return + } + for _, processedlib := range Ctxt.Shlibs { + if processedlib.Path == libpath { + return + } + } + if Ctxt.Debugvlog > 1 && Ctxt.Bso != nil { + fmt.Fprintf(Ctxt.Bso, "%5.2f ldshlibsyms: found library with name %s at %s\n", obj.Cputime(), shlib, libpath) + Ctxt.Bso.Flush() + } + + f, err := elf.Open(libpath) + if err != nil { + Diag("cannot open shared library: %s", libpath) + return + } + defer f.Close() + syms, err := f.Symbols() + 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{} + 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 + } + 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) + } + if s.Name == "go.link.abihashbytes" { + hash = readelfsymboldata(f, &s) + } + if elf.ST_BIND(s.Info) != elf.STB_GLOBAL { + continue + } + lsym := Linklookup(Ctxt, s.Name, 0) + if lsym.Type != 0 && lsym.Dupok == 0 { + Diag( + "Found duplicate symbol %s reading from %s, first found in %s", + s.Name, shlib, lsym.File) + } + 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 { + 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 + // list. + var last *LSym + + for s := Ctxt.Textp; s != nil; s = s.Next { + if s.Type == obj.SDYNIMPORT { + continue + } + + if last == nil { + Ctxt.Textp = s + } else { + last.Next = s + } + last = s + } + + if last == nil { + Ctxt.Textp = nil + Ctxt.Etextp = nil + } else { + last.Next = nil + Ctxt.Etextp = last + } + + Ctxt.Shlibs = append(Ctxt.Shlibs, Shlib{Path: libpath, Hash: hash}) +} + +func mywhatsys() { + goroot = obj.Getgoroot() + goos = obj.Getgoos() + goarch = obj.Getgoarch() + + if !strings.HasPrefix(goarch, Thestring) { + log.Fatalf("cannot use %cc with GOARCH=%s", Thearch.Thechar, goarch) + } +} + +// Copied from ../gc/subr.c:/^pathtoprefix; must stay in sync. +/* + * Convert raw string to the prefix that will be used in the symbol table. + * Invalid bytes turn into %xx. Right now the only bytes that need + * escaping are %, ., and ", but we escape all control characters too. + * + * If you edit this, edit ../gc/subr.c:/^pathtoprefix too. + * If you edit this, edit ../../debug/goobj/read.go:/importPathToPrefix too. + */ +func pathtoprefix(s string) string { + slash := strings.LastIndex(s, "/") + for i := 0; i < len(s); i++ { + c := s[i] + if c <= ' ' || i >= slash && c == '.' || c == '%' || c == '"' || c >= 0x7F { + var buf bytes.Buffer + for i := 0; i < len(s); i++ { + c := s[i] + if c <= ' ' || i >= slash && c == '.' || c == '%' || c == '"' || c >= 0x7F { + fmt.Fprintf(&buf, "%%%02x", c) + continue + } + buf.WriteByte(c) + } + return buf.String() + } + } + return s +} + +func addsection(seg *Segment, name string, rwx int) *Section { + var l **Section + + for l = &seg.Sect; *l != nil; l = &(*l).Next { + } + sect := new(Section) + sect.Rwx = uint8(rwx) + sect.Name = name + sect.Seg = seg + sect.Align = int32(Thearch.Ptrsize) // everything is at least pointer-aligned + *l = sect + return sect +} + +func Le16(b []byte) uint16 { + return uint16(b[0]) | uint16(b[1])<<8 +} + +func Le32(b []byte) uint32 { + return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 +} + +func Le64(b []byte) uint64 { + return uint64(Le32(b)) | uint64(Le32(b[4:]))<<32 +} + +func Be16(b []byte) uint16 { + return uint16(b[0])<<8 | uint16(b[1]) +} + +func Be32(b []byte) uint32 { + return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]) +} + +func Be64(b []byte) uint64 { + return uint64(Be32(b))<<32 | uint64(Be32(b[4:])) +} + +type Chain struct { + sym *LSym + up *Chain + limit int // limit on entry to sym +} + +var ( + morestack *LSym + newstack *LSym +) + +// TODO: Record enough information in new object files to +// allow stack checks here. + +func haslinkregister() bool { + return Thearch.Thechar == '5' || Thearch.Thechar == '9' || Thearch.Thechar == '7' +} + +func callsize() int { + if haslinkregister() { + return 0 + } + return Thearch.Regsize +} + +func dostkcheck() { + var ch Chain + + morestack = Linklookup(Ctxt, "runtime.morestack", 0) + newstack = Linklookup(Ctxt, "runtime.newstack", 0) + + // Every splitting function ensures that there are at least StackLimit + // bytes available below SP when the splitting prologue finishes. + // If the splitting function calls F, then F begins execution with + // at least StackLimit - callsize() bytes available. + // Check that every function behaves correctly with this amount + // of stack, following direct calls in order to piece together chains + // of non-splitting functions. + ch.up = nil + + ch.limit = obj.StackLimit - callsize() + + // Check every function, but do the nosplit functions in a first pass, + // to make the printed failure chains as short as possible. + for s := Ctxt.Textp; s != nil; s = s.Next { + // runtime.racesymbolizethunk is called from gcc-compiled C + // code running on the operating system thread stack. + // It uses more than the usual amount of stack but that's okay. + if s.Name == "runtime.racesymbolizethunk" { + continue + } + + if s.Nosplit != 0 { + Ctxt.Cursym = s + ch.sym = s + stkcheck(&ch, 0) + } + } + + for s := Ctxt.Textp; s != nil; s = s.Next { + if s.Nosplit == 0 { + Ctxt.Cursym = s + ch.sym = s + stkcheck(&ch, 0) + } + } +} + +func stkcheck(up *Chain, depth int) int { + limit := up.limit + s := up.sym + + // Don't duplicate work: only need to consider each + // function at top of safe zone once. + if limit == obj.StackLimit-callsize() { + if s.Stkcheck != 0 { + return 0 + } + s.Stkcheck = 1 + } + + if depth > 100 { + Diag("nosplit stack check too deep") + stkbroke(up, 0) + return -1 + } + + if s.External != 0 || s.Pcln == nil { + // external function. + // should never be called directly. + // only diagnose the direct caller. + // TODO(mwhudson): actually think about this. + if depth == 1 && s.Type != obj.SXREF && !DynlinkingGo() { + Diag("call to external function %s", s.Name) + } + return -1 + } + + if limit < 0 { + stkbroke(up, limit) + return -1 + } + + // morestack looks like it calls functions, + // but it switches the stack pointer first. + if s == morestack { + return 0 + } + + var ch Chain + ch.up = up + + // Walk through sp adjustments in function, consuming relocs. + ri := 0 + + endr := len(s.R) + var ch1 Chain + var pcsp Pciter + var r *Reloc + for pciterinit(Ctxt, &pcsp, &s.Pcln.Pcsp); pcsp.done == 0; pciternext(&pcsp) { + // pcsp.value is in effect for [pcsp.pc, pcsp.nextpc). + + // Check stack size in effect for this span. + if int32(limit)-pcsp.value < 0 { + stkbroke(up, int(int32(limit)-pcsp.value)) + return -1 + } + + // Process calls in this span. + for ; ri < endr && uint32(s.R[ri].Off) < pcsp.nextpc; ri++ { + r = &s.R[ri] + switch r.Type { + // Direct call. + case obj.R_CALL, obj.R_CALLARM, obj.R_CALLARM64, obj.R_CALLPOWER: + ch.limit = int(int32(limit) - pcsp.value - int32(callsize())) + + ch.sym = r.Sym + if stkcheck(&ch, depth+1) < 0 { + return -1 + } + + // If this is a call to morestack, we've just raised our limit back + // to StackLimit beyond the frame size. + if strings.HasPrefix(r.Sym.Name, "runtime.morestack") { + limit = int(obj.StackLimit + s.Locals) + if haslinkregister() { + limit += Thearch.Regsize + } + } + + // Indirect call. Assume it is a call to a splitting function, + // so we have to make sure it can call morestack. + // Arrange the data structures to report both calls, so that + // if there is an error, stkprint shows all the steps involved. + case obj.R_CALLIND: + ch.limit = int(int32(limit) - pcsp.value - int32(callsize())) + + ch.sym = nil + ch1.limit = ch.limit - callsize() // for morestack in called prologue + ch1.up = &ch + ch1.sym = morestack + if stkcheck(&ch1, depth+2) < 0 { + return -1 + } + } + } + } + + return 0 +} + +func stkbroke(ch *Chain, limit int) { + Diag("nosplit stack overflow") + stkprint(ch, limit) +} + +func stkprint(ch *Chain, limit int) { + var name string + + if ch.sym != nil { + name = ch.sym.Name + } else { + name = "function pointer" + } + + if ch.up == nil { + // top of chain. ch->sym != nil. + if ch.sym.Nosplit != 0 { + fmt.Printf("\t%d\tassumed on entry to %s\n", ch.limit, name) + } else { + fmt.Printf("\t%d\tguaranteed after split check in %s\n", ch.limit, name) + } + } else { + stkprint(ch.up, ch.limit+callsize()) + if !haslinkregister() { + fmt.Printf("\t%d\ton entry to %s\n", ch.limit, name) + } + } + + if ch.limit != limit { + fmt.Printf("\t%d\tafter %s uses %d\n", limit, name, ch.limit-limit) + } +} + +func Yconv(s *LSym) string { + var fp string + + if s == nil { + fp += fmt.Sprintf("") + } else { + fmt_ := "" + fmt_ += fmt.Sprintf("%s @0x%08x [%d]", s.Name, int64(s.Value), int64(s.Size)) + for i := 0; int64(i) < s.Size; i++ { + if i%8 == 0 { + fmt_ += fmt.Sprintf("\n\t0x%04x ", i) + } + fmt_ += fmt.Sprintf("%02x ", s.P[i]) + } + + fmt_ += fmt.Sprintf("\n") + for i := 0; i < len(s.R); i++ { + fmt_ += fmt.Sprintf("\t0x%04x[%x] %d %s[%x]\n", s.R[i].Off, s.R[i].Siz, s.R[i].Type, s.R[i].Sym.Name, int64(s.R[i].Add)) + } + + str := fmt_ + fp += str + } + + return fp +} + +func Cflush() { + if err := coutbuf.Writer.Flush(); err != nil { + Exitf("flushing %s: %v", coutbuf.f.Name(), err) + } +} + +func Cpos() int64 { + off, err := coutbuf.f.Seek(0, 1) + if err != nil { + Exitf("seeking in output [0, 1]: %v", err) + } + return off + int64(coutbuf.Buffered()) +} + +func Cseek(p int64) { + Cflush() + if _, err := coutbuf.f.Seek(p, 0); err != nil { + Exitf("seeking in output [0, 1]: %v", err) + } +} + +func Cwrite(p []byte) { + coutbuf.Write(p) +} + +func Cput(c uint8) { + coutbuf.WriteByte(c) +} + +func usage() { + fmt.Fprintf(os.Stderr, "usage: %cl [options] obj.%c\n", Thearch.Thechar, Thearch.Thechar) + obj.Flagprint(2) + Exit(2) +} + +func setheadtype(s string) { + h := headtype(s) + if h < 0 { + Exitf("unknown header type -H %s", s) + } + + headstring = s + HEADTYPE = int32(headtype(s)) +} + +func setinterp(s string) { + Debug['I'] = 1 // denote cmdline interpreter override + interpreter = s +} + +func doversion() { + Exitf("version %s", obj.Getgoversion()) +} + +func genasmsym(put func(*LSym, string, int, int64, int64, int, *LSym)) { + // These symbols won't show up in the first loop below because we + // skip STEXT symbols. Normal STEXT symbols are emitted by walking textp. + s := Linklookup(Ctxt, "runtime.text", 0) + + if s.Type == obj.STEXT { + put(s, s.Name, 'T', s.Value, s.Size, int(s.Version), nil) + } + s = Linklookup(Ctxt, "runtime.etext", 0) + if s.Type == obj.STEXT { + put(s, s.Name, 'T', s.Value, s.Size, int(s.Version), nil) + } + + for s := Ctxt.Allsym; s != nil; s = s.Allsym { + if s.Hide != 0 || (s.Name[0] == '.' && s.Version == 0 && s.Name != ".rathole") { + continue + } + switch s.Type & obj.SMASK { + case obj.SCONST, + obj.SRODATA, + obj.SSYMTAB, + obj.SPCLNTAB, + obj.SINITARR, + obj.SDATA, + obj.SNOPTRDATA, + obj.SELFROSECT, + obj.SMACHOGOT, + obj.STYPE, + obj.SSTRING, + obj.SGOSTRING, + obj.SGOFUNC, + obj.SWINDOWS: + if !s.Reachable { + continue + } + put(s, s.Name, 'D', Symaddr(s), s.Size, int(s.Version), s.Gotype) + + case obj.SBSS, obj.SNOPTRBSS: + if !s.Reachable { + continue + } + if len(s.P) > 0 { + Diag("%s should not be bss (size=%d type=%d special=%d)", s.Name, int(len(s.P)), s.Type, s.Special) + } + put(s, s.Name, 'B', Symaddr(s), s.Size, int(s.Version), s.Gotype) + + case obj.SFILE: + put(nil, s.Name, 'f', s.Value, 0, int(s.Version), nil) + + case obj.SHOSTOBJ: + if HEADTYPE == obj.Hwindows || Iself { + put(s, s.Name, 'U', s.Value, 0, int(s.Version), nil) + } + + case obj.SDYNIMPORT: + if !s.Reachable { + continue + } + put(s, s.Extname, 'U', 0, 0, int(s.Version), nil) + + case obj.STLSBSS: + if Linkmode == LinkExternal && HEADTYPE != obj.Hopenbsd { + var type_ int + if goos == "android" { + type_ = 'B' + } else { + type_ = 't' + } + put(s, s.Name, type_, Symaddr(s), s.Size, int(s.Version), s.Gotype) + } + } + } + + var a *Auto + var off int32 + for s := Ctxt.Textp; s != nil; s = s.Next { + put(s, s.Name, 'T', s.Value, s.Size, int(s.Version), s.Gotype) + + // NOTE(ality): acid can't produce a stack trace without .frame symbols + put(nil, ".frame", 'm', int64(s.Locals)+int64(Thearch.Ptrsize), 0, 0, nil) + + for a = s.Autom; a != nil; a = a.Link { + // Emit a or p according to actual offset, even if label is wrong. + // This avoids negative offsets, which cannot be encoded. + if a.Name != obj.A_AUTO && a.Name != obj.A_PARAM { + continue + } + + // compute offset relative to FP + if a.Name == obj.A_PARAM { + off = a.Aoffset + } else { + off = a.Aoffset - int32(Thearch.Ptrsize) + } + + // FP + if off >= 0 { + put(nil, a.Asym.Name, 'p', int64(off), 0, 0, a.Gotype) + continue + } + + // SP + if off <= int32(-Thearch.Ptrsize) { + put(nil, a.Asym.Name, 'a', -(int64(off) + int64(Thearch.Ptrsize)), 0, 0, a.Gotype) + continue + } + } + } + + // Otherwise, off is addressing the saved program counter. + // Something underhanded is going on. Say nothing. + if Debug['v'] != 0 || Debug['n'] != 0 { + fmt.Fprintf(&Bso, "%5.2f symsize = %d\n", obj.Cputime(), uint32(Symsize)) + } + Bso.Flush() +} + +func Symaddr(s *LSym) int64 { + if !s.Reachable { + Diag("unreachable symbol in symaddr - %s", s.Name) + } + return s.Value +} + +func xdefine(p string, t int, v int64) { + s := Linklookup(Ctxt, p, 0) + s.Type = int16(t) + s.Value = v + s.Reachable = true + s.Special = 1 + s.Local = true +} + +func datoff(addr int64) int64 { + if uint64(addr) >= Segdata.Vaddr { + return int64(uint64(addr) - Segdata.Vaddr + Segdata.Fileoff) + } + if uint64(addr) >= Segtext.Vaddr { + return int64(uint64(addr) - Segtext.Vaddr + Segtext.Fileoff) + } + Diag("datoff %#x", addr) + return 0 +} + +func Entryvalue() int64 { + a := INITENTRY + if a[0] >= '0' && a[0] <= '9' { + return atolwhex(a) + } + s := Linklookup(Ctxt, a, 0) + if s.Type == 0 { + return INITTEXT + } + if s.Type != obj.STEXT { + Diag("entry not text: %s", s.Name) + } + return s.Value +} + +func undefsym(s *LSym) { + var r *Reloc + + Ctxt.Cursym = s + for i := 0; i < len(s.R); i++ { + r = &s.R[i] + if r.Sym == nil { // happens for some external ARM relocs + continue + } + if r.Sym.Type == obj.Sxxx || r.Sym.Type == obj.SXREF { + Diag("undefined: %s", r.Sym.Name) + } + if !r.Sym.Reachable { + Diag("use of unreachable symbol: %s", r.Sym.Name) + } + } +} + +func undef() { + for s := Ctxt.Textp; s != nil; s = s.Next { + undefsym(s) + } + for s := datap; s != nil; s = s.Next { + undefsym(s) + } + if nerrors > 0 { + errorexit() + } +} + +func callgraph() { + if Debug['c'] == 0 { + return + } + + var i int + var r *Reloc + for s := Ctxt.Textp; s != nil; s = s.Next { + for i = 0; i < len(s.R); i++ { + r = &s.R[i] + if r.Sym == nil { + continue + } + if (r.Type == obj.R_CALL || r.Type == obj.R_CALLARM || r.Type == obj.R_CALLPOWER) && r.Sym.Type == obj.STEXT { + fmt.Fprintf(&Bso, "%s calls %s\n", s.Name, r.Sym.Name) + } + } + } +} + +func Diag(format string, args ...interface{}) { + tn := "" + sep := "" + if Ctxt.Cursym != nil { + tn = Ctxt.Cursym.Name + sep = ": " + } + fmt.Printf("%s%s%s\n", tn, sep, fmt.Sprintf(format, args...)) + + nerrors++ + if nerrors > 20 { + Exitf("too many errors") + } +} + +func checkgo() { + if Debug['C'] == 0 { + return + } + + // TODO(rsc,khr): Eventually we want to get to no Go-called C functions at all, + // which would simplify this logic quite a bit. + + // Mark every Go-called C function with cfunc=2, recursively. + var changed int + var i int + var r *Reloc + var s *LSym + for { + changed = 0 + for s = Ctxt.Textp; s != nil; s = s.Next { + if s.Cfunc == 0 || (s.Cfunc == 2 && s.Nosplit != 0) { + for i = 0; i < len(s.R); i++ { + r = &s.R[i] + if r.Sym == nil { + continue + } + if (r.Type == obj.R_CALL || r.Type == obj.R_CALLARM) && r.Sym.Type == obj.STEXT { + if r.Sym.Cfunc == 1 { + changed = 1 + r.Sym.Cfunc = 2 + } + } + } + } + } + if changed == 0 { + break + } + } + + // Complain about Go-called C functions that can split the stack + // (that can be preempted for garbage collection or trigger a stack copy). + for s := Ctxt.Textp; s != nil; s = s.Next { + if s.Cfunc == 0 || (s.Cfunc == 2 && s.Nosplit != 0) { + for i = 0; i < len(s.R); i++ { + r = &s.R[i] + if r.Sym == nil { + continue + } + if (r.Type == obj.R_CALL || r.Type == obj.R_CALLARM) && r.Sym.Type == obj.STEXT { + if s.Cfunc == 0 && r.Sym.Cfunc == 2 && r.Sym.Nosplit == 0 { + fmt.Printf("Go %s calls C %s\n", s.Name, r.Sym.Name) + } else if s.Cfunc == 2 && s.Nosplit != 0 && r.Sym.Nosplit == 0 { + fmt.Printf("Go calls C %s calls %s\n", s.Name, r.Sym.Name) + } + } + } + } + } +} + +func Rnd(v int64, r int64) int64 { + if r <= 0 { + return v + } + v += r - 1 + c := v % r + if c < 0 { + c += r + } + v -= c + return v +} diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go new file mode 100644 index 0000000000..3098147819 --- /dev/null +++ b/src/cmd/link/internal/ld/link.go @@ -0,0 +1,226 @@ +// Derived from Inferno utils/6l/l.h and related files. +// http://code.google.com/p/inferno-os/source/browse/utils/6l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "cmd/internal/obj" + "debug/elf" + "encoding/binary" + "fmt" +) + +type LSym struct { + Name string + Extname string + Type int16 + Version int16 + Dupok uint8 + Cfunc uint8 + External uint8 + Nosplit uint8 + Reachable bool + Cgoexport uint8 + Special uint8 + Stkcheck uint8 + Hide uint8 + Leaf uint8 + Localentry uint8 + Onlist uint8 + // ElfType is set for symbols read from shared libraries by ldshlibsyms. It + // is not set for symbols defined by the packages being linked or by symbols + // read by ldelf (and so is left as elf.STT_NOTYPE). + ElfType elf.SymType + Dynid int32 + Plt int32 + Got int32 + Align int32 + Elfsym int32 + Args int32 + Locals int32 + Value int64 + Size int64 + Hash *LSym + Allsym *LSym + Next *LSym + Sub *LSym + Outer *LSym + Gotype *LSym + Reachparent *LSym + Queue *LSym + File string + Dynimplib string + Dynimpvers string + Sect interface{} + Autom *Auto + Pcln *Pcln + P []byte + R []Reloc + Local bool + 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 + Done uint8 + Type int32 + Variant int32 + Add int64 + Xadd int64 + Sym *LSym + Xsym *LSym +} + +type Auto struct { + Asym *LSym + Link *Auto + Aoffset int32 + Name int16 + Gotype *LSym +} + +type Shlib struct { + Path string + Hash []byte +} + +type Link struct { + Thechar int32 + Thestring string + Goarm int32 + Headtype int + Arch *LinkArch + Debugasm int32 + Debugvlog int32 + Bso *obj.Biobuf + Windows int32 + Goroot string + Hash map[symVer]*LSym + Allsym *LSym + Nsymbol int32 + Tlsg *LSym + Libdir []string + Library []*Library + Shlibs []Shlib + Tlsoffset int + Diag func(string, ...interface{}) + Cursym *LSym + Version int + Textp *LSym + Etextp *LSym + Nhistfile int32 + Filesyms *LSym +} + +type LinkArch struct { + ByteOrder binary.ByteOrder + Name string + Thechar int + Minlc int + Ptrsize int + Regsize int +} + +type Library struct { + Objref string + Srcref string + File string + Pkg string + Shlib string + hash []byte +} + +type Pcln struct { + Pcsp Pcdata + Pcfile Pcdata + Pcline Pcdata + Pcdata []Pcdata + Npcdata int + Funcdata []*LSym + Funcdataoff []int64 + Nfuncdata int + File []*LSym + Nfile int + Mfile int + Lastfile *LSym + Lastindex int +} + +type Pcdata struct { + P []byte +} + +type Pciter struct { + d Pcdata + p []byte + pc uint32 + nextpc uint32 + pcscale uint32 + value int32 + start int + done int +} + +// Reloc.variant +const ( + RV_NONE = iota + RV_POWER_LO + RV_POWER_HI + RV_POWER_HA + RV_POWER_DS + RV_CHECK_OVERFLOW = 1 << 8 + RV_TYPE_MASK = RV_CHECK_OVERFLOW - 1 +) + +const ( + LINKHASH = 100003 +) + +// Pcdata iterator. +// for(pciterinit(ctxt, &it, &pcd); !it.done; pciternext(&it)) { it.value holds in [it.pc, it.nextpc) } + +// Link holds the context for writing object code from a compiler +// to be linker input or for reading that input into the linker. + +// LinkArch is the definition of a single architecture. + +const ( + LinkAuto = 0 + iota + LinkInternal + LinkExternal +) diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go new file mode 100644 index 0000000000..0258aff104 --- /dev/null +++ b/src/cmd/link/internal/ld/macho.go @@ -0,0 +1,855 @@ +// Copyright 2009 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 ( + "cmd/internal/obj" + "sort" + "strings" +) + +type MachoHdr struct { + cpu uint32 + subcpu uint32 +} + +type MachoSect struct { + name string + segname string + addr uint64 + size uint64 + off uint32 + align uint32 + reloc uint32 + nreloc uint32 + flag uint32 + res1 uint32 + res2 uint32 +} + +type MachoSeg struct { + name string + vsize uint64 + vaddr uint64 + fileoffset uint64 + filesize uint64 + prot1 uint32 + prot2 uint32 + nsect uint32 + msect uint32 + sect []MachoSect + flag uint32 +} + +type MachoLoad struct { + type_ uint32 + data []uint32 +} + +/* + * Total amount of space to reserve at the start of the file + * for Header, PHeaders, and SHeaders. + * May waste some. + */ +const ( + INITIAL_MACHO_HEADR = 4 * 1024 +) + +const ( + MACHO_CPU_AMD64 = 1<<24 | 7 + MACHO_CPU_386 = 7 + MACHO_SUBCPU_X86 = 3 + MACHO_CPU_ARM = 12 + MACHO_SUBCPU_ARM = 0 + MACHO_SUBCPU_ARMV7 = 9 + MACHO_CPU_ARM64 = 1<<24 | 12 + MACHO_SUBCPU_ARM64_ALL = 0 + MACHO32SYMSIZE = 12 + MACHO64SYMSIZE = 16 + MACHO_X86_64_RELOC_UNSIGNED = 0 + MACHO_X86_64_RELOC_SIGNED = 1 + MACHO_X86_64_RELOC_BRANCH = 2 + MACHO_X86_64_RELOC_GOT_LOAD = 3 + MACHO_X86_64_RELOC_GOT = 4 + MACHO_X86_64_RELOC_SUBTRACTOR = 5 + MACHO_X86_64_RELOC_SIGNED_1 = 6 + MACHO_X86_64_RELOC_SIGNED_2 = 7 + MACHO_X86_64_RELOC_SIGNED_4 = 8 + MACHO_ARM_RELOC_VANILLA = 0 + MACHO_ARM_RELOC_BR24 = 5 + MACHO_ARM64_RELOC_UNSIGNED = 0 + MACHO_ARM64_RELOC_BRANCH26 = 2 + MACHO_ARM64_RELOC_PAGE21 = 3 + MACHO_ARM64_RELOC_PAGEOFF12 = 4 + MACHO_ARM64_RELOC_ADDEND = 10 + MACHO_GENERIC_RELOC_VANILLA = 0 + MACHO_FAKE_GOTPCREL = 100 +) + +// Copyright 2009 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. + +// Mach-O file writing +// http://developer.apple.com/mac/library/DOCUMENTATION/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html + +var macho64 bool + +var machohdr MachoHdr + +var load []MachoLoad + +var seg [16]MachoSeg + +var nseg int + +var ndebug int + +var nsect int + +const ( + SymKindLocal = 0 + iota + SymKindExtdef + SymKindUndef + NumSymKind +) + +var nkind [NumSymKind]int + +var sortsym []*LSym + +var nsortsym int + +// Amount of space left for adding load commands +// that refer to dynamic libraries. Because these have +// to go in the Mach-O header, we can't just pick a +// "big enough" header size. The initial header is +// one page, the non-dynamic library stuff takes +// up about 1300 bytes; we overestimate that as 2k. +var load_budget int = INITIAL_MACHO_HEADR - 2*1024 + +func Machoinit() { + switch Thearch.Thechar { + // 64-bit architectures + case '6', '7', '9': + macho64 = true + + // 32-bit architectures + default: + break + } +} + +func getMachoHdr() *MachoHdr { + return &machohdr +} + +func newMachoLoad(type_ uint32, ndata uint32) *MachoLoad { + if macho64 && (ndata&1 != 0) { + ndata++ + } + + load = append(load, MachoLoad{}) + l := &load[len(load)-1] + l.type_ = type_ + l.data = make([]uint32, ndata) + return l +} + +func newMachoSeg(name string, msect int) *MachoSeg { + if nseg >= len(seg) { + Exitf("too many segs") + } + + s := &seg[nseg] + nseg++ + s.name = name + s.msect = uint32(msect) + s.sect = make([]MachoSect, msect) + return s +} + +func newMachoSect(seg *MachoSeg, name string, segname string) *MachoSect { + if seg.nsect >= seg.msect { + Exitf("too many sects in segment %s", seg.name) + } + + s := &seg.sect[seg.nsect] + seg.nsect++ + s.name = name + s.segname = segname + nsect++ + return s +} + +// Generic linking code. + +var dylib []string + +var linkoff int64 + +func machowrite() int { + o1 := Cpos() + + loadsize := 4 * 4 * ndebug + for i := 0; i < len(load); i++ { + loadsize += 4 * (len(load[i].data) + 2) + } + if macho64 { + loadsize += 18 * 4 * nseg + loadsize += 20 * 4 * nsect + } else { + loadsize += 14 * 4 * nseg + loadsize += 17 * 4 * nsect + } + + if macho64 { + Thearch.Lput(0xfeedfacf) + } else { + Thearch.Lput(0xfeedface) + } + Thearch.Lput(machohdr.cpu) + Thearch.Lput(machohdr.subcpu) + if Linkmode == LinkExternal { + Thearch.Lput(1) /* file type - mach object */ + } else { + Thearch.Lput(2) /* file type - mach executable */ + } + Thearch.Lput(uint32(len(load)) + uint32(nseg) + uint32(ndebug)) + Thearch.Lput(uint32(loadsize)) + Thearch.Lput(1) /* flags - no undefines */ + if macho64 { + Thearch.Lput(0) /* reserved */ + } + + var j int + var s *MachoSeg + var t *MachoSect + for i := 0; i < nseg; i++ { + s = &seg[i] + if macho64 { + Thearch.Lput(25) /* segment 64 */ + Thearch.Lput(72 + 80*s.nsect) + strnput(s.name, 16) + Thearch.Vput(s.vaddr) + Thearch.Vput(s.vsize) + Thearch.Vput(s.fileoffset) + Thearch.Vput(s.filesize) + Thearch.Lput(s.prot1) + Thearch.Lput(s.prot2) + Thearch.Lput(s.nsect) + Thearch.Lput(s.flag) + } else { + Thearch.Lput(1) /* segment 32 */ + Thearch.Lput(56 + 68*s.nsect) + strnput(s.name, 16) + Thearch.Lput(uint32(s.vaddr)) + Thearch.Lput(uint32(s.vsize)) + Thearch.Lput(uint32(s.fileoffset)) + Thearch.Lput(uint32(s.filesize)) + Thearch.Lput(s.prot1) + Thearch.Lput(s.prot2) + Thearch.Lput(s.nsect) + Thearch.Lput(s.flag) + } + + for j = 0; uint32(j) < s.nsect; j++ { + t = &s.sect[j] + if macho64 { + strnput(t.name, 16) + strnput(t.segname, 16) + Thearch.Vput(t.addr) + Thearch.Vput(t.size) + Thearch.Lput(t.off) + Thearch.Lput(t.align) + Thearch.Lput(t.reloc) + Thearch.Lput(t.nreloc) + Thearch.Lput(t.flag) + Thearch.Lput(t.res1) /* reserved */ + Thearch.Lput(t.res2) /* reserved */ + Thearch.Lput(0) /* reserved */ + } else { + strnput(t.name, 16) + strnput(t.segname, 16) + Thearch.Lput(uint32(t.addr)) + Thearch.Lput(uint32(t.size)) + Thearch.Lput(t.off) + Thearch.Lput(t.align) + Thearch.Lput(t.reloc) + Thearch.Lput(t.nreloc) + Thearch.Lput(t.flag) + Thearch.Lput(t.res1) /* reserved */ + Thearch.Lput(t.res2) /* reserved */ + } + } + } + + var l *MachoLoad + for i := 0; i < len(load); i++ { + l = &load[i] + Thearch.Lput(l.type_) + Thearch.Lput(4 * (uint32(len(l.data)) + 2)) + for j = 0; j < len(l.data); j++ { + Thearch.Lput(l.data[j]) + } + } + + return int(Cpos() - o1) +} + +func domacho() { + if Debug['d'] != 0 { + return + } + + // empirically, string table must begin with " \x00". + s := Linklookup(Ctxt, ".machosymstr", 0) + + s.Type = obj.SMACHOSYMSTR + s.Reachable = true + Adduint8(Ctxt, s, ' ') + Adduint8(Ctxt, s, '\x00') + + s = Linklookup(Ctxt, ".machosymtab", 0) + s.Type = obj.SMACHOSYMTAB + s.Reachable = true + + if Linkmode != LinkExternal { + s := Linklookup(Ctxt, ".plt", 0) // will be __symbol_stub + s.Type = obj.SMACHOPLT + s.Reachable = true + + s = Linklookup(Ctxt, ".got", 0) // will be __nl_symbol_ptr + s.Type = obj.SMACHOGOT + s.Reachable = true + s.Align = 4 + + s = Linklookup(Ctxt, ".linkedit.plt", 0) // indirect table for .plt + s.Type = obj.SMACHOINDIRECTPLT + s.Reachable = true + + s = Linklookup(Ctxt, ".linkedit.got", 0) // indirect table for .got + s.Type = obj.SMACHOINDIRECTGOT + s.Reachable = true + } +} + +func Machoadddynlib(lib string) { + // Will need to store the library name rounded up + // and 24 bytes of header metadata. If not enough + // space, grab another page of initial space at the + // beginning of the output file. + load_budget -= (len(lib)+7)/8*8 + 24 + + if load_budget < 0 { + HEADR += 4096 + INITTEXT += 4096 + load_budget += 4096 + } + + dylib = append(dylib, lib) +} + +func machoshbits(mseg *MachoSeg, sect *Section, segname string) { + buf := "__" + strings.Replace(sect.Name[1:], ".", "_", -1) + + var msect *MachoSect + if Thearch.Thechar == '7' && sect.Rwx&1 == 0 { + // darwin/arm64 forbids absolute relocs in __TEXT, so if + // the section is not executable, put it in __DATA segment. + msect = newMachoSect(mseg, buf, "__DATA") + } else { + msect = newMachoSect(mseg, buf, segname) + } + + if sect.Rellen > 0 { + msect.reloc = uint32(sect.Reloff) + msect.nreloc = uint32(sect.Rellen / 8) + } + + for 1< sect.Seg.Vaddr+sect.Seg.Filelen-sect.Vaddr { + Diag("macho cannot represent section %s crossing data and bss", sect.Name) + } + msect.off = uint32(sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr) + } else { + // zero fill + msect.off = 0 + + msect.flag |= 1 + } + + if sect.Rwx&1 != 0 { + msect.flag |= 0x400 /* has instructions */ + } + + if sect.Name == ".plt" { + msect.name = "__symbol_stub1" + msect.flag = 0x80000408 /* only instructions, code, symbol stubs */ + msect.res1 = 0 //nkind[SymKindLocal]; + msect.res2 = 6 + } + + if sect.Name == ".got" { + msect.name = "__nl_symbol_ptr" + msect.flag = 6 /* section with nonlazy symbol pointers */ + msect.res1 = uint32(Linklookup(Ctxt, ".linkedit.plt", 0).Size / 4) /* offset into indirect symbol table */ + } + + if sect.Name == ".init_array" { + msect.name = "__mod_init_func" + msect.flag = 9 // S_MOD_INIT_FUNC_POINTERS + } +} + +func Asmbmacho() { + /* apple MACH */ + va := INITTEXT - int64(HEADR) + + mh := getMachoHdr() + switch Thearch.Thechar { + default: + Exitf("unknown macho architecture: %v", Thearch.Thechar) + + case '5': + mh.cpu = MACHO_CPU_ARM + mh.subcpu = MACHO_SUBCPU_ARMV7 + + case '6': + mh.cpu = MACHO_CPU_AMD64 + mh.subcpu = MACHO_SUBCPU_X86 + + case '7': + mh.cpu = MACHO_CPU_ARM64 + mh.subcpu = MACHO_SUBCPU_ARM64_ALL + + case '8': + mh.cpu = MACHO_CPU_386 + mh.subcpu = MACHO_SUBCPU_X86 + } + + var ms *MachoSeg + if Linkmode == LinkExternal { + /* segment for entire file */ + ms = newMachoSeg("", 40) + + ms.fileoffset = Segtext.Fileoff + ms.filesize = Segdwarf.Fileoff + Segdwarf.Filelen - Segtext.Fileoff + ms.vsize = ms.filesize + } + + /* segment for zero page */ + if Linkmode != LinkExternal { + ms = newMachoSeg("__PAGEZERO", 0) + ms.vsize = uint64(va) + } + + /* text */ + v := Rnd(int64(uint64(HEADR)+Segtext.Length), int64(INITRND)) + + if Linkmode != LinkExternal { + ms = newMachoSeg("__TEXT", 20) + ms.vaddr = uint64(va) + ms.vsize = uint64(v) + ms.fileoffset = 0 + ms.filesize = uint64(v) + ms.prot1 = 7 + ms.prot2 = 5 + } + + for sect := Segtext.Sect; sect != nil; sect = sect.Next { + machoshbits(ms, sect, "__TEXT") + } + + /* data */ + if Linkmode != LinkExternal { + w := int64(Segdata.Length) + ms = newMachoSeg("__DATA", 20) + ms.vaddr = uint64(va) + uint64(v) + ms.vsize = uint64(w) + ms.fileoffset = uint64(v) + ms.filesize = Segdata.Filelen + ms.prot1 = 3 + ms.prot2 = 3 + } + + for sect := Segdata.Sect; sect != nil; sect = sect.Next { + machoshbits(ms, sect, "__DATA") + } + + if Linkmode != LinkExternal { + switch Thearch.Thechar { + default: + Exitf("unknown macho architecture: %v", Thearch.Thechar) + + case '5': + ml := newMachoLoad(5, 17+2) /* unix thread */ + ml.data[0] = 1 /* thread type */ + ml.data[1] = 17 /* word count */ + ml.data[2+15] = uint32(Entryvalue()) /* start pc */ + + case '6': + ml := newMachoLoad(5, 42+2) /* unix thread */ + ml.data[0] = 4 /* thread type */ + ml.data[1] = 42 /* word count */ + ml.data[2+32] = uint32(Entryvalue()) /* start pc */ + ml.data[2+32+1] = uint32(Entryvalue() >> 32) + + case '7': + ml := newMachoLoad(5, 68+2) /* unix thread */ + ml.data[0] = 6 /* thread type */ + ml.data[1] = 68 /* word count */ + ml.data[2+64] = uint32(Entryvalue()) /* start pc */ + ml.data[2+64+1] = uint32(Entryvalue() >> 32) + + case '8': + ml := newMachoLoad(5, 16+2) /* unix thread */ + ml.data[0] = 1 /* thread type */ + ml.data[1] = 16 /* word count */ + ml.data[2+10] = uint32(Entryvalue()) /* start pc */ + } + } + + if Debug['d'] == 0 { + // must match domacholink below + s1 := Linklookup(Ctxt, ".machosymtab", 0) + + s2 := Linklookup(Ctxt, ".linkedit.plt", 0) + s3 := Linklookup(Ctxt, ".linkedit.got", 0) + s4 := Linklookup(Ctxt, ".machosymstr", 0) + + if Linkmode != LinkExternal { + ms := newMachoSeg("__LINKEDIT", 0) + ms.vaddr = uint64(va) + uint64(v) + uint64(Rnd(int64(Segdata.Length), int64(INITRND))) + ms.vsize = uint64(s1.Size) + uint64(s2.Size) + uint64(s3.Size) + uint64(s4.Size) + ms.fileoffset = uint64(linkoff) + ms.filesize = ms.vsize + ms.prot1 = 7 + ms.prot2 = 3 + } + + ml := newMachoLoad(2, 4) /* LC_SYMTAB */ + ml.data[0] = uint32(linkoff) /* symoff */ + ml.data[1] = uint32(nsortsym) /* nsyms */ + ml.data[2] = uint32(linkoff + s1.Size + s2.Size + s3.Size) /* stroff */ + ml.data[3] = uint32(s4.Size) /* strsize */ + + machodysymtab() + + if Linkmode != LinkExternal { + ml := newMachoLoad(14, 6) /* LC_LOAD_DYLINKER */ + ml.data[0] = 12 /* offset to string */ + stringtouint32(ml.data[1:], "/usr/lib/dyld") + + for i := 0; i < len(dylib); i++ { + ml = newMachoLoad(12, 4+(uint32(len(dylib[i]))+1+7)/8*2) /* LC_LOAD_DYLIB */ + ml.data[0] = 24 /* offset of string from beginning of load */ + ml.data[1] = 0 /* time stamp */ + ml.data[2] = 0 /* version */ + ml.data[3] = 0 /* compatibility version */ + stringtouint32(ml.data[4:], dylib[i]) + } + } + } + + // TODO: dwarf headers go in ms too + if Debug['s'] == 0 { + dwarfaddmachoheaders(ms) + } + + a := machowrite() + if int32(a) > HEADR { + Exitf("HEADR too small: %d > %d", a, HEADR) + } +} + +func symkind(s *LSym) int { + if s.Type == obj.SDYNIMPORT { + return SymKindUndef + } + if s.Cgoexport != 0 { + return SymKindExtdef + } + return SymKindLocal +} + +func addsym(s *LSym, name string, type_ int, addr int64, size int64, ver int, gotype *LSym) { + if s == nil { + return + } + + switch type_ { + default: + return + + case 'D', 'B', 'T': + break + } + + if sortsym != nil { + sortsym[nsortsym] = s + nkind[symkind(s)]++ + } + + nsortsym++ +} + +type machoscmp []*LSym + +func (x machoscmp) Len() int { + return len(x) +} + +func (x machoscmp) Swap(i, j int) { + x[i], x[j] = x[j], x[i] +} + +func (x machoscmp) Less(i, j int) bool { + s1 := x[i] + s2 := x[j] + + k1 := symkind(s1) + k2 := symkind(s2) + if k1 != k2 { + return k1-k2 < 0 + } + + return stringsCompare(s1.Extname, s2.Extname) < 0 +} + +func machogenasmsym(put func(*LSym, string, int, int64, int64, int, *LSym)) { + genasmsym(put) + for s := Ctxt.Allsym; s != nil; s = s.Allsym { + if s.Type == obj.SDYNIMPORT || s.Type == obj.SHOSTOBJ { + if s.Reachable { + put(s, "", 'D', 0, 0, 0, nil) + } + } + } +} + +func machosymorder() { + // On Mac OS X Mountain Lion, we must sort exported symbols + // So we sort them here and pre-allocate dynid for them + // See http://golang.org/issue/4029 + for i := 0; i < len(dynexp); i++ { + dynexp[i].Reachable = true + } + machogenasmsym(addsym) + sortsym = make([]*LSym, nsortsym) + nsortsym = 0 + machogenasmsym(addsym) + sort.Sort(machoscmp(sortsym[:nsortsym])) + for i := 0; i < nsortsym; i++ { + sortsym[i].Dynid = int32(i) + } +} + +func machosymtab() { + var s *LSym + var o *LSym + var p string + + symtab := Linklookup(Ctxt, ".machosymtab", 0) + symstr := Linklookup(Ctxt, ".machosymstr", 0) + + for i := 0; i < nsortsym; i++ { + s = sortsym[i] + Adduint32(Ctxt, symtab, uint32(symstr.Size)) + + // Only add _ to C symbols. Go symbols have dot in the name. + if !strings.Contains(s.Extname, ".") { + Adduint8(Ctxt, symstr, '_') + } + + // replace "·" as ".", because DTrace cannot handle it. + if !strings.Contains(s.Extname, "·") { + Addstring(symstr, s.Extname) + } else { + for p = s.Extname; p != ""; p = p[1:] { + if uint8(p[0]) == 0xc2 && uint8((p[1:])[0]) == 0xb7 { + Adduint8(Ctxt, symstr, '.') + p = p[1:] + } else { + Adduint8(Ctxt, symstr, uint8(p[0])) + } + } + + Adduint8(Ctxt, symstr, '\x00') + } + + if s.Type == obj.SDYNIMPORT || s.Type == obj.SHOSTOBJ { + Adduint8(Ctxt, symtab, 0x01) // type N_EXT, external symbol + Adduint8(Ctxt, symtab, 0) // no section + Adduint16(Ctxt, symtab, 0) // desc + adduintxx(Ctxt, symtab, 0, Thearch.Ptrsize) // no value + } else { + if s.Cgoexport != 0 { + Adduint8(Ctxt, symtab, 0x0f) + } else { + Adduint8(Ctxt, symtab, 0x0e) + } + o = s + for o.Outer != nil { + o = o.Outer + } + if o.Sect == nil { + Diag("missing section for %s", s.Name) + Adduint8(Ctxt, symtab, 0) + } else { + Adduint8(Ctxt, symtab, uint8((o.Sect.(*Section)).Extnum)) + } + Adduint16(Ctxt, symtab, 0) // desc + adduintxx(Ctxt, symtab, uint64(Symaddr(s)), Thearch.Ptrsize) + } + } +} + +func machodysymtab() { + ml := newMachoLoad(11, 18) /* LC_DYSYMTAB */ + + n := 0 + ml.data[0] = uint32(n) /* ilocalsym */ + ml.data[1] = uint32(nkind[SymKindLocal]) /* nlocalsym */ + n += nkind[SymKindLocal] + + ml.data[2] = uint32(n) /* iextdefsym */ + ml.data[3] = uint32(nkind[SymKindExtdef]) /* nextdefsym */ + n += nkind[SymKindExtdef] + + ml.data[4] = uint32(n) /* iundefsym */ + ml.data[5] = uint32(nkind[SymKindUndef]) /* nundefsym */ + + ml.data[6] = 0 /* tocoffset */ + ml.data[7] = 0 /* ntoc */ + ml.data[8] = 0 /* modtaboff */ + ml.data[9] = 0 /* nmodtab */ + ml.data[10] = 0 /* extrefsymoff */ + ml.data[11] = 0 /* nextrefsyms */ + + // must match domacholink below + s1 := Linklookup(Ctxt, ".machosymtab", 0) + + s2 := Linklookup(Ctxt, ".linkedit.plt", 0) + s3 := Linklookup(Ctxt, ".linkedit.got", 0) + ml.data[12] = uint32(linkoff + s1.Size) /* indirectsymoff */ + ml.data[13] = uint32((s2.Size + s3.Size) / 4) /* nindirectsyms */ + + ml.data[14] = 0 /* extreloff */ + ml.data[15] = 0 /* nextrel */ + ml.data[16] = 0 /* locreloff */ + ml.data[17] = 0 /* nlocrel */ +} + +func Domacholink() int64 { + machosymtab() + + // write data that will be linkedit section + s1 := Linklookup(Ctxt, ".machosymtab", 0) + + s2 := Linklookup(Ctxt, ".linkedit.plt", 0) + s3 := Linklookup(Ctxt, ".linkedit.got", 0) + s4 := Linklookup(Ctxt, ".machosymstr", 0) + + // Force the linkedit section to end on a 16-byte + // boundary. This allows pure (non-cgo) Go binaries + // to be code signed correctly. + // + // Apple's codesign_allocate (a helper utility for + // the codesign utility) can do this fine itself if + // it is run on a dynamic Mach-O binary. However, + // when it is run on a pure (non-cgo) Go binary, where + // the linkedit section is mostly empty, it fails to + // account for the extra padding that it itself adds + // when adding the LC_CODE_SIGNATURE load command + // (which must be aligned on a 16-byte boundary). + // + // By forcing the linkedit section to end on a 16-byte + // boundary, codesign_allocate will not need to apply + // any alignment padding itself, working around the + // issue. + for s4.Size%16 != 0 { + Adduint8(Ctxt, s4, 0) + } + + size := int(s1.Size + s2.Size + s3.Size + s4.Size) + + if size > 0 { + linkoff = Rnd(int64(uint64(HEADR)+Segtext.Length), int64(INITRND)) + Rnd(int64(Segdata.Filelen), int64(INITRND)) + Rnd(int64(Segdwarf.Filelen), int64(INITRND)) + Cseek(linkoff) + + Cwrite(s1.P[:s1.Size]) + Cwrite(s2.P[:s2.Size]) + Cwrite(s3.P[:s3.Size]) + Cwrite(s4.P[:s4.Size]) + } + + return Rnd(int64(size), int64(INITRND)) +} + +func machorelocsect(sect *Section, first *LSym) { + // If main section has no bits, nothing to relocate. + if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { + return + } + + sect.Reloff = uint64(Cpos()) + var sym *LSym + for sym = first; sym != nil; sym = sym.Next { + if !sym.Reachable { + continue + } + if uint64(sym.Value) >= sect.Vaddr { + break + } + } + + eaddr := int32(sect.Vaddr + sect.Length) + var r *Reloc + var ri int + for ; sym != nil; sym = sym.Next { + if !sym.Reachable { + continue + } + if sym.Value >= int64(eaddr) { + break + } + Ctxt.Cursym = sym + + for ri = 0; ri < len(sym.R); ri++ { + r = &sym.R[ri] + if r.Done != 0 { + continue + } + if Thearch.Machoreloc1(r, int64(uint64(sym.Value+int64(r.Off))-sect.Vaddr)) < 0 { + Diag("unsupported obj reloc %d/%d to %s", r.Type, r.Siz, r.Sym.Name) + } + } + } + + sect.Rellen = uint64(Cpos()) - sect.Reloff +} + +func Machoemitreloc() { + for Cpos()&7 != 0 { + Cput(0) + } + + machorelocsect(Segtext.Sect, Ctxt.Textp) + for sect := Segtext.Sect.Next; sect != nil; sect = sect.Next { + machorelocsect(sect, datap) + } + for sect := Segdata.Sect; sect != nil; sect = sect.Next { + machorelocsect(sect, datap) + } + dwarfemitreloc() +} diff --git a/src/cmd/link/internal/ld/macho_combine_dwarf.go b/src/cmd/link/internal/ld/macho_combine_dwarf.go new file mode 100644 index 0000000000..9134373a52 --- /dev/null +++ b/src/cmd/link/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) +} diff --git a/src/cmd/link/internal/ld/objfile.go b/src/cmd/link/internal/ld/objfile.go new file mode 100644 index 0000000000..613fcb2a40 --- /dev/null +++ b/src/cmd/link/internal/ld/objfile.go @@ -0,0 +1,354 @@ +// Copyright 2013 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" + "cmd/internal/obj" + "fmt" + "log" + "strconv" + "strings" +) + +const ( + startmagic = "\x00\x00go13ld" + endmagic = "\xff\xffgo13ld" +) + +func ldobjfile(ctxt *Link, f *obj.Biobuf, pkg string, length int64, pn string) { + start := obj.Boffset(f) + ctxt.Version++ + var buf [8]uint8 + obj.Bread(f, buf[:]) + if string(buf[:]) != startmagic { + log.Fatalf("%s: invalid file start %x %x %x %x %x %x %x %x", pn, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]) + } + c := obj.Bgetc(f) + if c != 1 { + log.Fatalf("%s: invalid file version number %d", pn, c) + } + + var lib string + for { + lib = rdstring(f) + if lib == "" { + break + } + addlib(ctxt, pkg, pn, lib) + } + + for { + c, err := f.Peek(1) + if err != nil { + log.Fatalf("%s: peeking: %v", pn, err) + } + if c[0] == 0xff { + break + } + readsym(ctxt, f, pkg, pn) + } + + buf = [8]uint8{} + obj.Bread(f, buf[:]) + if string(buf[:]) != endmagic { + log.Fatalf("%s: invalid file end", pn) + } + + if obj.Boffset(f) != start+length { + log.Fatalf("%s: unexpected end at %d, want %d", pn, int64(obj.Boffset(f)), int64(start+length)) + } +} + +var readsym_ndup int + +func readsym(ctxt *Link, f *obj.Biobuf, pkg string, pn string) { + if obj.Bgetc(f) != 0xfe { + log.Fatalf("readsym out of sync") + } + t := int(rdint(f)) + name := expandpkg(rdstring(f), pkg) + v := int(rdint(f)) + if v != 0 && v != 1 { + log.Fatalf("invalid symbol version %d", v) + } + flags := int(rdint(f)) + dupok := flags & 1 + local := false + if flags&2 != 0 { + local = true + } + size := int(rdint(f)) + typ := rdsym(ctxt, f, pkg) + data := rddata(f) + nreloc := int(rdint(f)) + + if v != 0 { + v = ctxt.Version + } + s := Linklookup(ctxt, name, v) + var dup *LSym + if s.Type != 0 && s.Type != obj.SXREF { + if (t == obj.SDATA || t == obj.SBSS || t == obj.SNOPTRBSS) && len(data) == 0 && nreloc == 0 { + if s.Size < int64(size) { + s.Size = int64(size) + } + if typ != nil && s.Gotype == nil { + s.Gotype = typ + } + return + } + + if (s.Type == obj.SDATA || s.Type == obj.SBSS || s.Type == obj.SNOPTRBSS) && len(s.P) == 0 && len(s.R) == 0 { + goto overwrite + } + if s.Type != obj.SBSS && s.Type != obj.SNOPTRBSS && dupok == 0 && s.Dupok == 0 { + log.Fatalf("duplicate symbol %s (types %d and %d) in %s and %s", s.Name, s.Type, t, s.File, pn) + } + if len(s.P) > 0 { + dup = s + s = linknewsym(ctxt, ".dup", readsym_ndup) + readsym_ndup++ // scratch + } + } + +overwrite: + s.File = pkg + s.Dupok = uint8(dupok) + if t == obj.SXREF { + log.Fatalf("bad sxref") + } + if t == 0 { + log.Fatalf("missing type for %s in %s", name, pn) + } + if t == obj.SBSS && (s.Type == obj.SRODATA || s.Type == obj.SNOPTRBSS) { + t = int(s.Type) + } + s.Type = int16(t) + if s.Size < int64(size) { + s.Size = int64(size) + } + s.Local = local + if typ != nil { // if bss sym defined multiple times, take type from any one def + s.Gotype = typ + } + if dup != nil && typ != nil { + dup.Gotype = typ + } + s.P = data + s.P = s.P[:len(data)] + if nreloc > 0 { + s.R = make([]Reloc, nreloc) + s.R = s.R[:nreloc] + var r *Reloc + for i := 0; i < nreloc; i++ { + r = &s.R[i] + r.Off = int32(rdint(f)) + r.Siz = uint8(rdint(f)) + r.Type = int32(rdint(f)) + r.Add = rdint(f) + rdint(f) // Xadd, ignored + r.Sym = rdsym(ctxt, f, pkg) + rdsym(ctxt, f, pkg) // Xsym, ignored + } + } + + if len(s.P) > 0 && dup != nil && len(dup.P) > 0 && strings.HasPrefix(s.Name, "gclocals·") { + // content-addressed garbage collection liveness bitmap symbol. + // double check for hash collisions. + if !bytes.Equal(s.P, dup.P) { + log.Fatalf("dupok hash collision for %s in %s and %s", s.Name, s.File, pn) + } + } + + if s.Type == obj.STEXT { + s.Args = int32(rdint(f)) + s.Locals = int32(rdint(f)) + s.Nosplit = uint8(rdint(f)) + v := int(rdint(f)) + s.Leaf = uint8(v & 1) + s.Cfunc = uint8(v & 2) + n := int(rdint(f)) + var a *Auto + for i := 0; i < n; i++ { + a = new(Auto) + a.Asym = rdsym(ctxt, f, pkg) + a.Aoffset = int32(rdint(f)) + a.Name = int16(rdint(f)) + a.Gotype = rdsym(ctxt, f, pkg) + a.Link = s.Autom + s.Autom = a + } + + s.Pcln = new(Pcln) + pc := s.Pcln + pc.Pcsp.P = rddata(f) + pc.Pcfile.P = rddata(f) + pc.Pcline.P = rddata(f) + n = int(rdint(f)) + pc.Pcdata = make([]Pcdata, n) + pc.Npcdata = n + for i := 0; i < n; i++ { + pc.Pcdata[i].P = rddata(f) + } + n = int(rdint(f)) + pc.Funcdata = make([]*LSym, n) + pc.Funcdataoff = make([]int64, n) + pc.Nfuncdata = n + for i := 0; i < n; i++ { + pc.Funcdata[i] = rdsym(ctxt, f, pkg) + } + for i := 0; i < n; i++ { + pc.Funcdataoff[i] = rdint(f) + } + n = int(rdint(f)) + pc.File = make([]*LSym, n) + pc.Nfile = n + for i := 0; i < n; i++ { + pc.File[i] = rdsym(ctxt, f, pkg) + } + + if dup == nil { + if s.Onlist != 0 { + log.Fatalf("symbol %s listed multiple times", s.Name) + } + s.Onlist = 1 + if ctxt.Etextp != nil { + ctxt.Etextp.Next = s + } else { + ctxt.Textp = s + } + ctxt.Etextp = s + } + } + + if ctxt.Debugasm != 0 { + fmt.Fprintf(ctxt.Bso, "%s ", s.Name) + if s.Version != 0 { + fmt.Fprintf(ctxt.Bso, "v=%d ", s.Version) + } + if s.Type != 0 { + fmt.Fprintf(ctxt.Bso, "t=%d ", s.Type) + } + if s.Dupok != 0 { + fmt.Fprintf(ctxt.Bso, "dupok ") + } + if s.Cfunc != 0 { + fmt.Fprintf(ctxt.Bso, "cfunc ") + } + if s.Nosplit != 0 { + fmt.Fprintf(ctxt.Bso, "nosplit ") + } + fmt.Fprintf(ctxt.Bso, "size=%d value=%d", int64(s.Size), int64(s.Value)) + if s.Type == obj.STEXT { + fmt.Fprintf(ctxt.Bso, " args=%#x locals=%#x", uint64(s.Args), uint64(s.Locals)) + } + fmt.Fprintf(ctxt.Bso, "\n") + var c int + var j int + for i := 0; i < len(s.P); { + fmt.Fprintf(ctxt.Bso, "\t%#04x", uint(i)) + for j = i; j < i+16 && j < len(s.P); j++ { + fmt.Fprintf(ctxt.Bso, " %02x", s.P[j]) + } + for ; j < i+16; j++ { + fmt.Fprintf(ctxt.Bso, " ") + } + fmt.Fprintf(ctxt.Bso, " ") + for j = i; j < i+16 && j < len(s.P); j++ { + c = int(s.P[j]) + if ' ' <= c && c <= 0x7e { + fmt.Fprintf(ctxt.Bso, "%c", c) + } else { + fmt.Fprintf(ctxt.Bso, ".") + } + } + + fmt.Fprintf(ctxt.Bso, "\n") + i += 16 + } + + var r *Reloc + for i := 0; i < len(s.R); i++ { + r = &s.R[i] + fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%d\n", int(r.Off), r.Siz, r.Type, r.Sym.Name, int64(r.Add)) + } + } +} + +func rdint(f *obj.Biobuf) int64 { + var c int + + uv := uint64(0) + for shift := 0; ; shift += 7 { + if shift >= 64 { + log.Fatalf("corrupt input") + } + c = obj.Bgetc(f) + uv |= uint64(c&0x7F) << uint(shift) + if c&0x80 == 0 { + break + } + } + + return int64(uv>>1) ^ (int64(uint64(uv)<<63) >> 63) +} + +func rdstring(f *obj.Biobuf) string { + n := rdint(f) + p := make([]byte, n) + obj.Bread(f, p) + return string(p) +} + +func rddata(f *obj.Biobuf) []byte { + n := rdint(f) + p := make([]byte, n) + obj.Bread(f, p) + return p +} + +var symbuf []byte + +func rdsym(ctxt *Link, f *obj.Biobuf, pkg string) *LSym { + n := int(rdint(f)) + if n == 0 { + rdint(f) + return nil + } + + if len(symbuf) < n { + symbuf = make([]byte, n) + } + obj.Bread(f, symbuf[:n]) + p := string(symbuf[:n]) + v := int(rdint(f)) + if v != 0 { + v = ctxt.Version + } + s := Linklookup(ctxt, expandpkg(p, pkg), v) + + if v == 0 && s.Name[0] == '$' && s.Type == 0 { + if strings.HasPrefix(s.Name, "$f32.") { + x, _ := strconv.ParseUint(s.Name[5:], 16, 32) + i32 := int32(x) + s.Type = obj.SRODATA + s.Local = true + Adduint32(ctxt, s, uint32(i32)) + s.Reachable = false + } else if strings.HasPrefix(s.Name, "$f64.") || strings.HasPrefix(s.Name, "$i64.") { + x, _ := strconv.ParseUint(s.Name[5:], 16, 64) + i64 := int64(x) + s.Type = obj.SRODATA + s.Local = true + Adduint64(ctxt, s, uint64(i64)) + s.Reachable = false + } + } + if v == 0 && strings.HasPrefix(s.Name, "runtime.gcbits.") { + s.Local = true + } + return s +} diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go new file mode 100644 index 0000000000..be2568d3b9 --- /dev/null +++ b/src/cmd/link/internal/ld/pcln.go @@ -0,0 +1,462 @@ +// Copyright 2013 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 ( + "cmd/internal/obj" + "fmt" + "log" +) + +// funcpctab writes to dst a pc-value table mapping the code in func to the values +// returned by valfunc parameterized by arg. The invocation of valfunc to update the +// current value is, for each p, +// +// val = valfunc(func, val, p, 0, arg); +// record val as value at p->pc; +// val = valfunc(func, val, p, 1, arg); +// +// where func is the function, val is the current value, p is the instruction being +// considered, and arg can be used to further parameterize valfunc. + +// pctofileline computes either the file number (arg == 0) +// or the line number (arg == 1) to use at p. +// Because p->lineno applies to p, phase == 0 (before p) +// takes care of the update. + +// pctospadj computes the sp adjustment in effect. +// It is oldval plus any adjustment made by p itself. +// The adjustment by p takes effect only after p, so we +// apply the change during phase == 1. + +// pctopcdata computes the pcdata value in effect at p. +// A PCDATA instruction sets the value in effect at future +// non-PCDATA instructions. +// Since PCDATA instructions have no width in the final code, +// it does not matter which phase we use for the update. + +// iteration over encoded pcdata tables. + +func getvarint(pp *[]byte) uint32 { + v := uint32(0) + p := *pp + for shift := 0; ; shift += 7 { + v |= uint32(p[0]&0x7F) << uint(shift) + tmp4 := p + p = p[1:] + if tmp4[0]&0x80 == 0 { + break + } + } + + *pp = p + return v +} + +func pciternext(it *Pciter) { + it.pc = it.nextpc + if it.done != 0 { + return + } + if -cap(it.p) >= -cap(it.d.P[len(it.d.P):]) { + it.done = 1 + return + } + + // value delta + v := getvarint(&it.p) + + if v == 0 && it.start == 0 { + it.done = 1 + return + } + + it.start = 0 + dv := int32(v>>1) ^ (int32(v<<31) >> 31) + it.value += dv + + // pc delta + v = getvarint(&it.p) + + it.nextpc = it.pc + v*it.pcscale +} + +func pciterinit(ctxt *Link, it *Pciter, d *Pcdata) { + it.d = *d + it.p = it.d.P + it.pc = 0 + it.nextpc = 0 + it.value = -1 + it.start = 1 + it.done = 0 + it.pcscale = uint32(ctxt.Arch.Minlc) + pciternext(it) +} + +// Copyright 2013 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. + +func addvarint(d *Pcdata, val uint32) { + n := int32(0) + for v := val; v >= 0x80; v >>= 7 { + n++ + } + n++ + + old := len(d.P) + for cap(d.P) < len(d.P)+int(n) { + d.P = append(d.P[:cap(d.P)], 0) + } + d.P = d.P[:old+int(n)] + + p := d.P[old:] + var v uint32 + for v = val; v >= 0x80; v >>= 7 { + p[0] = byte(v | 0x80) + p = p[1:] + } + p[0] = byte(v) +} + +func addpctab(ftab *LSym, off int32, d *Pcdata) int32 { + start := int32(len(ftab.P)) + Symgrow(Ctxt, ftab, int64(start)+int64(len(d.P))) + copy(ftab.P[start:], d.P) + + return int32(setuint32(Ctxt, ftab, int64(off), uint32(start))) +} + +func ftabaddstring(ftab *LSym, s string) int32 { + n := int32(len(s)) + 1 + start := int32(len(ftab.P)) + Symgrow(Ctxt, ftab, int64(start)+int64(n)+1) + copy(ftab.P[start:], s) + return start +} + +func renumberfiles(ctxt *Link, files []*LSym, d *Pcdata) { + var f *LSym + + // Give files numbers. + for i := 0; i < len(files); i++ { + f = files[i] + if f.Type != obj.SFILEPATH { + ctxt.Nhistfile++ + f.Value = int64(ctxt.Nhistfile) + f.Type = obj.SFILEPATH + f.Next = ctxt.Filesyms + ctxt.Filesyms = f + } + } + + newval := int32(-1) + var out Pcdata + + var dv int32 + var it Pciter + var oldval int32 + var v uint32 + var val int32 + for pciterinit(ctxt, &it, d); it.done == 0; pciternext(&it) { + // value delta + oldval = it.value + + if oldval == -1 { + val = -1 + } else { + if oldval < 0 || oldval >= int32(len(files)) { + log.Fatalf("bad pcdata %d", oldval) + } + val = int32(files[oldval].Value) + } + + dv = val - newval + newval = val + v = (uint32(dv) << 1) ^ uint32(int32(dv>>31)) + addvarint(&out, v) + + // pc delta + addvarint(&out, (it.nextpc-it.pc)/it.pcscale) + } + + // terminating value delta + addvarint(&out, 0) + + *d = out +} + +func container(s *LSym) int { + // We want to generate func table entries only for the "lowest level" symbols, + // not containers of subsymbols. + if s != nil && s.Sub != nil { + return 1 + } + return 0 +} + +// pclntab initializes the pclntab symbol with +// runtime function and file name information. + +var pclntab_zpcln Pcln + +// These variables are used to initialize runtime.firstmoduledata, see symtab.go:symtab. +var pclntabNfunc int32 +var pclntabFiletabOffset int32 +var pclntabPclntabOffset int32 +var pclntabFirstFunc *LSym +var pclntabLastFunc *LSym + +func pclntab() { + funcdata_bytes := int64(0) + ftab := Linklookup(Ctxt, "runtime.pclntab", 0) + ftab.Type = obj.SPCLNTAB + ftab.Reachable = true + + // See golang.org/s/go12symtab for the format. Briefly: + // 8-byte header + // nfunc [thearch.ptrsize bytes] + // function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes] + // end PC [thearch.ptrsize bytes] + // offset to file table [4 bytes] + nfunc := int32(0) + + for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { + if container(Ctxt.Cursym) == 0 { + nfunc++ + } + } + + pclntabNfunc = nfunc + Symgrow(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize)+4) + setuint32(Ctxt, ftab, 0, 0xfffffffb) + setuint8(Ctxt, ftab, 6, uint8(Thearch.Minlc)) + setuint8(Ctxt, ftab, 7, uint8(Thearch.Ptrsize)) + setuintxx(Ctxt, ftab, 8, uint64(nfunc), int64(Thearch.Ptrsize)) + pclntabPclntabOffset = int32(8 + Thearch.Ptrsize) + + nfunc = 0 + var last *LSym + var end int32 + var funcstart int32 + var i int32 + var it Pciter + var off int32 + var pcln *Pcln + for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { + last = Ctxt.Cursym + if container(Ctxt.Cursym) != 0 { + continue + } + pcln = Ctxt.Cursym.Pcln + if pcln == nil { + pcln = &pclntab_zpcln + } + + if pclntabFirstFunc == nil { + pclntabFirstFunc = Ctxt.Cursym + } + + funcstart = int32(len(ftab.P)) + funcstart += int32(-len(ftab.P)) & (int32(Thearch.Ptrsize) - 1) + + setaddr(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize), Ctxt.Cursym) + setuintxx(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize), uint64(funcstart), int64(Thearch.Ptrsize)) + + // fixed size of struct, checked below + off = funcstart + + end = funcstart + int32(Thearch.Ptrsize) + 3*4 + 5*4 + int32(pcln.Npcdata)*4 + int32(pcln.Nfuncdata)*int32(Thearch.Ptrsize) + if pcln.Nfuncdata > 0 && (end&int32(Thearch.Ptrsize-1) != 0) { + end += 4 + } + Symgrow(Ctxt, ftab, int64(end)) + + // entry uintptr + off = int32(setaddr(Ctxt, ftab, int64(off), Ctxt.Cursym)) + + // name int32 + off = int32(setuint32(Ctxt, ftab, int64(off), uint32(ftabaddstring(ftab, Ctxt.Cursym.Name)))) + + // args int32 + // TODO: Move into funcinfo. + off = int32(setuint32(Ctxt, ftab, int64(off), uint32(Ctxt.Cursym.Args))) + + // frame int32 + // This has been removed (it was never set quite correctly anyway). + // Nothing should use it. + // Leave an obviously incorrect value. + // TODO: Remove entirely. + off = int32(setuint32(Ctxt, ftab, int64(off), 0x1234567)) + + if pcln != &pclntab_zpcln { + renumberfiles(Ctxt, pcln.File, &pcln.Pcfile) + if false { + // Sanity check the new numbering + for pciterinit(Ctxt, &it, &pcln.Pcfile); it.done == 0; pciternext(&it) { + if it.value < 1 || it.value > Ctxt.Nhistfile { + Diag("bad file number in pcfile: %d not in range [1, %d]\n", it.value, Ctxt.Nhistfile) + errorexit() + } + } + } + } + + // pcdata + off = addpctab(ftab, off, &pcln.Pcsp) + + off = addpctab(ftab, off, &pcln.Pcfile) + off = addpctab(ftab, off, &pcln.Pcline) + off = int32(setuint32(Ctxt, ftab, int64(off), uint32(pcln.Npcdata))) + off = int32(setuint32(Ctxt, ftab, int64(off), uint32(pcln.Nfuncdata))) + for i = 0; i < int32(pcln.Npcdata); i++ { + off = addpctab(ftab, off, &pcln.Pcdata[i]) + } + + // funcdata, must be pointer-aligned and we're only int32-aligned. + // Missing funcdata will be 0 (nil pointer). + if pcln.Nfuncdata > 0 { + if off&int32(Thearch.Ptrsize-1) != 0 { + off += 4 + } + for i = 0; i < int32(pcln.Nfuncdata); i++ { + if pcln.Funcdata[i] == nil { + setuintxx(Ctxt, ftab, int64(off)+int64(Thearch.Ptrsize)*int64(i), uint64(pcln.Funcdataoff[i]), int64(Thearch.Ptrsize)) + } else { + // TODO: Dedup. + funcdata_bytes += pcln.Funcdata[i].Size + + setaddrplus(Ctxt, ftab, int64(off)+int64(Thearch.Ptrsize)*int64(i), pcln.Funcdata[i], pcln.Funcdataoff[i]) + } + } + + off += int32(pcln.Nfuncdata) * int32(Thearch.Ptrsize) + } + + if off != end { + Diag("bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, pcln.Npcdata, pcln.Nfuncdata, Thearch.Ptrsize) + errorexit() + } + + nfunc++ + } + + pclntabLastFunc = last + // Final entry of table is just end pc. + setaddrplus(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize), last, last.Size) + + // Start file table. + start := int32(len(ftab.P)) + + start += int32(-len(ftab.P)) & (int32(Thearch.Ptrsize) - 1) + pclntabFiletabOffset = start + setuint32(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize), uint32(start)) + + Symgrow(Ctxt, ftab, int64(start)+(int64(Ctxt.Nhistfile)+1)*4) + setuint32(Ctxt, ftab, int64(start), uint32(Ctxt.Nhistfile)) + for s := Ctxt.Filesyms; s != nil; s = s.Next { + setuint32(Ctxt, ftab, int64(start)+s.Value*4, uint32(ftabaddstring(ftab, s.Name))) + } + + ftab.Size = int64(len(ftab.P)) + + if Debug['v'] != 0 { + fmt.Fprintf(&Bso, "%5.2f pclntab=%d bytes, funcdata total %d bytes\n", obj.Cputime(), int64(ftab.Size), int64(funcdata_bytes)) + } +} + +const ( + BUCKETSIZE = 256 * MINFUNC + SUBBUCKETS = 16 + SUBBUCKETSIZE = BUCKETSIZE / SUBBUCKETS + NOIDX = 0x7fffffff +) + +// findfunctab generates a lookup table to quickly find the containing +// function for a pc. See src/runtime/symtab.go:findfunc for details. +func findfunctab() { + t := Linklookup(Ctxt, "runtime.findfunctab", 0) + t.Type = obj.SRODATA + t.Reachable = true + t.Local = true + + // find min and max address + min := Ctxt.Textp.Value + + max := int64(0) + for s := Ctxt.Textp; s != nil; s = s.Next { + max = s.Value + s.Size + } + + // for each subbucket, compute the minimum of all symbol indexes + // that map to that subbucket. + n := int32((max - min + SUBBUCKETSIZE - 1) / SUBBUCKETSIZE) + + indexes := make([]int32, n) + for i := int32(0); i < n; i++ { + indexes[i] = NOIDX + } + idx := int32(0) + var e *LSym + var i int32 + var p int64 + var q int64 + for s := Ctxt.Textp; s != nil; s = s.Next { + if container(s) != 0 { + continue + } + p = s.Value + e = s.Next + for container(e) != 0 { + e = e.Next + } + if e != nil { + q = e.Value + } else { + q = max + } + + //print("%d: [%lld %lld] %s\n", idx, p, q, s->name); + for ; p < q; p += SUBBUCKETSIZE { + i = int32((p - min) / SUBBUCKETSIZE) + if indexes[i] > idx { + indexes[i] = idx + } + } + + i = int32((q - 1 - min) / SUBBUCKETSIZE) + if indexes[i] > idx { + indexes[i] = idx + } + idx++ + } + + // allocate table + nbuckets := int32((max - min + BUCKETSIZE - 1) / BUCKETSIZE) + + Symgrow(Ctxt, t, 4*int64(nbuckets)+int64(n)) + + // fill in table + var base int32 + var j int32 + for i := int32(0); i < nbuckets; i++ { + base = indexes[i*SUBBUCKETS] + if base == NOIDX { + Diag("hole in findfunctab") + } + setuint32(Ctxt, t, int64(i)*(4+SUBBUCKETS), uint32(base)) + for j = 0; j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++ { + idx = indexes[i*SUBBUCKETS+j] + if idx == NOIDX { + Diag("hole in findfunctab") + } + if idx-base >= 256 { + Diag("too many functions in a findfunc bucket! %d/%d %d %d", i, nbuckets, j, idx-base) + } + + setuint8(Ctxt, t, int64(i)*(4+SUBBUCKETS)+4+int64(j), uint8(idx-base)) + } + } +} diff --git a/src/cmd/link/internal/ld/pe.go b/src/cmd/link/internal/ld/pe.go new file mode 100644 index 0000000000..cb82c053d8 --- /dev/null +++ b/src/cmd/link/internal/ld/pe.go @@ -0,0 +1,1253 @@ +// Copyright 2009 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 ( + "cmd/internal/obj" + "encoding/binary" + "fmt" + "sort" + "strconv" + "strings" +) + +type IMAGE_FILE_HEADER struct { + Machine uint16 + NumberOfSections uint16 + TimeDateStamp uint32 + PointerToSymbolTable uint32 + NumberOfSymbols uint32 + SizeOfOptionalHeader uint16 + Characteristics uint16 +} + +type IMAGE_DATA_DIRECTORY struct { + VirtualAddress uint32 + Size uint32 +} + +type IMAGE_OPTIONAL_HEADER struct { + Magic uint16 + MajorLinkerVersion uint8 + MinorLinkerVersion uint8 + SizeOfCode uint32 + SizeOfInitializedData uint32 + SizeOfUninitializedData uint32 + AddressOfEntryPoint uint32 + BaseOfCode uint32 + BaseOfData uint32 + ImageBase uint32 + SectionAlignment uint32 + FileAlignment uint32 + MajorOperatingSystemVersion uint16 + MinorOperatingSystemVersion uint16 + MajorImageVersion uint16 + MinorImageVersion uint16 + MajorSubsystemVersion uint16 + MinorSubsystemVersion uint16 + Win32VersionValue uint32 + SizeOfImage uint32 + SizeOfHeaders uint32 + CheckSum uint32 + Subsystem uint16 + DllCharacteristics uint16 + SizeOfStackReserve uint32 + SizeOfStackCommit uint32 + SizeOfHeapReserve uint32 + SizeOfHeapCommit uint32 + LoaderFlags uint32 + NumberOfRvaAndSizes uint32 + DataDirectory [16]IMAGE_DATA_DIRECTORY +} + +type IMAGE_SECTION_HEADER struct { + Name [8]uint8 + VirtualSize uint32 + VirtualAddress uint32 + SizeOfRawData uint32 + PointerToRawData uint32 + PointerToRelocations uint32 + PointerToLineNumbers uint32 + NumberOfRelocations uint16 + NumberOfLineNumbers uint16 + Characteristics uint32 +} + +type IMAGE_IMPORT_DESCRIPTOR struct { + OriginalFirstThunk uint32 + TimeDateStamp uint32 + ForwarderChain uint32 + Name uint32 + FirstThunk uint32 +} + +type IMAGE_EXPORT_DIRECTORY struct { + Characteristics uint32 + TimeDateStamp uint32 + MajorVersion uint16 + MinorVersion uint16 + Name uint32 + Base uint32 + NumberOfFunctions uint32 + NumberOfNames uint32 + AddressOfFunctions uint32 + AddressOfNames uint32 + AddressOfNameOrdinals uint32 +} + +const ( + PEBASE = 0x00400000 + + // SectionAlignment must be greater than or equal to FileAlignment. + // The default is the page size for the architecture. + PESECTALIGN = 0x1000 + + // FileAlignment should be a power of 2 between 512 and 64 K, inclusive. + // The default is 512. If the SectionAlignment is less than + // the architecture's page size, then FileAlignment must match SectionAlignment. + PEFILEALIGN = 2 << 8 +) + +const ( + IMAGE_FILE_MACHINE_I386 = 0x14c + IMAGE_FILE_MACHINE_AMD64 = 0x8664 + IMAGE_FILE_RELOCS_STRIPPED = 0x0001 + IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002 + IMAGE_FILE_LINE_NUMS_STRIPPED = 0x0004 + IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020 + IMAGE_FILE_32BIT_MACHINE = 0x0100 + IMAGE_FILE_DEBUG_STRIPPED = 0x0200 + IMAGE_SCN_CNT_CODE = 0x00000020 + IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040 + IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080 + IMAGE_SCN_MEM_EXECUTE = 0x20000000 + IMAGE_SCN_MEM_READ = 0x40000000 + IMAGE_SCN_MEM_WRITE = 0x80000000 + IMAGE_SCN_MEM_DISCARDABLE = 0x2000000 + IMAGE_SCN_LNK_NRELOC_OVFL = 0x1000000 + IMAGE_SCN_ALIGN_32BYTES = 0x600000 + IMAGE_DIRECTORY_ENTRY_EXPORT = 0 + IMAGE_DIRECTORY_ENTRY_IMPORT = 1 + IMAGE_DIRECTORY_ENTRY_RESOURCE = 2 + IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3 + IMAGE_DIRECTORY_ENTRY_SECURITY = 4 + IMAGE_DIRECTORY_ENTRY_BASERELOC = 5 + IMAGE_DIRECTORY_ENTRY_DEBUG = 6 + IMAGE_DIRECTORY_ENTRY_COPYRIGHT = 7 + IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7 + IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8 + IMAGE_DIRECTORY_ENTRY_TLS = 9 + IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10 + IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11 + IMAGE_DIRECTORY_ENTRY_IAT = 12 + IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13 + IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14 + IMAGE_SUBSYSTEM_WINDOWS_GUI = 2 + IMAGE_SUBSYSTEM_WINDOWS_CUI = 3 +) + +// X64 +type PE64_IMAGE_OPTIONAL_HEADER struct { + Magic uint16 + MajorLinkerVersion uint8 + MinorLinkerVersion uint8 + SizeOfCode uint32 + SizeOfInitializedData uint32 + SizeOfUninitializedData uint32 + AddressOfEntryPoint uint32 + BaseOfCode uint32 + ImageBase uint64 + SectionAlignment uint32 + FileAlignment uint32 + MajorOperatingSystemVersion uint16 + MinorOperatingSystemVersion uint16 + MajorImageVersion uint16 + MinorImageVersion uint16 + MajorSubsystemVersion uint16 + MinorSubsystemVersion uint16 + Win32VersionValue uint32 + SizeOfImage uint32 + SizeOfHeaders uint32 + CheckSum uint32 + Subsystem uint16 + DllCharacteristics uint16 + SizeOfStackReserve uint64 + SizeOfStackCommit uint64 + SizeOfHeapReserve uint64 + SizeOfHeapCommit uint64 + LoaderFlags uint32 + NumberOfRvaAndSizes uint32 + DataDirectory [16]IMAGE_DATA_DIRECTORY +} + +// Copyright 2009 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. + +// PE (Portable Executable) file writing +// http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx + +// DOS stub that prints out +// "This program cannot be run in DOS mode." +var dosstub = []uint8{ + 0x4d, + 0x5a, + 0x90, + 0x00, + 0x03, + 0x00, + 0x04, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0xff, + 0xff, + 0x00, + 0x00, + 0x8b, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x80, + 0x00, + 0x00, + 0x00, + 0x0e, + 0x1f, + 0xba, + 0x0e, + 0x00, + 0xb4, + 0x09, + 0xcd, + 0x21, + 0xb8, + 0x01, + 0x4c, + 0xcd, + 0x21, + 0x54, + 0x68, + 0x69, + 0x73, + 0x20, + 0x70, + 0x72, + 0x6f, + 0x67, + 0x72, + 0x61, + 0x6d, + 0x20, + 0x63, + 0x61, + 0x6e, + 0x6e, + 0x6f, + 0x74, + 0x20, + 0x62, + 0x65, + 0x20, + 0x72, + 0x75, + 0x6e, + 0x20, + 0x69, + 0x6e, + 0x20, + 0x44, + 0x4f, + 0x53, + 0x20, + 0x6d, + 0x6f, + 0x64, + 0x65, + 0x2e, + 0x0d, + 0x0d, + 0x0a, + 0x24, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +} + +var rsrcsym *LSym + +var strtbl []byte + +var PESECTHEADR int32 + +var PEFILEHEADR int32 + +var pe64 int + +var pensect int + +var nextsectoff int + +var nextfileoff int + +var textsect int + +var datasect int + +var bsssect int + +var fh IMAGE_FILE_HEADER + +var oh IMAGE_OPTIONAL_HEADER + +var oh64 PE64_IMAGE_OPTIONAL_HEADER + +var sh [16]IMAGE_SECTION_HEADER + +var dd []IMAGE_DATA_DIRECTORY + +type Imp struct { + s *LSym + off uint64 + next *Imp + argsize int +} + +type Dll struct { + name string + nameoff uint64 + thunkoff uint64 + ms *Imp + next *Dll +} + +var dr *Dll + +var dexport [1024]*LSym + +var nexport int + +type COFFSym struct { + sym *LSym + strtbloff int + sect int + value int64 + typ uint16 +} + +var coffsym []COFFSym + +var ncoffsym int + +func addpesection(name string, sectsize int, filesize int) *IMAGE_SECTION_HEADER { + if pensect == 16 { + Diag("too many sections") + errorexit() + } + + h := &sh[pensect] + pensect++ + copy(h.Name[:], name) + h.VirtualSize = uint32(sectsize) + h.VirtualAddress = uint32(nextsectoff) + nextsectoff = int(Rnd(int64(nextsectoff)+int64(sectsize), PESECTALIGN)) + h.PointerToRawData = uint32(nextfileoff) + if filesize > 0 { + h.SizeOfRawData = uint32(Rnd(int64(filesize), PEFILEALIGN)) + nextfileoff += int(h.SizeOfRawData) + } + + return h +} + +func chksectoff(h *IMAGE_SECTION_HEADER, off int64) { + if off != int64(h.PointerToRawData) { + Diag("%s.PointerToRawData = %#x, want %#x", cstring(h.Name[:]), uint64(int64(h.PointerToRawData)), uint64(off)) + errorexit() + } +} + +func chksectseg(h *IMAGE_SECTION_HEADER, s *Segment) { + if s.Vaddr-PEBASE != uint64(h.VirtualAddress) { + Diag("%s.VirtualAddress = %#x, want %#x", cstring(h.Name[:]), uint64(int64(h.VirtualAddress)), uint64(int64(s.Vaddr-PEBASE))) + errorexit() + } + + if s.Fileoff != uint64(h.PointerToRawData) { + Diag("%s.PointerToRawData = %#x, want %#x", cstring(h.Name[:]), uint64(int64(h.PointerToRawData)), uint64(int64(s.Fileoff))) + errorexit() + } +} + +func Peinit() { + var l int + + switch Thearch.Thechar { + // 64-bit architectures + case '6': + pe64 = 1 + + l = binary.Size(&oh64) + dd = oh64.DataDirectory[:] + + // 32-bit architectures + default: + l = binary.Size(&oh) + + dd = oh.DataDirectory[:] + } + + PEFILEHEADR = int32(Rnd(int64(len(dosstub)+binary.Size(&fh)+l+binary.Size(&sh)), PEFILEALIGN)) + PESECTHEADR = int32(Rnd(int64(PEFILEHEADR), PESECTALIGN)) + nextsectoff = int(PESECTHEADR) + nextfileoff = int(PEFILEHEADR) + + // some mingw libs depend on this symbol, for example, FindPESectionByName + xdefine("__image_base__", obj.SDATA, PEBASE) + + xdefine("_image_base__", obj.SDATA, PEBASE) +} + +func pewrite() { + Cseek(0) + if Linkmode != LinkExternal { + Cwrite(dosstub) + strnput("PE", 4) + } + + binary.Write(&coutbuf, binary.LittleEndian, &fh) + + if pe64 != 0 { + binary.Write(&coutbuf, binary.LittleEndian, &oh64) + } else { + binary.Write(&coutbuf, binary.LittleEndian, &oh) + } + binary.Write(&coutbuf, binary.LittleEndian, sh[:pensect]) +} + +func strput(s string) { + coutbuf.WriteString(s) + Cput(0) + // string must be padded to even size + if (len(s)+1)%2 != 0 { + Cput(0) + } +} + +func initdynimport() *Dll { + var d *Dll + + dr = nil + var m *Imp + for s := Ctxt.Allsym; s != nil; s = s.Allsym { + if !s.Reachable || s.Type != obj.SDYNIMPORT { + continue + } + for d = dr; d != nil; d = d.next { + if d.name == s.Dynimplib { + m = new(Imp) + break + } + } + + if d == nil { + d = new(Dll) + d.name = s.Dynimplib + d.next = dr + dr = d + m = new(Imp) + } + + // Because external link requires properly stdcall decorated name, + // all external symbols in runtime use %n to denote that the number + // of uinptrs this function consumes. Store the argsize and discard + // the %n suffix if any. + m.argsize = -1 + if i := strings.IndexByte(s.Extname, '%'); i >= 0 { + var err error + m.argsize, err = strconv.Atoi(s.Extname[i+1:]) + if err != nil { + Diag("failed to parse stdcall decoration: %v", err) + } + m.argsize *= Thearch.Ptrsize + s.Extname = s.Extname[:i] + } + + m.s = s + m.next = d.ms + d.ms = m + } + + if Linkmode == LinkExternal { + // Add real symbol name + for d := dr; d != nil; d = d.next { + for m = d.ms; m != nil; m = m.next { + m.s.Type = obj.SDATA + Symgrow(Ctxt, m.s, int64(Thearch.Ptrsize)) + dynName := m.s.Extname + // only windows/386 requires stdcall decoration + if Thearch.Thechar == '8' && m.argsize >= 0 { + dynName += fmt.Sprintf("@%d", m.argsize) + } + dynSym := Linklookup(Ctxt, dynName, 0) + dynSym.Reachable = true + dynSym.Type = obj.SHOSTOBJ + r := Addrel(m.s) + r.Sym = dynSym + r.Off = 0 + r.Siz = uint8(Thearch.Ptrsize) + r.Type = obj.R_ADDR + + // pre-allocate symtab entries for those symbols + dynSym.Dynid = int32(ncoffsym) + ncoffsym++ + } + } + } else { + dynamic := Linklookup(Ctxt, ".windynamic", 0) + dynamic.Reachable = true + dynamic.Type = obj.SWINDOWS + for d := dr; d != nil; d = d.next { + for m = d.ms; m != nil; m = m.next { + m.s.Type = obj.SWINDOWS | obj.SSUB + m.s.Sub = dynamic.Sub + dynamic.Sub = m.s + m.s.Value = dynamic.Size + dynamic.Size += int64(Thearch.Ptrsize) + } + + dynamic.Size += int64(Thearch.Ptrsize) + } + } + + return dr +} + +// peimporteddlls returns the gcc command line argument to link all imported +// DLLs. +func peimporteddlls() []string { + var dlls []string + + for d := dr; d != nil; d = d.next { + dlls = append(dlls, "-l"+strings.TrimSuffix(d.name, ".dll")) + } + + return dlls +} + +func addimports(datsect *IMAGE_SECTION_HEADER) { + startoff := Cpos() + dynamic := Linklookup(Ctxt, ".windynamic", 0) + + // skip import descriptor table (will write it later) + n := uint64(0) + + for d := dr; d != nil; d = d.next { + n++ + } + Cseek(startoff + int64(binary.Size(&IMAGE_IMPORT_DESCRIPTOR{}))*int64(n+1)) + + // write dll names + for d := dr; d != nil; d = d.next { + d.nameoff = uint64(Cpos()) - uint64(startoff) + strput(d.name) + } + + // write function names + var m *Imp + for d := dr; d != nil; d = d.next { + for m = d.ms; m != nil; m = m.next { + m.off = uint64(nextsectoff) + uint64(Cpos()) - uint64(startoff) + Wputl(0) // hint + strput(m.s.Extname) + } + } + + // write OriginalFirstThunks + oftbase := uint64(Cpos()) - uint64(startoff) + + n = uint64(Cpos()) + for d := dr; d != nil; d = d.next { + d.thunkoff = uint64(Cpos()) - n + for m = d.ms; m != nil; m = m.next { + if pe64 != 0 { + Vputl(m.off) + } else { + Lputl(uint32(m.off)) + } + } + + if pe64 != 0 { + Vputl(0) + } else { + Lputl(0) + } + } + + // add pe section and pad it at the end + n = uint64(Cpos()) - uint64(startoff) + + isect := addpesection(".idata", int(n), int(n)) + isect.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE + chksectoff(isect, startoff) + strnput("", int(uint64(isect.SizeOfRawData)-n)) + endoff := Cpos() + + // write FirstThunks (allocated in .data section) + ftbase := uint64(dynamic.Value) - uint64(datsect.VirtualAddress) - PEBASE + + Cseek(int64(uint64(datsect.PointerToRawData) + ftbase)) + for d := dr; d != nil; d = d.next { + for m = d.ms; m != nil; m = m.next { + if pe64 != 0 { + Vputl(m.off) + } else { + Lputl(uint32(m.off)) + } + } + + if pe64 != 0 { + Vputl(0) + } else { + Lputl(0) + } + } + + // finally write import descriptor table + Cseek(startoff) + + for d := dr; d != nil; d = d.next { + Lputl(uint32(uint64(isect.VirtualAddress) + oftbase + d.thunkoff)) + Lputl(0) + Lputl(0) + Lputl(uint32(uint64(isect.VirtualAddress) + d.nameoff)) + Lputl(uint32(uint64(datsect.VirtualAddress) + ftbase + d.thunkoff)) + } + + Lputl(0) //end + Lputl(0) + Lputl(0) + Lputl(0) + Lputl(0) + + // update data directory + dd[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = isect.VirtualAddress + + dd[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = isect.VirtualSize + dd[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = uint32(dynamic.Value - PEBASE) + dd[IMAGE_DIRECTORY_ENTRY_IAT].Size = uint32(dynamic.Size) + + Cseek(endoff) +} + +type pescmp []*LSym + +func (x pescmp) Len() int { + return len(x) +} + +func (x pescmp) Swap(i, j int) { + x[i], x[j] = x[j], x[i] +} + +func (x pescmp) Less(i, j int) bool { + s1 := x[i] + s2 := x[j] + return stringsCompare(s1.Extname, s2.Extname) < 0 +} + +func initdynexport() { + nexport = 0 + for s := Ctxt.Allsym; s != nil; s = s.Allsym { + if !s.Reachable || s.Cgoexport&CgoExportDynamic == 0 { + continue + } + if nexport+1 > len(dexport) { + Diag("pe dynexport table is full") + errorexit() + } + + dexport[nexport] = s + nexport++ + } + + sort.Sort(pescmp(dexport[:nexport])) +} + +func addexports() { + var e IMAGE_EXPORT_DIRECTORY + + size := binary.Size(&e) + 10*nexport + len(outfile) + 1 + for i := 0; i < nexport; i++ { + size += len(dexport[i].Extname) + 1 + } + + if nexport == 0 { + return + } + + sect := addpesection(".edata", size, size) + sect.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ + chksectoff(sect, Cpos()) + va := int(sect.VirtualAddress) + dd[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress = uint32(va) + dd[IMAGE_DIRECTORY_ENTRY_EXPORT].Size = sect.VirtualSize + + va_name := va + binary.Size(&e) + nexport*4 + va_addr := va + binary.Size(&e) + va_na := va + binary.Size(&e) + nexport*8 + + e.Characteristics = 0 + e.MajorVersion = 0 + e.MinorVersion = 0 + e.NumberOfFunctions = uint32(nexport) + e.NumberOfNames = uint32(nexport) + e.Name = uint32(va+binary.Size(&e)) + uint32(nexport)*10 // Program names. + e.Base = 1 + e.AddressOfFunctions = uint32(va_addr) + e.AddressOfNames = uint32(va_name) + e.AddressOfNameOrdinals = uint32(va_na) + + // put IMAGE_EXPORT_DIRECTORY + binary.Write(&coutbuf, binary.LittleEndian, &e) + + // put EXPORT Address Table + for i := 0; i < nexport; i++ { + Lputl(uint32(dexport[i].Value - PEBASE)) + } + + // put EXPORT Name Pointer Table + v := int(e.Name + uint32(len(outfile)) + 1) + + for i := 0; i < nexport; i++ { + Lputl(uint32(v)) + v += len(dexport[i].Extname) + 1 + } + + // put EXPORT Ordinal Table + for i := 0; i < nexport; i++ { + Wputl(uint16(i)) + } + + // put Names + strnput(outfile, len(outfile)+1) + + for i := 0; i < nexport; i++ { + strnput(dexport[i].Extname, len(dexport[i].Extname)+1) + } + strnput("", int(sect.SizeOfRawData-uint32(size))) +} + +// perelocsect relocates symbols from first in section sect, and returns +// the total number of relocations emitted. +func perelocsect(sect *Section, first *LSym) int { + // If main section has no bits, nothing to relocate. + if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { + return 0 + } + + relocs := 0 + + sect.Reloff = uint64(Cpos()) + var sym *LSym + for sym = first; sym != nil; sym = sym.Next { + if !sym.Reachable { + continue + } + if uint64(sym.Value) >= sect.Vaddr { + break + } + } + + eaddr := int32(sect.Vaddr + sect.Length) + var r *Reloc + var ri int + for ; sym != nil; sym = sym.Next { + if !sym.Reachable { + continue + } + if sym.Value >= int64(eaddr) { + break + } + Ctxt.Cursym = sym + + for ri = 0; ri < len(sym.R); ri++ { + r = &sym.R[ri] + if r.Done != 0 { + continue + } + if r.Xsym == nil { + Diag("missing xsym in relocation") + continue + } + + if r.Xsym.Dynid < 0 { + Diag("reloc %d to non-coff symbol %s (outer=%s) %d", r.Type, r.Sym.Name, r.Xsym.Name, r.Sym.Type) + } + if !Thearch.PEreloc1(r, int64(uint64(sym.Value+int64(r.Off))-PEBASE)) { + Diag("unsupported obj reloc %d/%d to %s", r.Type, r.Siz, r.Sym.Name) + } + + relocs++ + } + } + + sect.Rellen = uint64(Cpos()) - sect.Reloff + + return relocs +} + +// peemitreloc emits relocation entries for go.o in external linking. +func peemitreloc(text, data *IMAGE_SECTION_HEADER) { + for Cpos()&7 != 0 { + Cput(0) + } + + text.PointerToRelocations = uint32(Cpos()) + // first entry: extended relocs + Lputl(0) // placeholder for number of relocation + 1 + Lputl(0) + Wputl(0) + + n := perelocsect(Segtext.Sect, Ctxt.Textp) + 1 + for sect := Segtext.Sect.Next; sect != nil; sect = sect.Next { + n += perelocsect(sect, datap) + } + + cpos := Cpos() + Cseek(int64(text.PointerToRelocations)) + Lputl(uint32(n)) + Cseek(cpos) + if n > 0x10000 { + n = 0x10000 + text.Characteristics |= IMAGE_SCN_LNK_NRELOC_OVFL + } else { + text.PointerToRelocations += 10 // skip the extend reloc entry + } + text.NumberOfRelocations = uint16(n - 1) + + data.PointerToRelocations = uint32(cpos) + // first entry: extended relocs + Lputl(0) // placeholder for number of relocation + 1 + Lputl(0) + Wputl(0) + + n = 1 + for sect := Segdata.Sect; sect != nil; sect = sect.Next { + n += perelocsect(sect, datap) + } + + cpos = Cpos() + Cseek(int64(data.PointerToRelocations)) + Lputl(uint32(n)) + Cseek(cpos) + if n > 0x10000 { + n = 0x10000 + data.Characteristics |= IMAGE_SCN_LNK_NRELOC_OVFL + } else { + data.PointerToRelocations += 10 // skip the extend reloc entry + } + data.NumberOfRelocations = uint16(n - 1) +} + +func dope() { + /* relocation table */ + rel := Linklookup(Ctxt, ".rel", 0) + + rel.Reachable = true + rel.Type = obj.SELFROSECT + + initdynimport() + initdynexport() +} + +func strtbladd(name string) int { + off := len(strtbl) + 4 // offset includes 4-byte length at beginning of table + strtbl = append(strtbl, name...) + strtbl = append(strtbl, 0) + return off +} + +/* + * For more than 8 characters section names, name contains a slash (/) that is + * followed by an ASCII representation of a decimal number that is an offset into + * the string table. + * reference: pecoff_v8.docx Page 24. + * + */ +func newPEDWARFSection(name string, size int64) *IMAGE_SECTION_HEADER { + if size == 0 { + return nil + } + + off := strtbladd(name) + s := fmt.Sprintf("/%d", off) + h := addpesection(s, int(size), int(size)) + h.Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE + + return h +} + +func addpesym(s *LSym, name string, type_ int, addr int64, size int64, ver int, gotype *LSym) { + if s == nil { + return + } + + if s.Sect == nil && type_ != 'U' { + return + } + + switch type_ { + default: + return + + case 'D', 'B', 'T', 'U': + break + } + + if coffsym != nil { + // only windows/386 requires underscore prefix on external symbols + if Thearch.Thechar == '8' && Linkmode == LinkExternal && (s.Type == obj.SHOSTOBJ || s.Cgoexport != 0) && s.Name == s.Extname { + s.Name = "_" + s.Name + } + cs := &coffsym[ncoffsym] + cs.sym = s + if len(s.Name) > 8 { + cs.strtbloff = strtbladd(s.Name) + } + // Note: although address of runtime.edata (type SDATA) is at the start of .bss section + // it still belongs to the .data section, not the .bss section. + if uint64(s.Value) >= Segdata.Vaddr+Segdata.Filelen && s.Type != obj.SDATA && Linkmode == LinkExternal { + cs.value = int64(uint64(s.Value) - Segdata.Vaddr - Segdata.Filelen) + cs.sect = bsssect + } else if uint64(s.Value) >= Segdata.Vaddr { + cs.value = int64(uint64(s.Value) - Segdata.Vaddr) + cs.sect = datasect + } else if uint64(s.Value) >= Segtext.Vaddr { + cs.value = int64(uint64(s.Value) - Segtext.Vaddr) + cs.sect = textsect + } else if type_ == 'U' { + cs.value = 0 + cs.typ = IMAGE_SYM_DTYPE_FUNCTION + } else { + cs.value = 0 + cs.sect = 0 + Diag("addpesym %#x", addr) + } + } + + s.Dynid = int32(ncoffsym) + ncoffsym++ +} + +func addpesymtable() { + if Debug['s'] == 0 { + genasmsym(addpesym) + coffsym = make([]COFFSym, ncoffsym) + ncoffsym = 0 + if Linkmode == LinkExternal { + for d := dr; d != nil; d = d.next { + for m := d.ms; m != nil; m = m.next { + s := m.s.R[0].Xsym + addpesym(s, s.Name, 'U', 0, int64(Thearch.Ptrsize), 0, nil) + } + } + } + genasmsym(addpesym) + } + size := len(strtbl) + 4 + 18*ncoffsym + + var h *IMAGE_SECTION_HEADER + if Linkmode != LinkExternal { + // We do not really need .symtab for go.o, and if we have one, ld + // will also include it in the exe, and that will confuse windows. + h = addpesection(".symtab", size, size) + h.Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE + chksectoff(h, Cpos()) + } + fh.PointerToSymbolTable = uint32(Cpos()) + fh.NumberOfSymbols = uint32(ncoffsym) + + // put COFF symbol table + var s *COFFSym + for i := 0; i < ncoffsym; i++ { + s = &coffsym[i] + if s.strtbloff == 0 { + strnput(s.sym.Name, 8) + } else { + Lputl(0) + Lputl(uint32(s.strtbloff)) + } + + Lputl(uint32(s.value)) + Wputl(uint16(s.sect)) + if s.typ != 0 { + Wputl(s.typ) + } else if Linkmode == LinkExternal { + Wputl(0) + } else { + Wputl(0x0308) // "array of structs" + } + Cput(2) // storage class: external + Cput(0) // no aux entries + } + + // put COFF string table + Lputl(uint32(len(strtbl)) + 4) + + for i := 0; i < len(strtbl); i++ { + Cput(uint8(strtbl[i])) + } + if Linkmode != LinkExternal { + strnput("", int(h.SizeOfRawData-uint32(size))) + } +} + +func setpersrc(sym *LSym) { + if rsrcsym != nil { + Diag("too many .rsrc sections") + } + + rsrcsym = sym +} + +func addpersrc() { + if rsrcsym == nil { + return + } + + h := addpesection(".rsrc", int(rsrcsym.Size), int(rsrcsym.Size)) + h.Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_INITIALIZED_DATA + chksectoff(h, Cpos()) + + // relocation + var p []byte + var r *Reloc + var val uint32 + for ri := 0; ri < len(rsrcsym.R); ri++ { + r = &rsrcsym.R[ri] + p = rsrcsym.P[r.Off:] + val = uint32(int64(h.VirtualAddress) + r.Add) + + // 32-bit little-endian + p[0] = byte(val) + + p[1] = byte(val >> 8) + p[2] = byte(val >> 16) + p[3] = byte(val >> 24) + } + + Cwrite(rsrcsym.P) + strnput("", int(int64(h.SizeOfRawData)-rsrcsym.Size)) + + // update data directory + dd[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = h.VirtualAddress + + dd[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = h.VirtualSize +} + +func Asmbpe() { + switch Thearch.Thechar { + default: + Exitf("unknown PE architecture: %v", Thearch.Thechar) + case '6': + fh.Machine = IMAGE_FILE_MACHINE_AMD64 + case '8': + fh.Machine = IMAGE_FILE_MACHINE_I386 + } + + t := addpesection(".text", int(Segtext.Length), int(Segtext.Length)) + t.Characteristics = IMAGE_SCN_CNT_CODE | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ + chksectseg(t, &Segtext) + textsect = pensect + + var d *IMAGE_SECTION_HEADER + if Linkmode != LinkExternal { + d = addpesection(".data", int(Segdata.Length), int(Segdata.Filelen)) + d.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE + chksectseg(d, &Segdata) + datasect = pensect + } else { + d = addpesection(".data", int(Segdata.Filelen), int(Segdata.Filelen)) + d.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_ALIGN_32BYTES + chksectseg(d, &Segdata) + datasect = pensect + + b := addpesection(".bss", int(Segdata.Length-Segdata.Filelen), 0) + b.Characteristics = IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_ALIGN_32BYTES + b.PointerToRawData = 0 + bsssect = pensect + } + + if Debug['s'] == 0 { + dwarfaddpeheaders() + } + + Cseek(int64(nextfileoff)) + if Linkmode != LinkExternal { + addimports(d) + addexports() + } + addpesymtable() + addpersrc() + if Linkmode == LinkExternal { + peemitreloc(t, d) + } + + fh.NumberOfSections = uint16(pensect) + + // Being able to produce identical output for identical input is + // much more beneficial than having build timestamp in the header. + fh.TimeDateStamp = 0 + + if Linkmode == LinkExternal { + fh.Characteristics = IMAGE_FILE_LINE_NUMS_STRIPPED + } else { + fh.Characteristics = IMAGE_FILE_RELOCS_STRIPPED | IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DEBUG_STRIPPED + } + if pe64 != 0 { + fh.SizeOfOptionalHeader = uint16(binary.Size(&oh64)) + fh.Characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE + oh64.Magic = 0x20b // PE32+ + } else { + fh.SizeOfOptionalHeader = uint16(binary.Size(&oh)) + fh.Characteristics |= IMAGE_FILE_32BIT_MACHINE + oh.Magic = 0x10b // PE32 + oh.BaseOfData = d.VirtualAddress + } + + // Fill out both oh64 and oh. We only use one. Oh well. + oh64.MajorLinkerVersion = 3 + + oh.MajorLinkerVersion = 3 + oh64.MinorLinkerVersion = 0 + oh.MinorLinkerVersion = 0 + oh64.SizeOfCode = t.SizeOfRawData + oh.SizeOfCode = t.SizeOfRawData + oh64.SizeOfInitializedData = d.SizeOfRawData + oh.SizeOfInitializedData = d.SizeOfRawData + oh64.SizeOfUninitializedData = 0 + oh.SizeOfUninitializedData = 0 + if Linkmode != LinkExternal { + oh64.AddressOfEntryPoint = uint32(Entryvalue() - PEBASE) + oh.AddressOfEntryPoint = uint32(Entryvalue() - PEBASE) + } + oh64.BaseOfCode = t.VirtualAddress + oh.BaseOfCode = t.VirtualAddress + oh64.ImageBase = PEBASE + oh.ImageBase = PEBASE + oh64.SectionAlignment = PESECTALIGN + oh.SectionAlignment = PESECTALIGN + oh64.FileAlignment = PEFILEALIGN + oh.FileAlignment = PEFILEALIGN + oh64.MajorOperatingSystemVersion = 4 + oh.MajorOperatingSystemVersion = 4 + oh64.MinorOperatingSystemVersion = 0 + oh.MinorOperatingSystemVersion = 0 + oh64.MajorImageVersion = 1 + oh.MajorImageVersion = 1 + oh64.MinorImageVersion = 0 + oh.MinorImageVersion = 0 + oh64.MajorSubsystemVersion = 4 + oh.MajorSubsystemVersion = 4 + oh64.MinorSubsystemVersion = 0 + oh.MinorSubsystemVersion = 0 + oh64.SizeOfImage = uint32(nextsectoff) + oh.SizeOfImage = uint32(nextsectoff) + oh64.SizeOfHeaders = uint32(PEFILEHEADR) + oh.SizeOfHeaders = uint32(PEFILEHEADR) + if headstring == "windowsgui" { + oh64.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI + oh.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI + } else { + oh64.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI + oh.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI + } + + // Disable stack growth as we don't want Windows to + // fiddle with the thread stack limits, which we set + // ourselves to circumvent the stack checks in the + // Windows exception dispatcher. + // Commit size must be strictly less than reserve + // size otherwise reserve will be rounded up to a + // larger size, as verified with VMMap. + + // Go code would be OK with 64k stacks, but we need larger stacks for cgo. + // That default stack reserve size affects only the main thread, + // for other threads we specify stack size in runtime explicitly + // (runtime knows whether cgo is enabled or not). + // If you change stack reserve sizes here, + // change STACKSIZE in runtime/cgo/gcc_windows_{386,amd64}.c and correspondent + // CreateThread parameter in runtime.newosproc as well. + if !iscgo { + oh64.SizeOfStackReserve = 0x00020000 + oh.SizeOfStackReserve = 0x00020000 + oh64.SizeOfStackCommit = 0x00001000 + oh.SizeOfStackCommit = 0x00001000 + } else { + oh64.SizeOfStackReserve = 0x00200000 + oh.SizeOfStackReserve = 0x00100000 + + // account for 2 guard pages + oh64.SizeOfStackCommit = 0x00200000 - 0x2000 + + oh.SizeOfStackCommit = 0x00100000 - 0x2000 + } + + oh64.SizeOfHeapReserve = 0x00100000 + oh.SizeOfHeapReserve = 0x00100000 + oh64.SizeOfHeapCommit = 0x00001000 + oh.SizeOfHeapCommit = 0x00001000 + oh64.NumberOfRvaAndSizes = 16 + oh.NumberOfRvaAndSizes = 16 + + pewrite() +} diff --git a/src/cmd/link/internal/ld/pobj.go b/src/cmd/link/internal/ld/pobj.go new file mode 100644 index 0000000000..8568744c3d --- /dev/null +++ b/src/cmd/link/internal/ld/pobj.go @@ -0,0 +1,253 @@ +// Inferno utils/6l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "cmd/internal/obj" + "flag" + "fmt" + "os" + "strings" +) + +var pkglistfornote []byte + +// Reading object files. + +func Ldmain() { + Ctxt = linknew(Thelinkarch) + Ctxt.Thechar = int32(Thearch.Thechar) + Ctxt.Thestring = Thestring + Ctxt.Diag = Diag + Ctxt.Bso = &Bso + + Bso = *obj.Binitw(os.Stdout) + Debug = [128]int{} + nerrors = 0 + outfile = "" + HEADTYPE = -1 + INITTEXT = -1 + INITDAT = -1 + INITRND = -1 + INITENTRY = "" + Linkmode = LinkAuto + + // For testing behavior of go command when tools crash. + // Undocumented, not in standard flag parser to avoid + // exposing in usage message. + for _, arg := range os.Args { + if arg == "-crash_for_testing" { + *(*int)(nil) = 0 + } + } + + if Thearch.Thechar == '5' && Ctxt.Goarm == 5 { + Debug['F'] = 1 + } + + obj.Flagcount("1", "use alternate profiling code", &Debug['1']) + if Thearch.Thechar == '6' { + obj.Flagcount("8", "assume 64-bit addresses", &Debug['8']) + } + obj.Flagfn1("B", "add an ELF NT_GNU_BUILD_ID `note` when using ELF", addbuildinfo) + obj.Flagcount("C", "check Go calls to C code", &Debug['C']) + obj.Flagint64("D", "set data segment `address`", &INITDAT) + obj.Flagstr("E", "set `entry` symbol name", &INITENTRY) + if Thearch.Thechar == '5' { + obj.Flagcount("G", "debug pseudo-ops", &Debug['G']) + } + obj.Flagfn1("I", "use `linker` as ELF dynamic linker", setinterp) + obj.Flagfn1("L", "add specified `directory` to library path", Lflag) + obj.Flagfn1("H", "set header `type`", setheadtype) + obj.Flagcount("K", "add stack underflow checks", &Debug['K']) + if Thearch.Thechar == '5' { + obj.Flagcount("M", "disable software div/mod", &Debug['M']) + } + obj.Flagcount("O", "print pc-line tables", &Debug['O']) + obj.Flagcount("Q", "debug byte-register code gen", &Debug['Q']) + if Thearch.Thechar == '5' { + obj.Flagcount("P", "debug code generation", &Debug['P']) + } + obj.Flagint32("R", "set address rounding `quantum`", &INITRND) + obj.Flagcount("nil", "check type signatures", &Debug['S']) + obj.Flagint64("T", "set text segment `address`", &INITTEXT) + obj.Flagfn0("V", "print version and exit", doversion) + obj.Flagcount("W", "disassemble input", &Debug['W']) + obj.Flagfn1("X", "set the value of a string variable; the next two arguments are its name and value", addstrdata1) + obj.Flagcount("Z", "clear stack frame on entry", &Debug['Z']) + obj.Flagcount("a", "disassemble output", &Debug['a']) + flag.Var(&Buildmode, "buildmode", "set build `mode`") + obj.Flagcount("c", "dump call graph", &Debug['c']) + obj.Flagcount("d", "disable dynamic executable", &Debug['d']) + obj.Flagstr("extld", "use `linker` when linking in external mode", &extld) + obj.Flagstr("extldflags", "pass `flags` to external linker", &extldflags) + obj.Flagcount("f", "ignore version mismatch", &Debug['f']) + obj.Flagcount("g", "disable go package data checks", &Debug['g']) + obj.Flagstr("installsuffix", "set package directory `suffix`", &flag_installsuffix) + obj.Flagstr("k", "set field tracking `symbol`", &tracksym) + obj.Flagfn1("linkmode", "set link `mode` (internal, external, auto)", setlinkmode) + flag.BoolVar(&Linkshared, "linkshared", false, "link against installed Go shared libraries") + obj.Flagcount("n", "dump symbol table", &Debug['n']) + obj.Flagstr("o", "write output to `file`", &outfile) + flag.Var(&rpath, "r", "set the ELF dynamic linker search `path` to dir1:dir2:...") + obj.Flagcount("race", "enable race detector", &flag_race) + obj.Flagcount("s", "disable symbol table", &Debug['s']) + var flagShared int + if Thearch.Thechar == '5' || Thearch.Thechar == '6' { + obj.Flagcount("shared", "generate shared object (implies -linkmode external)", &flagShared) + } + obj.Flagstr("tmpdir", "use `directory` for temporary files", &tmpdir) + obj.Flagcount("u", "reject unsafe packages", &Debug['u']) + obj.Flagcount("v", "print link trace", &Debug['v']) + obj.Flagcount("w", "disable DWARF generation", &Debug['w']) + + // Clumsy hack to preserve old behavior of -X taking two arguments. + for i := 0; i < len(os.Args); i++ { + arg := os.Args[i] + if (arg == "--X" || arg == "-X") && i+2 < len(os.Args) { + os.Args[i+2] = "-X=VALUE:" + os.Args[i+2] + i += 2 + } else if (strings.HasPrefix(arg, "--X=") || strings.HasPrefix(arg, "-X=")) && i+1 < len(os.Args) { + os.Args[i+1] = "-X=VALUE:" + os.Args[i+1] + i++ + } + } + obj.Flagstr("cpuprofile", "write cpu profile to `file`", &cpuprofile) + obj.Flagstr("memprofile", "write memory profile to `file`", &memprofile) + obj.Flagint64("memprofilerate", "set runtime.MemProfileRate to `rate`", &memprofilerate) + obj.Flagparse(usage) + startProfile() + Ctxt.Bso = &Bso + Ctxt.Debugvlog = int32(Debug['v']) + if flagShared != 0 { + if Buildmode == BuildmodeExe { + Buildmode = BuildmodeCShared + } else if Buildmode != BuildmodeCShared { + Exitf("-shared and -buildmode=%s are incompatible", Buildmode.String()) + } + } + + if Buildmode != BuildmodeShared && flag.NArg() != 1 { + usage() + } + + if outfile == "" { + if HEADTYPE == obj.Hwindows { + outfile = fmt.Sprintf("%c.out.exe", Thearch.Thechar) + } else { + outfile = fmt.Sprintf("%c.out", Thearch.Thechar) + } + } + + libinit() // creates outfile + + if HEADTYPE == -1 { + HEADTYPE = int32(headtype(goos)) + } + Ctxt.Headtype = int(HEADTYPE) + if headstring == "" { + headstring = Headstr(int(HEADTYPE)) + } + + Thearch.Archinit() + + if Linkshared && !Iself { + Exitf("-linkshared can only be used on elf systems") + } + + if Debug['v'] != 0 { + fmt.Fprintf(&Bso, "HEADER = -H%d -T0x%x -D0x%x -R0x%x\n", HEADTYPE, uint64(INITTEXT), uint64(INITDAT), uint32(INITRND)) + } + Bso.Flush() + + if Buildmode == BuildmodeShared { + for i := 0; i < flag.NArg(); i++ { + arg := flag.Arg(i) + parts := strings.SplitN(arg, "=", 2) + var pkgpath, file string + if len(parts) == 1 { + pkgpath, file = "main", arg + } else { + pkgpath, file = parts[0], parts[1] + } + pkglistfornote = append(pkglistfornote, pkgpath...) + pkglistfornote = append(pkglistfornote, '\n') + addlibpath(Ctxt, "command line", "command line", file, pkgpath, "") + } + } else { + addlibpath(Ctxt, "command line", "command line", flag.Arg(0), "main", "") + } + loadlib() + + if Thearch.Thechar == '5' { + // mark some functions that are only referenced after linker code editing + if Debug['F'] != 0 { + mark(Linkrlookup(Ctxt, "_sfloat", 0)) + } + mark(Linklookup(Ctxt, "runtime.read_tls_fallback", 0)) + } + + checkgo() + deadcode() + callgraph() + + doelf() + if HEADTYPE == obj.Hdarwin { + domacho() + } + dostkcheck() + if HEADTYPE == obj.Hwindows { + dope() + } + addexport() + Thearch.Gentext() // trampolines, call stubs, etc. + textaddress() + pclntab() + findfunctab() + symtab() + dodata() + address() + doweak() + reloc() + Thearch.Asmb() + undef() + hostlink() + archive() + if Debug['v'] != 0 { + fmt.Fprintf(&Bso, "%5.2f cpu time\n", obj.Cputime()) + fmt.Fprintf(&Bso, "%d symbols\n", Ctxt.Nsymbol) + fmt.Fprintf(&Bso, "%d liveness data\n", liveness) + } + + Bso.Flush() + + errorexit() +} diff --git a/src/cmd/link/internal/ld/sym.go b/src/cmd/link/internal/ld/sym.go new file mode 100644 index 0000000000..652109d3e3 --- /dev/null +++ b/src/cmd/link/internal/ld/sym.go @@ -0,0 +1,227 @@ +// Derived from Inferno utils/6l/obj.c and utils/6l/span.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/span.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "cmd/internal/obj" + "log" + "os" + "path/filepath" + "strconv" +) + +func yy_isalpha(c int) bool { + return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' +} + +var headers = []struct { + name string + val int +}{ + {"darwin", obj.Hdarwin}, + {"dragonfly", obj.Hdragonfly}, + {"elf", obj.Helf}, + {"freebsd", obj.Hfreebsd}, + {"linux", obj.Hlinux}, + {"android", obj.Hlinux}, // must be after "linux" entry or else headstr(Hlinux) == "android" + {"nacl", obj.Hnacl}, + {"netbsd", obj.Hnetbsd}, + {"openbsd", obj.Hopenbsd}, + {"plan9", obj.Hplan9}, + {"solaris", obj.Hsolaris}, + {"windows", obj.Hwindows}, + {"windowsgui", obj.Hwindows}, +} + +func linknew(arch *LinkArch) *Link { + ctxt := new(Link) + ctxt.Hash = make(map[symVer]*LSym) + ctxt.Arch = arch + ctxt.Version = obj.HistVersion + ctxt.Goroot = obj.Getgoroot() + + p := obj.Getgoarch() + if p != arch.Name { + log.Fatalf("invalid goarch %s (want %s)", p, arch.Name) + } + + var buf string + buf, _ = os.Getwd() + if buf == "" { + buf = "/???" + } + buf = filepath.ToSlash(buf) + + ctxt.Headtype = headtype(obj.Getgoos()) + if ctxt.Headtype < 0 { + log.Fatalf("unknown goos %s", obj.Getgoos()) + } + + // Record thread-local storage offset. + // TODO(rsc): Move tlsoffset back into the linker. + switch ctxt.Headtype { + default: + log.Fatalf("unknown thread-local storage offset for %s", Headstr(ctxt.Headtype)) + + case obj.Hplan9, obj.Hwindows: + break + + /* + * ELF uses TLS offset negative from FS. + * Translate 0(FS) and 8(FS) into -16(FS) and -8(FS). + * Known to low-level assembly in package runtime and runtime/cgo. + */ + case obj.Hlinux, + obj.Hfreebsd, + obj.Hnetbsd, + obj.Hopenbsd, + obj.Hdragonfly, + obj.Hsolaris: + ctxt.Tlsoffset = -1 * ctxt.Arch.Ptrsize + + case obj.Hnacl: + switch ctxt.Arch.Thechar { + default: + log.Fatalf("unknown thread-local storage offset for nacl/%s", ctxt.Arch.Name) + + case '5': + ctxt.Tlsoffset = 0 + + case '6': + ctxt.Tlsoffset = 0 + + case '8': + ctxt.Tlsoffset = -8 + } + + /* + * OS X system constants - offset from 0(GS) to our TLS. + * Explained in ../../runtime/cgo/gcc_darwin_*.c. + */ + case obj.Hdarwin: + switch ctxt.Arch.Thechar { + default: + log.Fatalf("unknown thread-local storage offset for darwin/%s", ctxt.Arch.Name) + + case '5': + ctxt.Tlsoffset = 0 // dummy value, not needed + + case '6': + ctxt.Tlsoffset = 0x8a0 + + case '7': + ctxt.Tlsoffset = 0 // dummy value, not needed + + case '8': + ctxt.Tlsoffset = 0x468 + } + } + + // On arm, record goarm. + if ctxt.Arch.Thechar == '5' { + p := obj.Getgoarm() + if p != "" { + ctxt.Goarm = int32(obj.Atoi(p)) + } else { + ctxt.Goarm = 6 + } + } + + return ctxt +} + +func linknewsym(ctxt *Link, symb string, v int) *LSym { + s := new(LSym) + *s = LSym{} + + s.Dynid = -1 + s.Plt = -1 + s.Got = -1 + s.Name = symb + s.Type = 0 + s.Version = int16(v) + s.Value = 0 + s.Size = 0 + ctxt.Nsymbol++ + + s.Allsym = ctxt.Allsym + ctxt.Allsym = s + + return s +} + +type symVer struct { + sym string + ver int +} + +func _lookup(ctxt *Link, symb string, v int, creat int) *LSym { + s := ctxt.Hash[symVer{symb, v}] + if s != nil { + return s + } + if creat == 0 { + return nil + } + + s = linknewsym(ctxt, symb, v) + s.Extname = s.Name + ctxt.Hash[symVer{symb, v}] = s + return s +} + +func Linklookup(ctxt *Link, name string, v int) *LSym { + return _lookup(ctxt, name, v, 1) +} + +// read-only lookup +func Linkrlookup(ctxt *Link, name string, v int) *LSym { + return _lookup(ctxt, name, v, 0) +} + +func Headstr(v int) string { + for i := 0; i < len(headers); i++ { + if v == headers[i].val { + return headers[i].name + } + } + return strconv.Itoa(v) +} + +func headtype(name string) int { + for i := 0; i < len(headers); i++ { + if name == headers[i].name { + return headers[i].val + } + } + return -1 +} diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go new file mode 100644 index 0000000000..ca66541935 --- /dev/null +++ b/src/cmd/link/internal/ld/symtab.go @@ -0,0 +1,534 @@ +// Inferno utils/6l/span.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/span.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "cmd/internal/obj" + "crypto/sha1" + "fmt" + "path/filepath" + "sort" + "strings" +) + +// Symbol table. + +func putelfstr(s string) int { + if len(Elfstrdat) == 0 && s != "" { + // first entry must be empty string + putelfstr("") + } + + // When dynamically linking, we create LSym's by reading the names from + // the symbol tables of the shared libraries and so the names need to + // match exactly. Tools like DTrace will have to wait for now. + if !DynlinkingGo() { + // Rewrite · to . for ASCII-only tools like DTrace (sigh) + s = strings.Replace(s, "·", ".", -1) + } + + n := len(s) + 1 + for len(Elfstrdat)+n > cap(Elfstrdat) { + Elfstrdat = append(Elfstrdat[:cap(Elfstrdat)], 0)[:len(Elfstrdat)] + } + + off := len(Elfstrdat) + Elfstrdat = Elfstrdat[:off+n] + copy(Elfstrdat[off:], s) + + return off +} + +func putelfsyment(off int, addr int64, size int64, info int, shndx int, other int) { + switch Thearch.Thechar { + case '6', '7', '9': + Thearch.Lput(uint32(off)) + Cput(uint8(info)) + Cput(uint8(other)) + Thearch.Wput(uint16(shndx)) + Thearch.Vput(uint64(addr)) + Thearch.Vput(uint64(size)) + Symsize += ELF64SYMSIZE + + default: + Thearch.Lput(uint32(off)) + Thearch.Lput(uint32(addr)) + Thearch.Lput(uint32(size)) + Cput(uint8(info)) + Cput(uint8(other)) + Thearch.Wput(uint16(shndx)) + Symsize += ELF32SYMSIZE + } +} + +var numelfsym int = 1 // 0 is reserved + +var elfbind int + +func putelfsym(x *LSym, s string, t int, addr int64, size int64, ver int, go_ *LSym) { + var type_ int + + switch t { + default: + return + + case 'T': + type_ = STT_FUNC + + case 'D': + type_ = STT_OBJECT + + case 'B': + type_ = STT_OBJECT + + case 'U': + // ElfType is only set for symbols read from Go shared libraries, but + // for other symbols it is left as STT_NOTYPE which is fine. + type_ = int(x.ElfType) + + case 't': + type_ = STT_TLS + } + + xo := x + for xo.Outer != nil { + xo = xo.Outer + } + + var elfshnum int + if xo.Type == obj.SDYNIMPORT || xo.Type == obj.SHOSTOBJ { + elfshnum = SHN_UNDEF + } else { + if xo.Sect == nil { + Ctxt.Cursym = x + Diag("missing section in putelfsym") + return + } + if xo.Sect.(*Section).Elfsect == nil { + Ctxt.Cursym = x + Diag("missing ELF section in putelfsym") + return + } + elfshnum = xo.Sect.(*Section).Elfsect.(*ElfShdr).shnum + } + + // One pass for each binding: STB_LOCAL, STB_GLOBAL, + // maybe one day STB_WEAK. + bind := STB_GLOBAL + + if ver != 0 || (x.Type&obj.SHIDDEN != 0) || x.Local { + bind = STB_LOCAL + } + + // In external linking mode, we have to invoke gcc with -rdynamic + // to get the exported symbols put into the dynamic symbol table. + // To avoid filling the dynamic table with lots of unnecessary symbols, + // mark all Go symbols local (not global) in the final executable. + // But when we're dynamically linking, we need all those global symbols. + if !DynlinkingGo() && Linkmode == LinkExternal && x.Cgoexport&CgoExportStatic == 0 && elfshnum != SHN_UNDEF { + bind = STB_LOCAL + } + + if bind != elfbind { + return + } + + off := putelfstr(s) + if Linkmode == LinkExternal && elfshnum != SHN_UNDEF { + addr -= int64(xo.Sect.(*Section).Vaddr) + } + other := STV_DEFAULT + if x.Type&obj.SHIDDEN != 0 { + other = STV_HIDDEN + } + putelfsyment(off, addr, size, bind<<4|type_&0xf, elfshnum, other) + x.Elfsym = int32(numelfsym) + numelfsym++ +} + +func putelfsectionsym(s *LSym, shndx int) { + putelfsyment(0, 0, 0, STB_LOCAL<<4|STT_SECTION, shndx, 0) + s.Elfsym = int32(numelfsym) + numelfsym++ +} + +func putelfsymshndx(sympos int64, shndx int) { + here := Cpos() + if elf64 { + Cseek(sympos + 6) + } else { + Cseek(sympos + 14) + } + + Thearch.Wput(uint16(shndx)) + Cseek(here) +} + +func Asmelfsym() { + // the first symbol entry is reserved + putelfsyment(0, 0, 0, STB_LOCAL<<4|STT_NOTYPE, 0, 0) + + dwarfaddelfsectionsyms() + + elfbind = STB_LOCAL + genasmsym(putelfsym) + + elfbind = STB_GLOBAL + elfglobalsymndx = numelfsym + genasmsym(putelfsym) +} + +func putplan9sym(x *LSym, s string, t int, addr int64, size int64, ver int, go_ *LSym) { + switch t { + case 'T', 'L', 'D', 'B': + if ver != 0 { + t += 'a' - 'A' + } + fallthrough + + case 'a', + 'p', + 'f', + 'z', + 'Z', + 'm': + l := 4 + if HEADTYPE == obj.Hplan9 && Thearch.Thechar == '6' && Debug['8'] == 0 { + Lputb(uint32(addr >> 32)) + l = 8 + } + + Lputb(uint32(addr)) + Cput(uint8(t + 0x80)) /* 0x80 is variable length */ + + var i int + if t == 'z' || t == 'Z' { + Cput(uint8(s[0])) + for i = 1; s[i] != 0 || s[i+1] != 0; i += 2 { + Cput(uint8(s[i])) + Cput(uint8(s[i+1])) + } + + Cput(0) + Cput(0) + i++ + } else { + /* skip the '<' in filenames */ + if t == 'f' { + s = s[1:] + } + for i = 0; i < len(s); i++ { + Cput(uint8(s[i])) + } + Cput(0) + } + + Symsize += int32(l) + 1 + int32(i) + 1 + + default: + return + } +} + +func Asmplan9sym() { + genasmsym(putplan9sym) +} + +var symt *LSym + +func Wputl(w uint16) { + Cput(uint8(w)) + Cput(uint8(w >> 8)) +} + +func Wputb(w uint16) { + Cput(uint8(w >> 8)) + Cput(uint8(w)) +} + +func Lputb(l uint32) { + Cput(uint8(l >> 24)) + Cput(uint8(l >> 16)) + Cput(uint8(l >> 8)) + Cput(uint8(l)) +} + +func Lputl(l uint32) { + Cput(uint8(l)) + Cput(uint8(l >> 8)) + Cput(uint8(l >> 16)) + Cput(uint8(l >> 24)) +} + +func Vputb(v uint64) { + Lputb(uint32(v >> 32)) + Lputb(uint32(v)) +} + +func Vputl(v uint64) { + Lputl(uint32(v)) + 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() + + // Define these so that they'll get put into the symbol table. + // data.c:/^address will provide the actual values. + xdefine("runtime.text", obj.STEXT, 0) + + xdefine("runtime.etext", obj.STEXT, 0) + xdefine("runtime.typelink", obj.SRODATA, 0) + xdefine("runtime.etypelink", obj.SRODATA, 0) + xdefine("runtime.rodata", obj.SRODATA, 0) + xdefine("runtime.erodata", obj.SRODATA, 0) + xdefine("runtime.noptrdata", obj.SNOPTRDATA, 0) + xdefine("runtime.enoptrdata", obj.SNOPTRDATA, 0) + xdefine("runtime.data", obj.SDATA, 0) + xdefine("runtime.edata", obj.SDATA, 0) + xdefine("runtime.bss", obj.SBSS, 0) + xdefine("runtime.ebss", obj.SBSS, 0) + xdefine("runtime.noptrbss", obj.SNOPTRBSS, 0) + xdefine("runtime.enoptrbss", obj.SNOPTRBSS, 0) + xdefine("runtime.end", obj.SBSS, 0) + xdefine("runtime.epclntab", obj.SRODATA, 0) + xdefine("runtime.esymtab", obj.SRODATA, 0) + + // garbage collection symbols + s := Linklookup(Ctxt, "runtime.gcdata", 0) + + s.Type = obj.SRODATA + s.Size = 0 + s.Reachable = true + xdefine("runtime.egcdata", obj.SRODATA, 0) + + s = Linklookup(Ctxt, "runtime.gcbss", 0) + s.Type = obj.SRODATA + s.Size = 0 + s.Reachable = true + xdefine("runtime.egcbss", obj.SRODATA, 0) + + // pseudo-symbols to mark locations of type, string, and go string data. + var symtype *LSym + if !DynlinkingGo() { + s = Linklookup(Ctxt, "type.*", 0) + + s.Type = obj.STYPE + s.Size = 0 + s.Reachable = true + symtype = s + } + + s = Linklookup(Ctxt, "go.string.*", 0) + s.Type = obj.SGOSTRING + s.Local = true + s.Size = 0 + s.Reachable = true + symgostring := s + + s = Linklookup(Ctxt, "go.func.*", 0) + s.Type = obj.SGOFUNC + s.Local = true + s.Size = 0 + s.Reachable = true + symgofunc := s + + symtypelink := Linklookup(Ctxt, "runtime.typelink", 0) + + symt = Linklookup(Ctxt, "runtime.symtab", 0) + symt.Local = true + symt.Type = obj.SSYMTAB + symt.Size = 0 + symt.Reachable = true + + ntypelinks := 0 + + // assign specific types so that they sort together. + // within a type they sort by size, so the .* symbols + // just defined above will be first. + // hide the specific symbols. + for s := Ctxt.Allsym; s != nil; s = s.Allsym { + if !s.Reachable || s.Special != 0 || s.Type != obj.SRODATA { + continue + } + + if strings.HasPrefix(s.Name, "type.") && !DynlinkingGo() { + s.Type = obj.STYPE + s.Hide = 1 + s.Outer = symtype + } + + if strings.HasPrefix(s.Name, "go.typelink.") { + ntypelinks++ + s.Type = obj.STYPELINK + s.Hide = 1 + s.Outer = symtypelink + } + + if strings.HasPrefix(s.Name, "go.string.") { + s.Type = obj.SGOSTRING + s.Hide = 1 + s.Outer = symgostring + } + + if strings.HasPrefix(s.Name, "go.func.") { + s.Type = obj.SGOFUNC + s.Hide = 1 + s.Outer = symgofunc + } + + if strings.HasPrefix(s.Name, "gcargs.") || strings.HasPrefix(s.Name, "gclocals.") || strings.HasPrefix(s.Name, "gclocals·") { + s.Type = obj.SGOFUNC + s.Hide = 1 + s.Outer = symgofunc + s.Align = 4 + liveness += (s.Size + int64(s.Align) - 1) &^ (int64(s.Align) - 1) + } + } + + 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. + // This code uses several global variables that are set by pcln.go:pclntab. + moduledata := Linklookup(Ctxt, "runtime.firstmoduledata", 0) + moduledata.Type = obj.SNOPTRDATA + moduledata.Size = 0 // truncate symbol back to 0 bytes to reinitialize + moduledata.Reachable = true + moduledata.Local = true + // The pclntab slice + Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.pclntab", 0)) + adduint(Ctxt, moduledata, uint64(Linklookup(Ctxt, "runtime.pclntab", 0).Size)) + adduint(Ctxt, moduledata, uint64(Linklookup(Ctxt, "runtime.pclntab", 0).Size)) + // The ftab slice + Addaddrplus(Ctxt, moduledata, Linklookup(Ctxt, "runtime.pclntab", 0), int64(pclntabPclntabOffset)) + adduint(Ctxt, moduledata, uint64(pclntabNfunc+1)) + adduint(Ctxt, moduledata, uint64(pclntabNfunc+1)) + // The filetab slice + Addaddrplus(Ctxt, moduledata, Linklookup(Ctxt, "runtime.pclntab", 0), int64(pclntabFiletabOffset)) + adduint(Ctxt, moduledata, uint64(Ctxt.Nhistfile)) + adduint(Ctxt, moduledata, uint64(Ctxt.Nhistfile)) + // findfunctab + Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.findfunctab", 0)) + // minpc, maxpc + Addaddr(Ctxt, moduledata, pclntabFirstFunc) + Addaddrplus(Ctxt, moduledata, pclntabLastFunc, pclntabLastFunc.Size) + // pointers to specific parts of the module + Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.text", 0)) + Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.etext", 0)) + Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.noptrdata", 0)) + Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.enoptrdata", 0)) + Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.data", 0)) + Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.edata", 0)) + Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.bss", 0)) + Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.ebss", 0)) + Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.noptrbss", 0)) + Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.enoptrbss", 0)) + Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.end", 0)) + Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.gcdata", 0)) + Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.gcbss", 0)) + // The typelinks slice + 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 + // compiler-provided size, so read it from the type data. + moduledatatype := Linkrlookup(Ctxt, "type.runtime.moduledata", 0) + moduledata.Size = decodetype_size(moduledatatype) + Symgrow(Ctxt, moduledata, moduledata.Size) + + lastmoduledatap := Linklookup(Ctxt, "runtime.lastmoduledatap", 0) + if lastmoduledatap.Type != obj.SDYNIMPORT { + lastmoduledatap.Type = obj.SNOPTRDATA + lastmoduledatap.Size = 0 // overwrite existing value + Addaddr(Ctxt, lastmoduledatap, moduledata) + } +} diff --git a/src/cmd/link/internal/ld/textflag.go b/src/cmd/link/internal/ld/textflag.go new file mode 100644 index 0000000000..335f20d21d --- /dev/null +++ b/src/cmd/link/internal/ld/textflag.go @@ -0,0 +1,133 @@ +// Copyright 2013 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 + +// Writing and reading of Go object files. +// +// Originally, Go object files were Plan 9 object files, but no longer. +// Now they are more like standard object files, in that each symbol is defined +// by an associated memory image (bytes) and a list of relocations to apply +// during linking. We do not (yet?) use a standard file format, however. +// For now, the format is chosen to be as simple as possible to read and write. +// It may change for reasons of efficiency, or we may even switch to a +// standard file format if there are compelling benefits to doing so. +// See golang.org/s/go13linker for more background. +// +// The file format is: +// +// - magic header: "\x00\x00go13ld" +// - byte 1 - version number +// - sequence of strings giving dependencies (imported packages) +// - empty string (marks end of sequence) +// - sequence of defined symbols +// - byte 0xff (marks end of sequence) +// - magic footer: "\xff\xffgo13ld" +// +// All integers are stored in a zigzag varint format. +// See golang.org/s/go12symtab for a definition. +// +// Data blocks and strings are both stored as an integer +// followed by that many bytes. +// +// A symbol reference is a string name followed by a version. +// An empty name corresponds to a nil LSym* pointer. +// +// Each symbol is laid out as the following fields (taken from LSym*): +// +// - byte 0xfe (sanity check for synchronization) +// - type [int] +// - name [string] +// - version [int] +// - flags [int] +// 1 dupok +// - size [int] +// - gotype [symbol reference] +// - p [data block] +// - nr [int] +// - r [nr relocations, sorted by off] +// +// If type == STEXT, there are a few more fields: +// +// - args [int] +// - locals [int] +// - nosplit [int] +// - flags [int] +// 1 leaf +// 2 C function +// - nlocal [int] +// - local [nlocal automatics] +// - pcln [pcln table] +// +// Each relocation has the encoding: +// +// - off [int] +// - siz [int] +// - type [int] +// - add [int] +// - xadd [int] +// - sym [symbol reference] +// - xsym [symbol reference] +// +// Each local has the encoding: +// +// - asym [symbol reference] +// - offset [int] +// - type [int] +// - gotype [symbol reference] +// +// The pcln table has the encoding: +// +// - pcsp [data block] +// - pcfile [data block] +// - pcline [data block] +// - npcdata [int] +// - pcdata [npcdata data blocks] +// - nfuncdata [int] +// - funcdata [nfuncdata symbol references] +// - funcdatasym [nfuncdata ints] +// - nfile [int] +// - file [nfile symbol references] +// +// The file layout and meaning of type integers are architecture-independent. +// +// TODO(rsc): The file format is good for a first pass but needs work. +// - There are SymID in the object file that should really just be strings. +// - The actual symbol memory images are interlaced with the symbol +// metadata. They should be separated, to reduce the I/O required to +// load just the metadata. +// - The symbol references should be shortened, either with a symbol +// table or by using a simple backward index to an earlier mentioned symbol. + +// Copyright 2013 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. + +// This file defines flags attached to various functions +// and data objects. The compilers, assemblers, and linker must +// all agree on these values. + +// Don't profile the marked routine. This flag is deprecated. + +// It is ok for the linker to get multiple of these symbols. It will +// pick one of the duplicates to use. + +// Don't insert stack check preamble. + +// Put this data in a read-only section. + +// This data contains no pointers. + +// This is a wrapper function and should not count as disabling 'recover'. + +// This function uses its incoming context register. +const ( + NOPROF = 1 + DUPOK = 2 + NOSPLIT = 4 + RODATA = 8 + NOPTR = 16 + WRAPPER = 32 + NEEDCTXT = 64 +) diff --git a/src/cmd/link/internal/ld/util.go b/src/cmd/link/internal/ld/util.go new file mode 100644 index 0000000000..f38f05ce9d --- /dev/null +++ b/src/cmd/link/internal/ld/util.go @@ -0,0 +1,172 @@ +// 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" + "encoding/binary" + "log" + "os" + "runtime" + "runtime/pprof" + "strings" + "time" +) + +func cstring(x []byte) string { + i := bytes.IndexByte(x, '\x00') + if i >= 0 { + x = x[:i] + } + return string(x) +} + +func tokenize(s string) []string { + var f []string + for { + s = strings.TrimLeft(s, " \t\r\n") + if s == "" { + break + } + quote := false + i := 0 + for ; i < len(s); i++ { + if s[i] == '\'' { + if quote && i+1 < len(s) && s[i+1] == '\'' { + i++ + continue + } + quote = !quote + } + if !quote && (s[i] == ' ' || s[i] == '\t' || s[i] == '\r' || s[i] == '\n') { + break + } + } + next := s[:i] + s = s[i:] + if strings.Contains(next, "'") { + var buf []byte + quote := false + for i := 0; i < len(next); i++ { + if next[i] == '\'' { + if quote && i+1 < len(next) && next[i+1] == '\'' { + i++ + buf = append(buf, '\'') + } + quote = !quote + continue + } + buf = append(buf, next[i]) + } + next = string(buf) + } + f = append(f, next) + } + return f +} + +func cutStringAtNUL(s string) string { + if i := strings.Index(s, "\x00"); i >= 0 { + s = s[:i] + } + return s +} + +func Access(name string, mode int) int { + if mode != 0 { + panic("bad access") + } + _, err := os.Stat(name) + if err != nil { + return -1 + } + return 0 +} + +// strings.Compare, introduced in Go 1.5. +func stringsCompare(a, b string) int { + if a == b { + return 0 + } + if a < b { + return -1 + } + return +1 +} + +var atExitFuncs []func() + +func AtExit(f func()) { + atExitFuncs = append(atExitFuncs, f) +} + +func Exit(code int) { + for i := len(atExitFuncs) - 1; i >= 0; i-- { + f := atExitFuncs[i] + atExitFuncs = atExitFuncs[:i] + f() + } + os.Exit(code) +} + +var ( + cpuprofile string + memprofile string + memprofilerate int64 +) + +func startProfile() { + if cpuprofile != "" { + f, err := os.Create(cpuprofile) + if err != nil { + log.Fatalf("%v", err) + } + if err := pprof.StartCPUProfile(f); err != nil { + log.Fatalf("%v", err) + } + AtExit(pprof.StopCPUProfile) + } + if memprofile != "" { + if memprofilerate != 0 { + runtime.MemProfileRate = int(memprofilerate) + } + f, err := os.Create(memprofile) + if err != nil { + log.Fatalf("%v", err) + } + AtExit(func() { + runtime.GC() // profile all outstanding allocations + if err := pprof.WriteHeapProfile(f); err != nil { + log.Fatalf("%v", err) + } + }) + } +} + +func artrim(x []byte) string { + i := 0 + j := len(x) + for i < len(x) && x[i] == ' ' { + i++ + } + for j > i && x[j-1] == ' ' { + j-- + } + return string(x[i:j]) +} + +func stringtouint32(x []uint32, s string) { + for i := 0; len(s) > 0; i++ { + var buf [4]byte + s = s[copy(buf[:], s):] + x[i] = binary.LittleEndian.Uint32(buf[:]) + } +} + +var start = time.Now() + +func elapsed() float64 { + return time.Since(start).Seconds() +} diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go new file mode 100644 index 0000000000..f070921ecf --- /dev/null +++ b/src/cmd/link/internal/ppc64/asm.go @@ -0,0 +1,737 @@ +// Inferno utils/5l/asm.c +// http://code.google.com/p/inferno-os/source/browse/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ppc64 + +import ( + "cmd/internal/obj" + "cmd/link/internal/ld" + "encoding/binary" + "fmt" + "log" +) + +func gentext() { + var s *ld.LSym + var stub *ld.LSym + var pprevtextp **ld.LSym + var r *ld.Reloc + var n string + var o1 uint32 + var i int + + // The ppc64 ABI PLT has similar concepts to other + // architectures, but is laid out quite differently. When we + // see an R_PPC64_REL24 relocation to a dynamic symbol + // (indicating that the call needs to go through the PLT), we + // generate up to three stubs and reserve a PLT slot. + // + // 1) The call site will be bl x; nop (where the relocation + // applies to the bl). We rewrite this to bl x_stub; ld + // r2,24(r1). The ld is necessary because x_stub will save + // r2 (the TOC pointer) at 24(r1) (the "TOC save slot"). + // + // 2) We reserve space for a pointer in the .plt section (once + // per referenced dynamic function). .plt is a data + // section filled solely by the dynamic linker (more like + // .plt.got on other architectures). Initially, the + // dynamic linker will fill each slot with a pointer to the + // corresponding x@plt entry point. + // + // 3) We generate the "call stub" x_stub (once per dynamic + // function/object file pair). This saves the TOC in the + // TOC save slot, reads the function pointer from x's .plt + // slot and calls it like any other global entry point + // (including setting r12 to the function address). + // + // 4) We generate the "symbol resolver stub" x@plt (once per + // dynamic function). This is solely a branch to the glink + // resolver stub. + // + // 5) We generate the glink resolver stub (only once). This + // computes which symbol resolver stub we came through and + // invokes the dynamic resolver via a pointer provided by + // the dynamic linker. This will patch up the .plt slot to + // point directly at the function so future calls go + // straight from the call stub to the real function, and + // then call the function. + + // NOTE: It's possible we could make ppc64 closer to other + // architectures: ppc64's .plt is like .plt.got on other + // platforms and ppc64's .glink is like .plt on other + // platforms. + + // Find all R_PPC64_REL24 relocations that reference dynamic + // imports. Reserve PLT entries for these symbols and + // generate call stubs. The call stubs need to live in .text, + // which is why we need to do this pass this early. + // + // This assumes "case 1" from the ABI, where the caller needs + // us to save and restore the TOC pointer. + pprevtextp = &ld.Ctxt.Textp + + for s = *pprevtextp; s != nil; pprevtextp, s = &s.Next, s.Next { + for i = range s.R { + r = &s.R[i] + if r.Type != 256+ld.R_PPC64_REL24 || r.Sym.Type != obj.SDYNIMPORT { + continue + } + + // Reserve PLT entry and generate symbol + // resolver + addpltsym(ld.Ctxt, r.Sym) + + // Generate call stub + n = fmt.Sprintf("%s.%s", s.Name, r.Sym.Name) + + stub = ld.Linklookup(ld.Ctxt, n, 0) + stub.Reachable = stub.Reachable || s.Reachable + if stub.Size == 0 { + // Need outer to resolve .TOC. + stub.Outer = s + + // Link in to textp before s (we could + // do it after, but would have to skip + // the subsymbols) + *pprevtextp = stub + + stub.Next = s + pprevtextp = &stub.Next + + gencallstub(1, stub, r.Sym) + } + + // Update the relocation to use the call stub + r.Sym = stub + + // Restore TOC after bl. The compiler put a + // nop here for us to overwrite. + o1 = 0xe8410018 // ld r2,24(r1) + ld.Ctxt.Arch.ByteOrder.PutUint32(s.P[r.Off+4:], o1) + } + } +} + +// Construct a call stub in stub that calls symbol targ via its PLT +// entry. +func gencallstub(abicase int, stub *ld.LSym, targ *ld.LSym) { + if abicase != 1 { + // If we see R_PPC64_TOCSAVE or R_PPC64_REL24_NOTOC + // relocations, we'll need to implement cases 2 and 3. + log.Fatalf("gencallstub only implements case 1 calls") + } + + plt := ld.Linklookup(ld.Ctxt, ".plt", 0) + + stub.Type = obj.STEXT + + // Save TOC pointer in TOC save slot + ld.Adduint32(ld.Ctxt, stub, 0xf8410018) // std r2,24(r1) + + // Load the function pointer from the PLT. + r := ld.Addrel(stub) + + r.Off = int32(stub.Size) + r.Sym = plt + r.Add = int64(targ.Plt) + r.Siz = 2 + if ld.Ctxt.Arch.ByteOrder == binary.BigEndian { + r.Off += int32(r.Siz) + } + r.Type = obj.R_POWER_TOC + r.Variant = ld.RV_POWER_HA + ld.Adduint32(ld.Ctxt, stub, 0x3d820000) // addis r12,r2,targ@plt@toc@ha + r = ld.Addrel(stub) + r.Off = int32(stub.Size) + r.Sym = plt + r.Add = int64(targ.Plt) + r.Siz = 2 + if ld.Ctxt.Arch.ByteOrder == binary.BigEndian { + r.Off += int32(r.Siz) + } + r.Type = obj.R_POWER_TOC + r.Variant = ld.RV_POWER_LO + ld.Adduint32(ld.Ctxt, stub, 0xe98c0000) // ld r12,targ@plt@toc@l(r12) + + // Jump to the loaded pointer + ld.Adduint32(ld.Ctxt, stub, 0x7d8903a6) // mtctr r12 + ld.Adduint32(ld.Ctxt, stub, 0x4e800420) // bctr +} + +func adddynrela(rel *ld.LSym, s *ld.LSym, r *ld.Reloc) { + log.Fatalf("adddynrela not implemented") +} + +func adddynrel(s *ld.LSym, r *ld.Reloc) { + targ := r.Sym + ld.Ctxt.Cursym = s + + switch r.Type { + default: + if r.Type >= 256 { + ld.Diag("unexpected relocation type %d", r.Type) + return + } + + // Handle relocations found in ELF object files. + case 256 + ld.R_PPC64_REL24: + r.Type = obj.R_CALLPOWER + + // This is a local call, so the caller isn't setting + // up r12 and r2 is the same for the caller and + // callee. Hence, we need to go to the local entry + // point. (If we don't do this, the callee will try + // to use r12 to compute r2.) + r.Add += int64(r.Sym.Localentry) * 4 + + if targ.Type == obj.SDYNIMPORT { + // Should have been handled in elfsetupplt + ld.Diag("unexpected R_PPC64_REL24 for dyn import") + } + + return + + case 256 + ld.R_PPC64_ADDR64: + r.Type = obj.R_ADDR + if targ.Type == obj.SDYNIMPORT { + // These happen in .toc sections + ld.Adddynsym(ld.Ctxt, targ) + + rela := ld.Linklookup(ld.Ctxt, ".rela", 0) + ld.Addaddrplus(ld.Ctxt, rela, s, int64(r.Off)) + ld.Adduint64(ld.Ctxt, rela, ld.ELF64_R_INFO(uint32(targ.Dynid), ld.R_PPC64_ADDR64)) + ld.Adduint64(ld.Ctxt, rela, uint64(r.Add)) + r.Type = 256 // ignore during relocsym + } + + return + + case 256 + ld.R_PPC64_TOC16: + r.Type = obj.R_POWER_TOC + r.Variant = ld.RV_POWER_LO | ld.RV_CHECK_OVERFLOW + return + + case 256 + ld.R_PPC64_TOC16_LO: + r.Type = obj.R_POWER_TOC + r.Variant = ld.RV_POWER_LO + return + + case 256 + ld.R_PPC64_TOC16_HA: + r.Type = obj.R_POWER_TOC + r.Variant = ld.RV_POWER_HA | ld.RV_CHECK_OVERFLOW + return + + case 256 + ld.R_PPC64_TOC16_HI: + r.Type = obj.R_POWER_TOC + r.Variant = ld.RV_POWER_HI | ld.RV_CHECK_OVERFLOW + return + + case 256 + ld.R_PPC64_TOC16_DS: + r.Type = obj.R_POWER_TOC + r.Variant = ld.RV_POWER_DS | ld.RV_CHECK_OVERFLOW + return + + case 256 + ld.R_PPC64_TOC16_LO_DS: + r.Type = obj.R_POWER_TOC + r.Variant = ld.RV_POWER_DS + return + + case 256 + ld.R_PPC64_REL16_LO: + r.Type = obj.R_PCREL + r.Variant = ld.RV_POWER_LO + r.Add += 2 // Compensate for relocation size of 2 + return + + case 256 + ld.R_PPC64_REL16_HI: + r.Type = obj.R_PCREL + r.Variant = ld.RV_POWER_HI | ld.RV_CHECK_OVERFLOW + r.Add += 2 + return + + case 256 + ld.R_PPC64_REL16_HA: + r.Type = obj.R_PCREL + r.Variant = ld.RV_POWER_HA | ld.RV_CHECK_OVERFLOW + r.Add += 2 + return + } + + // Handle references to ELF symbols from our own object files. + if targ.Type != obj.SDYNIMPORT { + return + } + + // TODO(austin): Translate our relocations to ELF + + ld.Diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ.Name, r.Type, targ.Type) +} + +func elfreloc1(r *ld.Reloc, sectoff int64) int { + // TODO(minux) + return -1 +} + +func elfsetupplt() { + plt := ld.Linklookup(ld.Ctxt, ".plt", 0) + if plt.Size == 0 { + // The dynamic linker stores the address of the + // dynamic resolver and the DSO identifier in the two + // doublewords at the beginning of the .plt section + // before the PLT array. Reserve space for these. + plt.Size = 16 + } +} + +func machoreloc1(r *ld.Reloc, sectoff int64) int { + return -1 +} + +// Return the value of .TOC. for symbol s +func symtoc(s *ld.LSym) int64 { + var toc *ld.LSym + + if s.Outer != nil { + toc = ld.Linkrlookup(ld.Ctxt, ".TOC.", int(s.Outer.Version)) + } else { + toc = ld.Linkrlookup(ld.Ctxt, ".TOC.", int(s.Version)) + } + + if toc == nil { + ld.Diag("TOC-relative relocation in object without .TOC.") + return 0 + } + + return toc.Value +} + +func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int { + if ld.Linkmode == ld.LinkExternal { + // TODO(minux): translate R_ADDRPOWER and R_CALLPOWER into standard ELF relocations. + // R_ADDRPOWER corresponds to R_PPC_ADDR16_HA and R_PPC_ADDR16_LO. + // R_CALLPOWER corresponds to R_PPC_REL24. + return -1 + } + + switch r.Type { + case obj.R_CONST: + *val = r.Add + return 0 + + case obj.R_GOTOFF: + *val = ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ld.Linklookup(ld.Ctxt, ".got", 0)) + return 0 + + case obj.R_ADDRPOWER: + // r->add is two ppc64 instructions holding an immediate 32-bit constant. + // We want to add r->sym's address to that constant. + // The encoding of the immediate x<<16 + y, + // where x is the low 16 bits of the first instruction and y is the low 16 + // bits of the second. Both x and y are signed (int16, not uint16). + o1 := uint32(r.Add >> 32) + o2 := uint32(r.Add) + t := ld.Symaddr(r.Sym) + if t < 0 { + ld.Ctxt.Diag("relocation for %s is too big (>=2G): %d", s.Name, ld.Symaddr(r.Sym)) + } + + t += int64((o1&0xffff)<<16 + uint32(int32(o2)<<16>>16)) + if t&0x8000 != 0 { + t += 0x10000 + } + o1 = o1&0xffff0000 | (uint32(t)>>16)&0xffff + o2 = o2&0xffff0000 | uint32(t)&0xffff + + // when laid out, the instruction order must always be o1, o2. + if ld.Ctxt.Arch.ByteOrder == binary.BigEndian { + *val = int64(o1)<<32 | int64(o2) + } else { + *val = int64(o2)<<32 | int64(o1) + } + return 0 + + case obj.R_CALLPOWER: + // Bits 6 through 29 = (S + A - P) >> 2 + var o1 uint32 + if ld.Ctxt.Arch.ByteOrder == binary.BigEndian { + o1 = ld.Be32(s.P[r.Off:]) + } else { + o1 = ld.Le32(s.P[r.Off:]) + } + + t := ld.Symaddr(r.Sym) + r.Add - (s.Value + int64(r.Off)) + if t&3 != 0 { + ld.Ctxt.Diag("relocation for %s+%d is not aligned: %d", r.Sym.Name, r.Off, t) + } + if int64(int32(t<<6)>>6) != t { + // TODO(austin) This can happen if text > 32M. + // Add a call trampoline to .text in that case. + ld.Ctxt.Diag("relocation for %s+%d is too big: %d", r.Sym.Name, r.Off, t) + } + + *val = int64(o1&0xfc000003 | uint32(t)&^0xfc000003) + return 0 + + case obj.R_POWER_TOC: // S + A - .TOC. + *val = ld.Symaddr(r.Sym) + r.Add - symtoc(s) + + return 0 + } + + return -1 +} + +func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 { + switch r.Variant & ld.RV_TYPE_MASK { + default: + ld.Diag("unexpected relocation variant %d", r.Variant) + fallthrough + + case ld.RV_NONE: + return t + + case ld.RV_POWER_LO: + if r.Variant&ld.RV_CHECK_OVERFLOW != 0 { + // Whether to check for signed or unsigned + // overflow depends on the instruction + var o1 uint32 + if ld.Ctxt.Arch.ByteOrder == binary.BigEndian { + o1 = ld.Be32(s.P[r.Off-2:]) + } else { + o1 = ld.Le32(s.P[r.Off:]) + } + switch o1 >> 26 { + case 24, // ori + 26, // xori + 28: // andi + if t>>16 != 0 { + goto overflow + } + + default: + if int64(int16(t)) != t { + goto overflow + } + } + } + + return int64(int16(t)) + + case ld.RV_POWER_HA: + t += 0x8000 + fallthrough + + // Fallthrough + case ld.RV_POWER_HI: + t >>= 16 + + if r.Variant&ld.RV_CHECK_OVERFLOW != 0 { + // Whether to check for signed or unsigned + // overflow depends on the instruction + var o1 uint32 + if ld.Ctxt.Arch.ByteOrder == binary.BigEndian { + o1 = ld.Be32(s.P[r.Off-2:]) + } else { + o1 = ld.Le32(s.P[r.Off:]) + } + switch o1 >> 26 { + case 25, // oris + 27, // xoris + 29: // andis + if t>>16 != 0 { + goto overflow + } + + default: + if int64(int16(t)) != t { + goto overflow + } + } + } + + return int64(int16(t)) + + case ld.RV_POWER_DS: + var o1 uint32 + if ld.Ctxt.Arch.ByteOrder == binary.BigEndian { + o1 = uint32(ld.Be16(s.P[r.Off:])) + } else { + o1 = uint32(ld.Le16(s.P[r.Off:])) + } + if t&3 != 0 { + ld.Diag("relocation for %s+%d is not aligned: %d", r.Sym.Name, r.Off, t) + } + if (r.Variant&ld.RV_CHECK_OVERFLOW != 0) && int64(int16(t)) != t { + goto overflow + } + return int64(o1)&0x3 | int64(int16(t)) + } + +overflow: + ld.Diag("relocation for %s+%d is too big: %d", r.Sym.Name, r.Off, t) + return t +} + +func addpltsym(ctxt *ld.Link, s *ld.LSym) { + if s.Plt >= 0 { + return + } + + ld.Adddynsym(ctxt, s) + + if ld.Iself { + plt := ld.Linklookup(ctxt, ".plt", 0) + rela := ld.Linklookup(ctxt, ".rela.plt", 0) + if plt.Size == 0 { + elfsetupplt() + } + + // Create the glink resolver if necessary + glink := ensureglinkresolver() + + // Write symbol resolver stub (just a branch to the + // glink resolver stub) + r := ld.Addrel(glink) + + r.Sym = glink + r.Off = int32(glink.Size) + r.Siz = 4 + r.Type = obj.R_CALLPOWER + ld.Adduint32(ctxt, glink, 0x48000000) // b .glink + + // In the ppc64 ABI, the dynamic linker is responsible + // for writing the entire PLT. We just need to + // reserve 8 bytes for each PLT entry and generate a + // JMP_SLOT dynamic relocation for it. + // + // TODO(austin): ABI v1 is different + s.Plt = int32(plt.Size) + + plt.Size += 8 + + ld.Addaddrplus(ctxt, rela, plt, int64(s.Plt)) + ld.Adduint64(ctxt, rela, ld.ELF64_R_INFO(uint32(s.Dynid), ld.R_PPC64_JMP_SLOT)) + ld.Adduint64(ctxt, rela, 0) + } else { + ld.Diag("addpltsym: unsupported binary format") + } +} + +// Generate the glink resolver stub if necessary and return the .glink section +func ensureglinkresolver() *ld.LSym { + glink := ld.Linklookup(ld.Ctxt, ".glink", 0) + if glink.Size != 0 { + return glink + } + + // This is essentially the resolver from the ppc64 ELF ABI. + // At entry, r12 holds the address of the symbol resolver stub + // for the target routine and the argument registers hold the + // arguments for the target routine. + // + // This stub is PIC, so first get the PC of label 1 into r11. + // Other things will be relative to this. + ld.Adduint32(ld.Ctxt, glink, 0x7c0802a6) // mflr r0 + ld.Adduint32(ld.Ctxt, glink, 0x429f0005) // bcl 20,31,1f + ld.Adduint32(ld.Ctxt, glink, 0x7d6802a6) // 1: mflr r11 + ld.Adduint32(ld.Ctxt, glink, 0x7c0803a6) // mtlf r0 + + // Compute the .plt array index from the entry point address. + // Because this is PIC, everything is relative to label 1b (in + // r11): + // r0 = ((r12 - r11) - (res_0 - r11)) / 4 = (r12 - res_0) / 4 + ld.Adduint32(ld.Ctxt, glink, 0x3800ffd0) // li r0,-(res_0-1b)=-48 + ld.Adduint32(ld.Ctxt, glink, 0x7c006214) // add r0,r0,r12 + ld.Adduint32(ld.Ctxt, glink, 0x7c0b0050) // sub r0,r0,r11 + ld.Adduint32(ld.Ctxt, glink, 0x7800f082) // srdi r0,r0,2 + + // r11 = address of the first byte of the PLT + r := ld.Addrel(glink) + + r.Off = int32(glink.Size) + r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) + r.Siz = 8 + r.Type = obj.R_ADDRPOWER + + // addis r11,0,.plt@ha; addi r11,r11,.plt@l + r.Add = 0x3d600000<<32 | 0x396b0000 + + glink.Size += 8 + + // Load r12 = dynamic resolver address and r11 = DSO + // identifier from the first two doublewords of the PLT. + ld.Adduint32(ld.Ctxt, glink, 0xe98b0000) // ld r12,0(r11) + ld.Adduint32(ld.Ctxt, glink, 0xe96b0008) // ld r11,8(r11) + + // Jump to the dynamic resolver + ld.Adduint32(ld.Ctxt, glink, 0x7d8903a6) // mtctr r12 + ld.Adduint32(ld.Ctxt, glink, 0x4e800420) // bctr + + // The symbol resolvers must immediately follow. + // res_0: + + // Add DT_PPC64_GLINK .dynamic entry, which points to 32 bytes + // before the first symbol resolver stub. + s := ld.Linklookup(ld.Ctxt, ".dynamic", 0) + + ld.Elfwritedynentsymplus(s, ld.DT_PPC64_GLINK, glink, glink.Size-32) + + return glink +} + +func asmb() { + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime()) + } + ld.Bso.Flush() + + if ld.Iself { + ld.Asmbelfsetup() + } + + sect := ld.Segtext.Sect + ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Codeblk(int64(sect.Vaddr), int64(sect.Length)) + for sect = sect.Next; sect != nil; sect = sect.Next { + ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Datblk(int64(sect.Vaddr), int64(sect.Length)) + } + + if ld.Segrodata.Filelen > 0 { + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) + } + ld.Bso.Flush() + + ld.Cseek(int64(ld.Segrodata.Fileoff)) + ld.Datblk(int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) + } + + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f datblk\n", obj.Cputime()) + } + ld.Bso.Flush() + + ld.Cseek(int64(ld.Segdata.Fileoff)) + ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + + /* output symbol table */ + ld.Symsize = 0 + + ld.Lcsize = 0 + symo := uint32(0) + if ld.Debug['s'] == 0 { + // TODO: rationalize + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime()) + } + ld.Bso.Flush() + switch ld.HEADTYPE { + default: + if ld.Iself { + symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) + symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND))) + } + + case obj.Hplan9: + symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) + } + + ld.Cseek(int64(symo)) + switch ld.HEADTYPE { + default: + if ld.Iself { + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f elfsym\n", obj.Cputime()) + } + ld.Asmelfsym() + ld.Cflush() + ld.Cwrite(ld.Elfstrdat) + + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) + } + ld.Dwarfemitdebugsections() + + if ld.Linkmode == ld.LinkExternal { + ld.Elfemitreloc() + } + } + + case obj.Hplan9: + ld.Asmplan9sym() + ld.Cflush() + + sym := ld.Linklookup(ld.Ctxt, "pclntab", 0) + if sym != nil { + ld.Lcsize = int32(len(sym.P)) + for i := 0; int32(i) < ld.Lcsize; i++ { + ld.Cput(uint8(sym.P[i])) + } + + ld.Cflush() + } + } + } + + ld.Ctxt.Cursym = nil + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f header\n", obj.Cputime()) + } + ld.Bso.Flush() + ld.Cseek(0) + switch ld.HEADTYPE { + default: + case obj.Hplan9: /* plan 9 */ + ld.Thearch.Lput(0x647) /* magic */ + ld.Thearch.Lput(uint32(ld.Segtext.Filelen)) /* sizes */ + ld.Thearch.Lput(uint32(ld.Segdata.Filelen)) + ld.Thearch.Lput(uint32(ld.Segdata.Length - ld.Segdata.Filelen)) + ld.Thearch.Lput(uint32(ld.Symsize)) /* nsyms */ + ld.Thearch.Lput(uint32(ld.Entryvalue())) /* va of entry */ + ld.Thearch.Lput(0) + ld.Thearch.Lput(uint32(ld.Lcsize)) + + case obj.Hlinux, + obj.Hfreebsd, + obj.Hnetbsd, + obj.Hopenbsd, + obj.Hnacl: + ld.Asmbelf(int64(symo)) + } + + ld.Cflush() + if ld.Debug['c'] != 0 { + fmt.Printf("textsize=%d\n", ld.Segtext.Filelen) + fmt.Printf("datsize=%d\n", ld.Segdata.Filelen) + fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen) + fmt.Printf("symsize=%d\n", ld.Symsize) + fmt.Printf("lcsize=%d\n", ld.Lcsize) + fmt.Printf("total=%d\n", ld.Segtext.Filelen+ld.Segdata.Length+uint64(ld.Symsize)+uint64(ld.Lcsize)) + } +} diff --git a/src/cmd/link/internal/ppc64/l.go b/src/cmd/link/internal/ppc64/l.go new file mode 100644 index 0000000000..1275a34dbb --- /dev/null +++ b/src/cmd/link/internal/ppc64/l.go @@ -0,0 +1,78 @@ +// Inferno utils/5l/asm.c +// http://code.google.com/p/inferno-os/source/browse/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ppc64 + +// Writing object files. + +// cmd/9l/l.h from Vita Nuova. +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +const ( + thechar = '9' + PtrSize = 8 + IntSize = 8 + RegSize = 8 + MaxAlign = 32 // max data alignment + FuncAlign = 8 + MINLC = 4 +) + +/* Used by ../internal/ld/dwarf.go */ +const ( + DWARFREGSP = 1 + DWARFREGLR = 65 +) diff --git a/src/cmd/link/internal/ppc64/obj.go b/src/cmd/link/internal/ppc64/obj.go new file mode 100644 index 0000000000..d663b6ebae --- /dev/null +++ b/src/cmd/link/internal/ppc64/obj.go @@ -0,0 +1,162 @@ +// Inferno utils/5l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/5l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ppc64 + +import ( + "cmd/internal/obj" + "cmd/link/internal/ld" + "fmt" + "log" +) + +// Reading object files. + +func Main() { + linkarchinit() + ld.Ldmain() +} + +func linkarchinit() { + ld.Thestring = obj.Getgoarch() + if ld.Thestring == "ppc64le" { + ld.Thelinkarch = &ld.Linkppc64le + } else { + ld.Thelinkarch = &ld.Linkppc64 + } + + ld.Thearch.Thechar = thechar + ld.Thearch.Ptrsize = ld.Thelinkarch.Ptrsize + ld.Thearch.Intsize = ld.Thelinkarch.Ptrsize + ld.Thearch.Regsize = ld.Thelinkarch.Regsize + ld.Thearch.Funcalign = FuncAlign + ld.Thearch.Maxalign = MaxAlign + ld.Thearch.Minlc = MINLC + ld.Thearch.Dwarfregsp = DWARFREGSP + ld.Thearch.Dwarfreglr = DWARFREGLR + + ld.Thearch.Adddynrel = adddynrel + ld.Thearch.Archinit = archinit + ld.Thearch.Archreloc = archreloc + ld.Thearch.Archrelocvariant = archrelocvariant + ld.Thearch.Asmb = asmb + ld.Thearch.Elfreloc1 = elfreloc1 + ld.Thearch.Elfsetupplt = elfsetupplt + ld.Thearch.Gentext = gentext + ld.Thearch.Machoreloc1 = machoreloc1 + if ld.Thelinkarch == &ld.Linkppc64le { + ld.Thearch.Lput = ld.Lputl + ld.Thearch.Wput = ld.Wputl + ld.Thearch.Vput = ld.Vputl + } else { + ld.Thearch.Lput = ld.Lputb + ld.Thearch.Wput = ld.Wputb + ld.Thearch.Vput = ld.Vputb + } + + // TODO(austin): ABI v1 uses /usr/lib/ld.so.1 + ld.Thearch.Linuxdynld = "/lib64/ld64.so.1" + + ld.Thearch.Freebsddynld = "XXX" + ld.Thearch.Openbsddynld = "XXX" + ld.Thearch.Netbsddynld = "XXX" + ld.Thearch.Dragonflydynld = "XXX" + ld.Thearch.Solarisdynld = "XXX" +} + +func archinit() { + // getgoextlinkenabled is based on GO_EXTLINK_ENABLED when + // Go was built; see ../../make.bash. + if ld.Linkmode == ld.LinkAuto && obj.Getgoextlinkenabled() == "0" { + ld.Linkmode = ld.LinkInternal + } + + switch ld.HEADTYPE { + default: + if ld.Linkmode == ld.LinkAuto { + ld.Linkmode = ld.LinkInternal + } + if ld.Linkmode == ld.LinkExternal && obj.Getgoextlinkenabled() != "1" { + log.Fatalf("cannot use -linkmode=external with -H %s", ld.Headstr(int(ld.HEADTYPE))) + } + } + + switch ld.HEADTYPE { + default: + ld.Exitf("unknown -H option: %v", ld.HEADTYPE) + + case obj.Hplan9: /* plan 9 */ + ld.HEADR = 32 + + if ld.INITTEXT == -1 { + ld.INITTEXT = 4128 + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 4096 + } + + case obj.Hlinux: /* ppc64 elf */ + if ld.Thestring == "ppc64" { + ld.Debug['d'] = 1 // TODO(austin): ELF ABI v1 not supported yet + } + ld.Elfinit() + ld.HEADR = ld.ELFRESERVE + if ld.INITTEXT == -1 { + ld.INITTEXT = 0x10000 + int64(ld.HEADR) + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 0x10000 + } + + case obj.Hnacl: + ld.Elfinit() + ld.HEADR = 0x10000 + ld.Funcalign = 16 + if ld.INITTEXT == -1 { + ld.INITTEXT = 0x20000 + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 0x10000 + } + } + + if ld.INITDAT != 0 && ld.INITRND != 0 { + fmt.Printf("warning: -D0x%x is ignored because of -R0x%x\n", uint64(ld.INITDAT), uint32(ld.INITRND)) + } +} diff --git a/src/cmd/link/internal/x86/asm.go b/src/cmd/link/internal/x86/asm.go new file mode 100644 index 0000000000..7bb99ca8b5 --- /dev/null +++ b/src/cmd/link/internal/x86/asm.go @@ -0,0 +1,643 @@ +// Inferno utils/8l/asm.c +// http://code.google.com/p/inferno-os/source/browse/utils/8l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package x86 + +import ( + "cmd/internal/obj" + "cmd/link/internal/ld" + "fmt" + "log" +) + +func gentext() { +} + +func adddynrela(rela *ld.LSym, s *ld.LSym, r *ld.Reloc) { + log.Fatalf("adddynrela not implemented") +} + +func adddynrel(s *ld.LSym, r *ld.Reloc) { + targ := r.Sym + ld.Ctxt.Cursym = s + + switch r.Type { + default: + if r.Type >= 256 { + ld.Diag("unexpected relocation type %d", r.Type) + return + } + + // Handle relocations found in ELF object files. + case 256 + ld.R_386_PC32: + if targ.Type == obj.SDYNIMPORT { + ld.Diag("unexpected R_386_PC32 relocation for dynamic symbol %s", targ.Name) + } + if targ.Type == 0 || targ.Type == obj.SXREF { + ld.Diag("unknown symbol %s in pcrel", targ.Name) + } + r.Type = obj.R_PCREL + r.Add += 4 + return + + case 256 + ld.R_386_PLT32: + r.Type = obj.R_PCREL + r.Add += 4 + if targ.Type == obj.SDYNIMPORT { + addpltsym(ld.Ctxt, targ) + r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) + r.Add += int64(targ.Plt) + } + + return + + case 256 + ld.R_386_GOT32: + if targ.Type != obj.SDYNIMPORT { + // have symbol + if r.Off >= 2 && s.P[r.Off-2] == 0x8b { + // turn MOVL of GOT entry into LEAL of symbol address, relative to GOT. + s.P[r.Off-2] = 0x8d + + r.Type = obj.R_GOTOFF + return + } + + if r.Off >= 2 && s.P[r.Off-2] == 0xff && s.P[r.Off-1] == 0xb3 { + // turn PUSHL of GOT entry into PUSHL of symbol itself. + // use unnecessary SS prefix to keep instruction same length. + s.P[r.Off-2] = 0x36 + + s.P[r.Off-1] = 0x68 + r.Type = obj.R_ADDR + return + } + + ld.Diag("unexpected GOT reloc for non-dynamic symbol %s", targ.Name) + return + } + + addgotsym(ld.Ctxt, targ) + r.Type = obj.R_CONST // write r->add during relocsym + r.Sym = nil + r.Add += int64(targ.Got) + return + + case 256 + ld.R_386_GOTOFF: + r.Type = obj.R_GOTOFF + return + + case 256 + ld.R_386_GOTPC: + r.Type = obj.R_PCREL + r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0) + r.Add += 4 + return + + case 256 + ld.R_386_32: + if targ.Type == obj.SDYNIMPORT { + ld.Diag("unexpected R_386_32 relocation for dynamic symbol %s", targ.Name) + } + r.Type = obj.R_ADDR + return + + case 512 + ld.MACHO_GENERIC_RELOC_VANILLA*2 + 0: + r.Type = obj.R_ADDR + if targ.Type == obj.SDYNIMPORT { + ld.Diag("unexpected reloc for dynamic symbol %s", targ.Name) + } + return + + case 512 + ld.MACHO_GENERIC_RELOC_VANILLA*2 + 1: + if targ.Type == obj.SDYNIMPORT { + addpltsym(ld.Ctxt, targ) + r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) + r.Add = int64(targ.Plt) + r.Type = obj.R_PCREL + return + } + + r.Type = obj.R_PCREL + return + + case 512 + ld.MACHO_FAKE_GOTPCREL: + if targ.Type != obj.SDYNIMPORT { + // have symbol + // turn MOVL of GOT entry into LEAL of symbol itself + if r.Off < 2 || s.P[r.Off-2] != 0x8b { + ld.Diag("unexpected GOT reloc for non-dynamic symbol %s", targ.Name) + return + } + + s.P[r.Off-2] = 0x8d + r.Type = obj.R_PCREL + return + } + + addgotsym(ld.Ctxt, targ) + r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0) + r.Add += int64(targ.Got) + r.Type = obj.R_PCREL + return + } + + // Handle references to ELF symbols from our own object files. + if targ.Type != obj.SDYNIMPORT { + return + } + + switch r.Type { + case obj.R_CALL, + obj.R_PCREL: + addpltsym(ld.Ctxt, targ) + r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) + r.Add = int64(targ.Plt) + return + + case obj.R_ADDR: + if s.Type != obj.SDATA { + break + } + if ld.Iself { + 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)) + r.Type = obj.R_CONST // write r->add during relocsym + r.Sym = nil + return + } + + if ld.HEADTYPE == obj.Hdarwin && s.Size == PtrSize && r.Off == 0 { + // Mach-O relocations are a royal pain to lay out. + // They use a compact stateful bytecode representation + // that is too much bother to deal with. + // Instead, interpret the C declaration + // void *_Cvar_stderr = &stderr; + // as making _Cvar_stderr the name of a GOT entry + // for stderr. This is separate from the usual GOT entry, + // 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. + ld.Adddynsym(ld.Ctxt, targ) + + got := ld.Linklookup(ld.Ctxt, ".got", 0) + s.Type = got.Type | obj.SSUB + s.Outer = got + s.Sub = got.Sub + got.Sub = s + s.Value = got.Size + ld.Adduint32(ld.Ctxt, got, 0) + ld.Adduint32(ld.Ctxt, ld.Linklookup(ld.Ctxt, ".linkedit.got", 0), uint32(targ.Dynid)) + r.Type = 256 // ignore during relocsym + return + } + + if ld.HEADTYPE == obj.Hwindows && s.Size == PtrSize { + // nothing to do, the relocation will be laid out in pereloc1 + return + } + } + + ld.Ctxt.Cursym = s + ld.Diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ.Name, r.Type, targ.Type) +} + +func elfreloc1(r *ld.Reloc, sectoff int64) int { + ld.Thearch.Lput(uint32(sectoff)) + + elfsym := r.Xsym.Elfsym + switch r.Type { + default: + return -1 + + case obj.R_ADDR: + if r.Siz == 4 { + ld.Thearch.Lput(ld.R_386_32 | uint32(elfsym)<<8) + } else { + return -1 + } + + case obj.R_CALL, + obj.R_PCREL: + if r.Siz == 4 { + ld.Thearch.Lput(ld.R_386_PC32 | uint32(elfsym)<<8) + } else { + return -1 + } + + case obj.R_TLS_LE: + if r.Siz == 4 { + ld.Thearch.Lput(ld.R_386_TLS_LE | uint32(elfsym)<<8) + } else { + return -1 + } + } + + return 0 +} + +func machoreloc1(r *ld.Reloc, sectoff int64) int { + var v uint32 + + rs := r.Xsym + + if rs.Type == obj.SHOSTOBJ { + if rs.Dynid < 0 { + ld.Diag("reloc %d to non-macho symbol %s type=%d", r.Type, rs.Name, rs.Type) + return -1 + } + + v = uint32(rs.Dynid) + v |= 1 << 27 // external relocation + } else { + v = uint32((rs.Sect.(*ld.Section)).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) + return -1 + } + } + + switch r.Type { + default: + return -1 + + case obj.R_ADDR: + v |= ld.MACHO_GENERIC_RELOC_VANILLA << 28 + + case obj.R_CALL, + obj.R_PCREL: + v |= 1 << 24 // pc-relative bit + v |= ld.MACHO_GENERIC_RELOC_VANILLA << 28 + } + + switch r.Siz { + default: + return -1 + + case 1: + v |= 0 << 25 + + case 2: + v |= 1 << 25 + + case 4: + v |= 2 << 25 + + case 8: + v |= 3 << 25 + } + + ld.Thearch.Lput(uint32(sectoff)) + ld.Thearch.Lput(v) + return 0 +} + +func pereloc1(r *ld.Reloc, sectoff int64) bool { + var v uint32 + + rs := r.Xsym + + if rs.Dynid < 0 { + ld.Diag("reloc %d to non-coff symbol %s type=%d", r.Type, rs.Name, rs.Type) + return false + } + + ld.Thearch.Lput(uint32(sectoff)) + ld.Thearch.Lput(uint32(rs.Dynid)) + + switch r.Type { + default: + return false + + case obj.R_ADDR: + v = ld.IMAGE_REL_I386_DIR32 + + case obj.R_CALL, + obj.R_PCREL: + v = ld.IMAGE_REL_I386_REL32 + } + + ld.Thearch.Wput(uint16(v)) + + return true +} + +func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int { + if ld.Linkmode == ld.LinkExternal { + return -1 + } + switch r.Type { + case obj.R_CONST: + *val = r.Add + return 0 + + case obj.R_GOTOFF: + *val = ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ld.Linklookup(ld.Ctxt, ".got", 0)) + return 0 + } + + return -1 +} + +func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 { + log.Fatalf("unexpected relocation variant") + return t +} + +func elfsetupplt() { + plt := ld.Linklookup(ld.Ctxt, ".plt", 0) + got := ld.Linklookup(ld.Ctxt, ".got.plt", 0) + if plt.Size == 0 { + // pushl got+4 + ld.Adduint8(ld.Ctxt, plt, 0xff) + + ld.Adduint8(ld.Ctxt, plt, 0x35) + ld.Addaddrplus(ld.Ctxt, plt, got, 4) + + // jmp *got+8 + ld.Adduint8(ld.Ctxt, plt, 0xff) + + ld.Adduint8(ld.Ctxt, plt, 0x25) + ld.Addaddrplus(ld.Ctxt, plt, got, 8) + + // zero pad + ld.Adduint32(ld.Ctxt, plt, 0) + + // assume got->size == 0 too + ld.Addaddrplus(ld.Ctxt, got, ld.Linklookup(ld.Ctxt, ".dynamic", 0), 0) + + ld.Adduint32(ld.Ctxt, got, 0) + ld.Adduint32(ld.Ctxt, got, 0) + } +} + +func addpltsym(ctxt *ld.Link, s *ld.LSym) { + if s.Plt >= 0 { + return + } + + ld.Adddynsym(ctxt, s) + + if ld.Iself { + plt := ld.Linklookup(ctxt, ".plt", 0) + got := ld.Linklookup(ctxt, ".got.plt", 0) + rel := ld.Linklookup(ctxt, ".rel.plt", 0) + if plt.Size == 0 { + elfsetupplt() + } + + // jmpq *got+size + ld.Adduint8(ctxt, plt, 0xff) + + ld.Adduint8(ctxt, plt, 0x25) + ld.Addaddrplus(ctxt, plt, got, got.Size) + + // add to got: pointer to current pos in plt + ld.Addaddrplus(ctxt, got, plt, plt.Size) + + // pushl $x + ld.Adduint8(ctxt, plt, 0x68) + + ld.Adduint32(ctxt, plt, uint32(rel.Size)) + + // jmp .plt + ld.Adduint8(ctxt, plt, 0xe9) + + ld.Adduint32(ctxt, plt, uint32(-(plt.Size + 4))) + + // rel + ld.Addaddrplus(ctxt, rel, got, got.Size-4) + + ld.Adduint32(ctxt, rel, ld.ELF32_R_INFO(uint32(s.Dynid), ld.R_386_JMP_SLOT)) + + s.Plt = int32(plt.Size - 16) + } else if ld.HEADTYPE == obj.Hdarwin { + // Same laziness as in 6l. + + plt := ld.Linklookup(ctxt, ".plt", 0) + + addgotsym(ctxt, s) + + ld.Adduint32(ctxt, ld.Linklookup(ctxt, ".linkedit.plt", 0), uint32(s.Dynid)) + + // jmpq *got+size(IP) + s.Plt = int32(plt.Size) + + ld.Adduint8(ctxt, plt, 0xff) + ld.Adduint8(ctxt, plt, 0x25) + ld.Addaddrplus(ctxt, plt, ld.Linklookup(ctxt, ".got", 0), int64(s.Got)) + } else { + ld.Diag("addpltsym: unsupported binary format") + } +} + +func addgotsym(ctxt *ld.Link, s *ld.LSym) { + if s.Got >= 0 { + return + } + + ld.Adddynsym(ctxt, s) + got := ld.Linklookup(ctxt, ".got", 0) + s.Got = int32(got.Size) + ld.Adduint32(ctxt, got, 0) + + if ld.Iself { + rel := ld.Linklookup(ctxt, ".rel", 0) + ld.Addaddrplus(ctxt, rel, got, int64(s.Got)) + ld.Adduint32(ctxt, rel, ld.ELF32_R_INFO(uint32(s.Dynid), ld.R_386_GLOB_DAT)) + } else if ld.HEADTYPE == obj.Hdarwin { + ld.Adduint32(ctxt, ld.Linklookup(ctxt, ".linkedit.got", 0), uint32(s.Dynid)) + } else { + ld.Diag("addgotsym: unsupported binary format") + } +} + +func asmb() { + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime()) + } + ld.Bso.Flush() + + if ld.Iself { + ld.Asmbelfsetup() + } + + sect := ld.Segtext.Sect + ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Codeblk(int64(sect.Vaddr), int64(sect.Length)) + for sect = sect.Next; sect != nil; sect = sect.Next { + ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Datblk(int64(sect.Vaddr), int64(sect.Length)) + } + + if ld.Segrodata.Filelen > 0 { + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) + } + ld.Bso.Flush() + + ld.Cseek(int64(ld.Segrodata.Fileoff)) + ld.Datblk(int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) + } + + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f datblk\n", obj.Cputime()) + } + ld.Bso.Flush() + + ld.Cseek(int64(ld.Segdata.Fileoff)) + ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + + machlink := uint32(0) + if ld.HEADTYPE == obj.Hdarwin { + if ld.Debug['v'] != 0 { + 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)) + + ld.Segdwarf.Fileoff = uint64(ld.Cpos()) + ld.Dwarfemitdebugsections() + ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff + + machlink = uint32(ld.Domacholink()) + } + + ld.Symsize = 0 + ld.Spsize = 0 + ld.Lcsize = 0 + symo := uint32(0) + if ld.Debug['s'] == 0 { + // TODO: rationalize + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime()) + } + ld.Bso.Flush() + switch ld.HEADTYPE { + default: + if ld.Iself { + symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) + symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND))) + } + + case obj.Hplan9: + 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)) + + case obj.Hwindows: + symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) + symo = uint32(ld.Rnd(int64(symo), ld.PEFILEALIGN)) + } + + ld.Cseek(int64(symo)) + switch ld.HEADTYPE { + default: + if ld.Iself { + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f elfsym\n", obj.Cputime()) + } + ld.Asmelfsym() + ld.Cflush() + ld.Cwrite(ld.Elfstrdat) + + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) + } + ld.Dwarfemitdebugsections() + + if ld.Linkmode == ld.LinkExternal { + ld.Elfemitreloc() + } + } + + case obj.Hplan9: + ld.Asmplan9sym() + ld.Cflush() + + sym := ld.Linklookup(ld.Ctxt, "pclntab", 0) + if sym != nil { + ld.Lcsize = int32(len(sym.P)) + for i := 0; int32(i) < ld.Lcsize; i++ { + ld.Cput(uint8(sym.P[i])) + } + + ld.Cflush() + } + + case obj.Hwindows: + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) + } + ld.Dwarfemitdebugsections() + + case obj.Hdarwin: + if ld.Linkmode == ld.LinkExternal { + ld.Machoemitreloc() + } + } + } + + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f headr\n", obj.Cputime()) + } + ld.Bso.Flush() + ld.Cseek(0) + switch ld.HEADTYPE { + default: + case obj.Hplan9: /* plan9 */ + magic := int32(4*11*11 + 7) + + ld.Lputb(uint32(magic)) /* magic */ + ld.Lputb(uint32(ld.Segtext.Filelen)) /* sizes */ + ld.Lputb(uint32(ld.Segdata.Filelen)) + ld.Lputb(uint32(ld.Segdata.Length - ld.Segdata.Filelen)) + ld.Lputb(uint32(ld.Symsize)) /* nsyms */ + ld.Lputb(uint32(ld.Entryvalue())) /* va of entry */ + ld.Lputb(uint32(ld.Spsize)) /* sp offsets */ + ld.Lputb(uint32(ld.Lcsize)) /* line offsets */ + + case obj.Hdarwin: + ld.Asmbmacho() + + case obj.Hlinux, + obj.Hfreebsd, + obj.Hnetbsd, + obj.Hopenbsd, + obj.Hnacl: + ld.Asmbelf(int64(symo)) + + case obj.Hwindows: + ld.Asmbpe() + } + + ld.Cflush() +} diff --git a/src/cmd/link/internal/x86/l.go b/src/cmd/link/internal/x86/l.go new file mode 100644 index 0000000000..8a811ff0a2 --- /dev/null +++ b/src/cmd/link/internal/x86/l.go @@ -0,0 +1,47 @@ +// Inferno utils/8l/l.h +// http://code.google.com/p/inferno-os/source/browse/utils/8l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package x86 + +const ( + thechar = '8' + PtrSize = 4 + IntSize = 4 + RegSize = 4 + MaxAlign = 32 // max data alignment + FuncAlign = 16 + MINLC = 1 +) + +/* Used by ../internal/ld/dwarf.go */ +const ( + DWARFREGSP = 4 + DWARFREGLR = 8 +) diff --git a/src/cmd/link/internal/x86/obj.go b/src/cmd/link/internal/x86/obj.go new file mode 100644 index 0000000000..ee408f70c6 --- /dev/null +++ b/src/cmd/link/internal/x86/obj.go @@ -0,0 +1,187 @@ +// Inferno utils/8l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/8l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package x86 + +import ( + "cmd/internal/obj" + "cmd/link/internal/ld" + "fmt" + "log" +) + +// Reading object files. + +func Main() { + linkarchinit() + ld.Ldmain() +} + +func linkarchinit() { + ld.Thestring = "386" + ld.Thelinkarch = &ld.Link386 + + ld.Thearch.Thechar = thechar + ld.Thearch.Ptrsize = ld.Thelinkarch.Ptrsize + ld.Thearch.Intsize = ld.Thelinkarch.Ptrsize + ld.Thearch.Regsize = ld.Thelinkarch.Regsize + ld.Thearch.Funcalign = FuncAlign + ld.Thearch.Maxalign = MaxAlign + ld.Thearch.Minlc = MINLC + ld.Thearch.Dwarfregsp = DWARFREGSP + ld.Thearch.Dwarfreglr = DWARFREGLR + + ld.Thearch.Adddynrel = adddynrel + ld.Thearch.Archinit = archinit + ld.Thearch.Archreloc = archreloc + ld.Thearch.Archrelocvariant = archrelocvariant + ld.Thearch.Asmb = asmb + ld.Thearch.Elfreloc1 = elfreloc1 + ld.Thearch.Elfsetupplt = elfsetupplt + ld.Thearch.Gentext = gentext + ld.Thearch.Machoreloc1 = machoreloc1 + ld.Thearch.PEreloc1 = pereloc1 + ld.Thearch.Lput = ld.Lputl + ld.Thearch.Wput = ld.Wputl + ld.Thearch.Vput = ld.Vputl + + ld.Thearch.Linuxdynld = "/lib/ld-linux.so.2" + ld.Thearch.Freebsddynld = "/usr/libexec/ld-elf.so.1" + ld.Thearch.Openbsddynld = "/usr/libexec/ld.so" + ld.Thearch.Netbsddynld = "/usr/libexec/ld.elf_so" + ld.Thearch.Solarisdynld = "/lib/ld.so.1" +} + +func archinit() { + // getgoextlinkenabled is based on GO_EXTLINK_ENABLED when + // Go was built; see ../../make.bash. + if ld.Linkmode == ld.LinkAuto && obj.Getgoextlinkenabled() == "0" { + ld.Linkmode = ld.LinkInternal + } + + switch ld.HEADTYPE { + default: + if ld.Linkmode == ld.LinkAuto { + ld.Linkmode = ld.LinkInternal + } + if ld.Linkmode == ld.LinkExternal && obj.Getgoextlinkenabled() != "1" { + log.Fatalf("cannot use -linkmode=external with -H %s", ld.Headstr(int(ld.HEADTYPE))) + } + + case obj.Hdarwin, + obj.Hfreebsd, + obj.Hlinux, + obj.Hnetbsd, + obj.Hopenbsd, + obj.Hwindows: + break + } + + switch ld.HEADTYPE { + default: + ld.Exitf("unknown -H option: %v", ld.HEADTYPE) + + case obj.Hplan9: /* plan 9 */ + ld.HEADR = 32 + + if ld.INITTEXT == -1 { + ld.INITTEXT = 4096 + 32 + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 4096 + } + + case obj.Hdarwin: /* apple MACH */ + ld.Machoinit() + + ld.HEADR = ld.INITIAL_MACHO_HEADR + if ld.INITTEXT == -1 { + ld.INITTEXT = 4096 + int64(ld.HEADR) + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 4096 + } + + case obj.Hlinux, /* elf32 executable */ + obj.Hfreebsd, + obj.Hnetbsd, + obj.Hopenbsd: + ld.Elfinit() + + ld.HEADR = ld.ELFRESERVE + if ld.INITTEXT == -1 { + ld.INITTEXT = 0x08048000 + int64(ld.HEADR) + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 4096 + } + + case obj.Hnacl: + ld.Elfinit() + ld.HEADR = 0x10000 + ld.Funcalign = 32 + if ld.INITTEXT == -1 { + ld.INITTEXT = 0x20000 + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 0x10000 + } + + case obj.Hwindows: /* PE executable */ + ld.Peinit() + + ld.HEADR = ld.PEFILEHEADR + if ld.INITTEXT == -1 { + ld.INITTEXT = ld.PEBASE + int64(ld.PESECTHEADR) + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = ld.PESECTALIGN + } + } + + if ld.INITDAT != 0 && ld.INITRND != 0 { + fmt.Printf("warning: -D0x%x is ignored because of -R0x%x\n", uint64(ld.INITDAT), uint32(ld.INITRND)) + } +} 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() + } +} -- cgit v1.3 From 0f4132c907d9749a1a41a20af7856065ee10de9e Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 21 May 2015 13:28:13 -0400 Subject: 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(-) (limited to 'src/cmd') 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)) -- cgit v1.3 From cf932cd897a3eaf3aa6fec3ba5d0ab1d9107eebb Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 21 May 2015 13:28:17 -0400 Subject: 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(-) (limited to 'src/cmd') 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") -- cgit v1.3 From 216e5c747d28676beea2287f48a0771fa56bbaf8 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 21 May 2015 13:40:17 -0400 Subject: 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(-) (limited to 'src/cmd') 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, -- cgit v1.3 From 137817378548b263f0322d8240a0c503b456f696 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Wed, 20 May 2015 17:35:43 -0700 Subject: 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(-) (limited to 'src/cmd') 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) -- cgit v1.3 From b19ec6842d3d3bdc6d7b67fa065121a9d317cff7 Mon Sep 17 00:00:00 2001 From: David Chase Date: Thu, 21 May 2015 12:40:25 -0400 Subject: 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 (limited to 'src/cmd') 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) + } +} -- cgit v1.3 From e5060c7f7524bf32e07f62d2593aae8d280725e5 Mon Sep 17 00:00:00 2001 From: David Chase Date: Wed, 20 May 2015 15:16:34 -0400 Subject: 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(-) (limited to 'src/cmd') 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 +} -- cgit v1.3 From db0594b633525e5b88e1b508019edaeb1b44ed2f Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Thu, 21 May 2015 18:13:44 -0700 Subject: 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 | 155 +++++++++++++----------- 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, 635 insertions(+), 143 deletions(-) (limited to 'src/cmd') 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) - - if x.exp < dmin+1 { - // underflow - if x.neg { - var z float32 - return -z, Above + // 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 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 -- cgit v1.3 From bd7c1f1fb3176a8f926ca03f9342d83353b05f83 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Thu, 21 May 2015 18:23:51 -0700 Subject: 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(-) (limited to 'src/cmd') 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") } -- cgit v1.3 From 75250a9f791e650bcf0fe16e9d3436314a94dde6 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Fri, 22 May 2015 11:31:47 -0700 Subject: 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(-) (limited to 'src/cmd') 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) { -- cgit v1.3 From 1893d3b320887daeb0a088dc3067c45dd87d9d5b Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Fri, 22 May 2015 15:21:56 -0700 Subject: 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(-) (limited to 'src/cmd') 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 . -- cgit v1.3 From ac7ffdfa1006a40768d11ed7e2e20dab57799df0 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Fri, 15 May 2015 17:12:20 -0700 Subject: 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(-) (limited to 'src/cmd') 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 } -- cgit v1.3 From 714291f2d80bab1599a866f266a4fc6546e61632 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Sun, 24 May 2015 11:29:38 -0700 Subject: 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(+) (limited to 'src/cmd') 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 { -- cgit v1.3 From 972a478ddf689a375dffc1a9923742b896b63bd0 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 21 May 2015 15:00:06 -0400 Subject: 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(-) (limited to 'src/cmd') 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 } -- cgit v1.3 From b2f95a167aacc11177d66d1a3235d84a2b1f3538 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Tue, 26 May 2015 13:49:59 -0700 Subject: 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(-) (limited to 'src/cmd') 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 } -- cgit v1.3 From bc89ad598e51b1f4935c2574edbabafff4dc1dea Mon Sep 17 00:00:00 2001 From: Ryan Brown Date: Wed, 8 Apr 2015 12:55:34 -0700 Subject: 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(-) (limited to 'src/cmd') 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) { -- cgit v1.3 From cf2736c4c5bbb24b9a4ff9690887a0cb287ba34c Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Wed, 27 May 2015 12:04:25 +1200 Subject: 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(-) (limited to 'src/cmd') 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 } } -- cgit v1.3 From 65518032b946bbb640cf1e3d5e66f537642503ed Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Mon, 25 May 2015 13:59:08 +1200 Subject: 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(-) (limited to 'src/cmd') 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 -- cgit v1.3 From bcc1870fcfaf121ed69096c21a9d82f69a8e6a18 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Mon, 25 May 2015 14:51:02 +1200 Subject: 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 (limited to 'src/cmd') 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 { -- cgit v1.3 From c949cff6a755aacd7407a803d304f1aba057bf5c Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Mon, 25 May 2015 16:13:50 +1200 Subject: 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 | 52 +++++++++++++++++++++++++++++------ src/cmd/link/internal/ld/lib.go | 49 ++++++--------------------------- src/cmd/link/internal/ld/link.go | 2 +- 6 files changed, 62 insertions(+), 51 deletions(-) (limited to 'src/cmd') 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 } +// 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 nil +} + // Type.commonType.gc -func decodetype_gcprog(s *LSym) *LSym { +func decodetype_gcprog(s *LSym) []byte { 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) + 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)) + 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,27 +1296,15 @@ 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 @@ -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 { -- cgit v1.3 From 05d8f1d1664e6413756ed4e18392310b75501ff4 Mon Sep 17 00:00:00 2001 From: David Chase Date: Fri, 22 May 2015 22:01:01 -0400 Subject: 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 (limited to 'src/cmd') 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" +) -- cgit v1.3 From 4e4c1f9c4d101d5578656c06a42784856fec9f0d Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 27 May 2015 16:33:03 -0700 Subject: 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(-) (limited to 'src/cmd') 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)) } +// 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) + } + 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() { - // 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 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) + // 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) { -- cgit v1.3 From 0f27b915228ff661aeaf823fa90db023765cbe5d Mon Sep 17 00:00:00 2001 From: Shenghou Ma Date: Thu, 21 May 2015 17:51:34 -0400 Subject: 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(-) (limited to 'src/cmd') 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 { -- cgit v1.3 From 5ee552815ceaa5874b73125df07265bee34d1cc1 Mon Sep 17 00:00:00 2001 From: Ryan Brown Date: Wed, 8 Apr 2015 12:55:34 -0700 Subject: 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(-) (limited to 'src/cmd') 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) + } } } } -- cgit v1.3 From f90d802b6102ced82377ddc16f81f299c039ce83 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Wed, 27 May 2015 15:20:49 -0400 Subject: 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(-) (limited to 'src/cmd') 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) } -- cgit v1.3 From bd8bb6735765653b29c672d724eaf80a477d1eba Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Tue, 19 May 2015 15:25:35 -0700 Subject: 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(-) (limited to 'src/cmd') 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) -- cgit v1.3 From 596bb76248dd7844ba3bebcdeab9a7c42ef5855a Mon Sep 17 00:00:00 2001 From: David Chase Date: Wed, 27 May 2015 17:33:46 -0400 Subject: 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 (limited to 'src/cmd') 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) +} -- cgit v1.3